System.Bitmapの操作にかかるコスト



System.Bitmapにおける、Lock,Unlockのコスト

 ポインタでBitmapにアクセスするときに欠かせないのがLockとUnlockメソッドです。
 このLockに渡すPixelFormatって、いじりやすいってだけで、なんとなくFormat24bppRgbにしたりしてないでしょうか?そもそも、Bitmap自体の生成時に、PixelFormatを特に指定しなかったりしてないでしょうか?
 簡単な実験の結果、BitmapとLockメソッドで指定するPixelFormatを揃えていないと、以外と大きなコストを払っていることが分かりました。

// [共通プロパティ]→[参照設定]で、System.Drawingを追加する。
using namespace System;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;

//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
// 指定されたBitmapに対し、指定されたPixelFormatでLock,Unlockを行い、時間を計測する。
void TestLockToUnlockTime(Bitmap^ bmp, PixelFormat fmt) {
    const int LoopCnt = 1000;
    Console::Write("  {0,-20} = ", fmt.ToString());
    Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch();
    sw->Start();
    try {
        for (int i = 0 ; i < LoopCnt; i++ ) {
            BitmapData^ bmpData = bmp->LockBits(
                Rectangle(0,0,bmp->Width,bmp->Height),
                ImageLockMode::ReadWrite, fmt);
            bmp->UnlockBits(bmpData);
        }
    }
    catch (ArgumentException^) {
        Console::WriteLine("非対応っぽい");
        return;
    }
    sw->Stop();
    Console::WriteLine(sw->ElapsedMilliseconds / (double)LoopCnt);		
    return;
}
//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
// 指定されたPixelFormatをSourceとし、すべてのPixelFormatでTestLockToUnlockTimeを呼ぶ。
void Test(PixelFormat fmt) {
    Bitmap^ bmp = gcnew Bitmap(640,480,fmt);
    Console::WriteLine("Source = " + fmt.ToString()); 

    // PixelFormat列挙型でループを回す
     array<String^>^ a = Enum::GetNames( PixelFormat::typeid );
    for each ( String^ s in a) {
        // PixelFormat列挙型のうち、Format...で始まる物のみテスト対象とする
         if (s->IndexOf("Format") >= 0) {
            TestLockToUnlockTime( bmp,
                (PixelFormat)Enum::Parse(PixelFormat::typeid, s));
        }
    }
}
//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
int main(array<System::String ^> ^args) {
    // PixelFormat列挙型でループを回す
         array<String^>^ a = Enum::GetNames( PixelFormat::typeid );
    for each ( String^ s in a) {
          // PixelFormat列挙型のうち、Format...で始まる物のみテスト対象とする
        if (s->IndexOf("Format") >= 0) {
            Test((PixelFormat)Enum::Parse(PixelFormat::typeid, s));
        }
    }
    Console::ReadLine();
    return 0;
}
//===<< END OF FILE >>===========================================================

System.Bitmapにおける、DrawImage(Bitmap, Point)のコスト

 Format8bppIndexedで画像処理をして、表示用のBitmapに描画し、その上に更に何かを描画してから、PictureBox->Imageに代入するとか、よくあるシチュエーションだと思います。
 リサイズしないで、Bitmapに対してべつのBitmapを描画するときの速度を計測してみました。
 32bppArgbが、無指定で生成したBitmapのPixelFormatです。
 この結果を見ると、32bppPArgbでBitmapを扱うのが、効率の点からよさそうです。

// [共通プロパティ]→[参照設定]で、System.Drawingを追加する。
using namespace System;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;

//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
// 指定されたBitmapに対し、指定されたPixelFormatでDrawImageを行い、時間を計測する。
void TestDrawImageTime(Bitmap^ bmp, PixelFormat fmt) {
    const int LoopCnt = 100;
    Console::Write("  {0,-20} = ", fmt.ToString());
    Graphics ^ g = Graphics::FromImage(bmp);
    Bitmap^ bmp2 = gcnew Bitmap(bmp->Width, bmp->Height, fmt);

    Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch();
    sw->Start();
    try {
        for (int i = 0 ; i < LoopCnt; i++ ) {
            g->DrawImage(bmp2, Point(0,0));
        }
    }
    catch (ArgumentException^) {
        Console::WriteLine("非対応っぽい");
        return;
    }
    sw->Stop();

    Console::WriteLine(sw->ElapsedMilliseconds / (double)LoopCnt);		
    return;
}
//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
// 指定されたPixelFormatをSourceとして、すべてのPixelFormatでTestDrawImageTimeを呼ぶ。
void Test(PixelFormat fmt) {

    Bitmap^ bmp = gcnew Bitmap(640,480,fmt);
    Console::WriteLine("Source = " + fmt.ToString()); 

    // PixelFormat列挙型でループを回す
    array<String^>^ a = Enum::GetNames( PixelFormat::typeid );
    for each ( String^ s in a) {
        // PixelFormat列挙型のうち、Format...で始まる物のみテスト対象とする
        if (s->IndexOf("Format") >= 0) {
            TestDrawImageTime(bmp,
                (PixelFormat)Enum::Parse(PixelFormat::typeid, s));
        }
    }
}
//------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
int main(array<System::String ^> ^args) {
    Bitmap^ bmp = gcnew Bitmap(640,480); ;
    Console::WriteLine("PixelFormatを指定しないでBitmapを生成すると……"
                                           + bmp->PixelFormat.ToString());

    // PixelFormat列挙型でループを回す
    array<String^>^ a = Enum::GetNames( PixelFormat::typeid );
    for each ( String^ s in a) {
    // PixelFormat列挙型のうち、Format...で始まる物のみテスト対象とする
        if (s->IndexOf("Format") >= 0 && s->IndexOf("Indexed") < 0
                                     && s->IndexOf("1555") < 0
                                     && s->IndexOf("GrayScale") < 0) {
            Test((PixelFormat)Enum::Parse(PixelFormat::typeid, s));
        }
    }

    Console::ReadLine();
    return 0;
}
//===<< END OF FILE >>===========================================================
最終更新:2007年10月24日 09:59