contents

  1. はじめに
  2. 実験
  3. 考察
  4. 付録:GDI/GDI+の描画能力

GDI/GDI+のビットマップ転送能力

GDI+はGDIに比べて10倍ほど遅いという噂がありますが、実際のところを計測しました。 速度に最も影響するのは、DDB(デバイス依存ビットマップ)の存在でした。 GDIのDDBは非常に高速ですが、制限も多いようです。

※前回とは実験条件が変わっています。以前の結果はこちら

はじめに

先行研究として、やねうらおさんの超高速描画の謎があります。 DDBとDirectDrawとの比較をされています。

GDIでは明示的にDIB, DDBを区別して作成できるのに対し、GDI+では基本的にはDIBのみを扱います。 Gdiplus::CachedBitmapは存在するものの、オフスクリーン描画の対象にすることはできません(CachedBitmapを対象とするGraphicsは作成できない)。 一方、GDIでは、CreateCompatibleBitmap()で作成したオフスクリーンDDBに、DDBのイメージソースを描画できます。 こちらは、ビデオメモリ間のみでの転送になるので高速であると考えられます。

実験

以下の条件で速度を計測しました。 転送は、Graphcis::DrawImage()もしくはBitBlt()を使用しました。 【計測用プログラム

番号転送先転送元
1ONGDI::DIB
2ONGDI::DDB
3ONGdiplus::Bitmap
4ONGdiplus::CachedBitmap
5OFFGDI::DIB
6OFFGDI::DDB
7OFFGdiplus::Bitmap
8OFFGdiplus::CachedBitmap
ここでの用語
GDI::DIB
GDIのDIBセクション。
GDI::DDB
GDIのDDB(デバイス依存ビットマップ)。
転送先:ON
GDIの場合は、GetDC(hWnd)で取得できるデバイスコンテキスト。
GDI+の場合は、HWND を引数にとるコンストラクタで作成したGdiplus::Graphics。
転送先:OFF
GDI::DIBの場合は、CreateDIBSection()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。
GDI::DDBの場合は、CreateCompatibleBitmap()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。
GDI+の場合は、Gdiplus::Bitmap を引数にとるコンストラクタで作成したGdiplus::Graphics。

※オフスクリーンのフォーマットを転送元のフォーマットに一致させた理由は、異なる場合には必ず遅くなり、実用上意味がないことが以前の実験から判明しているためです。

結果

条件
1 DIB-ON
2 DDB-ON
3 Bmp-ON
4 Cache-ON
5 DIB-OFF
6 DDB-OFF
7 Bmp-OFF
8 Cache-OFF
A 画像:640x480x24, 解像度:1600x1200x32bpp, 500回平均, AthronXP 1800+, nVidia GeForce2MX, DDR266
番号1回あたり [msec]
16.64
20.00890
39.23
46.87
53.07
60.00685
74.97
84.03
B 最初にソース用デバイスコンテキストを作成しておき、BitBlt()ごとに再作成しない。 他はAと同じ。
番号1回あたり [msec]
16.61
20.00131
39.23
46.85
53.03
60.00121
74.97
84.03
C 画像:800x600x24 他はAと同じ。
番号1回あたり [msec]
110.0
20.00697
313.9
410.1
54.53
60.00686
77.37
85.97

考察

GDI+とGDI::DDBを比較するならば、10倍どころではなく遅いようです。 ちょっと古いマシンでは、GDI+で30fps出すのは不可能です。 CachedBitmapをオフスクリーンで使用した場合でもDDBの速度にはかないません。 高速性が求められるならば、GDI+ではなくGDI(のDDB)を使用するべきでしょう。

98/2000以降に追加されたGDI関数を使えば、GDI+の多彩な描画ルーチンの一部はカバーできますが、 これらの関数をサポートしているハードウェアは少ないようです。 その場合、例えばMaskBlt()をソフトウェア的に行うよりも、BitBlt()を2回使ったほうが遥かに高速です。 つまり、GDIを使う場合、BitBlt()しか信用できず、その結果「単純な転送」「単色カラーキー付き転送」くらいしかハードウェアアクセラレーションを期待できません(ただし、とんでもなく高速ですが)。 そのような制限を受けるくらいならば、初めから全部ソフトウェアで処理するGDI+を使ったほうがいいかもしれません。

実験データが不足していますので、計測結果を報告していただければ幸いです。 計測用プログラムはこちら。 Windows2000以降ならば、メッセージボックス上でCtrl-Cをすることでテキストがクリップボードにコピーされます。

宿題: DDBに対するGraphicsは作成できるのか? BltBlt()以外の転送関数の性能比較。

付録:GDI/GDI+の描画能力

98/2000 以降で使用できるGDI関数

GradientFill()
グラデーションのかかった三角形もしくは矩形を描画する。
GDI+ : LinearGradientBrush, PathGradientBrush に上位機能。
AlphaBlend()
ソース全体もしくはピクセルごとのα値に対応したStretchBlt()。
GDI+ : ImageAttributes::SetColorMatrix(), Graphics::DrawImage() に上位機能。
MaskBlt()
マスクビットマップを使用して2種類のラスタオペレーションを行う。 いわゆるカラーキー付き転送が可能。
GDI+ : 互換機能なし。
PlgBlt()
平行四辺形に変形描画する。マスク指定も可能。
GDI+ : Graphics::DrawImage() に上位機能。
TransparentBlt()
カラーキーを指定できるStretchBlt()。 Windows98ではリソースリークが発生するので注意!
GDI+ : ImageAttributes::SetColorKey() に上位機能。

GDI+独自のイメージ描画機能

Graphics::SetTransform()
2次元同次変換行列を用いた描画。
ImageAttributes::SetRemapTable()
指定した色を、もうひとつの指定した色に置き換えながら描画する。
ImageAttributes::SetColorKey()
透過色を範囲で指定し、範囲内の色を透過して描画する。
ImageAttributes::SetColorMatrix()
同次変換行列を使用してARGB値を変換する。
ImageAttributes::SetGamma()
ガンマ値を指定する。
ImageAttributes::SetOutputChannel()
CMYK (cyan, magenta, yellow, black) 変換する。
ImageAttributes::SetThreshold()
色が指定した値を超えたか否かで 0 または 255 に変換する。
ImageAttributes::SetWrapMode()
描画先サイズがイメージサイズより大きな場合のラップ(回り込み)モードを指定する。