contents

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

GDI/GDI+のビットマップ転送能力(第1稿)

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

はじめに

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

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

実験

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

  1. on-screen に Gdiplus::Bitmap, Gdiplus::CachedBitmapを転送する。
  2. off-screen に Gdiplus::Bitmap, Gdiplus::CachedBitmapを転送する。
  3. on-screen に GDI::DIB, GDI::DDBを転送する。
  4. dib-off-screen に GDI::DIB, GDI::DDBを転送する。
  5. ddb-off-screen に GDI::DIB, GDI::DDBを転送する。
ここでの用語
GDI::DIB
GDIのDIBセクション。
GDI::DDB
GDIのDDB(デバイス依存ビットマップ)。
on-screen
GetDC(hWnd)で取得できるデバイスコンテキスト。もしくはそれから作成したGdiplus::Graphics。
off-screen
Gdiplus::Bitmapから作成したGdiplus::Graphics。
dib-off-screen
CreateDIBSection()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。
ddb-off-screen
CreateCompatibleBitmap()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。

転送性能 例1

画像:640x480x24, ディスプレイ:1600x1200x32bpp, 400回平均, Celeron 400MHz, nVidiaTNT2, DIMM100
実験 No.Bitmap/DIB [ms]CachedBitmap/DDB [ms]
1 : GDI+ on 19.51 10.43
2 : GDI+ off 12.70 11.55
3 : GDI on 19.91 1.23
4a: GDI dib 24bpp12.46 168.08
4b: GDI dib 32bpp19.55 135.64
5 : GDI ddb 19.79 0.0725

転送性能 例2

画像:640x480x24, ディスプレイ:1024x768x32bpp, 400回平均, Pentium4 2.0AGHz, RADEON8500LE, DDR266
実験 No.Bitmap/DIB [ms]CachedBitmap/DDB [ms]
1 : GDI+ on 7.256 6.139
2 : GDI+ off 2.576 2.561
3 : GDI on 5.753 0.013
4a: GDI dib 24bpp2.061 109.821
4b: GDI dib 32bpp2.580 65.278
5 : GDI ddb 5.744 0.0128

考察

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

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

宿題: 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()
描画先サイズがイメージサイズより大きな場合のラップ(回り込み)モードを指定する。