BitBltとStretchBltによるWindowsビットマップ転送の基礎
Windows APIを用いて画面のコピーや画像の拡大縮小を行いたいとき、BitBltとStretchBltは最も基本的な二つのビットブロック転送関数として長年にわたり使われてきました。GDIの中核を担うこれらのAPIは、デバイスコンテキスト(HDC)を介してメモリ上、画面上、あるいは異なるウィンドウ間でピクセルデータを高速に転送する仕組みを提供します。スクリーンキャプチャやスプライト描画、リサイズ処理など幅広い場面で今も現役の技術です。
両者は一見似ていますが、用途と挙動に明確な違いがあります。BitBltはそのままの解像度でブロックをコピーするのに対し、StretchBltは指定したサイズに拡大または縮小しながら転送を行います。本記事ではそれぞれの特徴を比較しながら、実際のコード例と併せて基礎から丁寧に解説します。
BitBltとStretchBltの基本仕様を比較する
| 項目 | BitBlt | StretchBlt |
|---|---|---|
| 主な役割 | 同一サイズでのブロック転送 | サイズ変更を伴うブロック転送 |
| 拡大縮小 | 対応しない | 複数のストレッチモードで指定 |
| 転送速度 | 非常に高速 | 補間処理のためやや遅い |
| 必要条件 | 転送元と転送先のサイズ一致 | 元と先のサイズが異なることを許容 |
| 主な利用例 | スプライト描画、ダブルバッファリング | サムネイル生成、リサイズ処理 |
両関数ともHDCを引数として受け取り、ピクセルデータを一括でコピーします。共通点の多さから混同されがちですが、StretchBltはラスタオペコードに加えストレッチモードの設定が必要で、処理内容も内部的には異なります。BitBltはオーバーヘッドが少ないため、ゲームループのような毎フレームの描画にも向いています。
使い分けとしては、画像の原寸コピーやダブルバッファリングにはBitBltを、サムネイル生成のようにサイズ変更が必要なケースにはStretchBltを選ぶと、意図した描画結果を得やすくなります。
ビットマップ読み込みとHDCの準備
BitBltやStretchBltを実行するには、まず転送元のビットマップをHBITMAPとして用意し、それをメモリデバイスコンテキストに割り当てる必要があります。HBITMAPの作成にはLoadImage、LoadBitmap、CreateCompatibleBitmap、CreateDIBSectionなど複数の選択肢があり、用途によってロード処理を使い分けます。リソースから読み込む場合はLoadImageでLR_LOADFROMFILEフラグを指定します。
メモリHDCはCreateCompatibleDCで作ります。これは画面HDCと互換性のあるHDCを作成する関数で、戻り値のHDCはDeleteDCで必ず解放する必要があります。作成したメモリHDCにはSelectObjectでHBITMAPを選択し、はじめてビットマップが描画対象として認識されます。描画後は忘れずに元のビットハンドルを戻してからDeleteObjectとDeleteDCを実行してください。
DLLやEXEのリソースからビットマップを取得する場面では、GetModuleHandleとLoadLibraryの使いどころがモジュールハンドル管理の参考になります。リソースとして埋め込まれたビットマップを扱う場合、適切なモジュールハンドルの取得が必要となるためです。
BitBltによる高速ブロック転送
BitBltの関数シグネチャはBOOL BitBlt(HDC hdcDest, int xDest, int yDest, int wDestWidth, int wDestHeight, HDC hdcSrc, int xSrc, int ySrc, DWORD rop)で、転送先HDC、座標、サイズ、転送元HDC、座標、そしてラスタオペレーションコードを受け取ります。最もよく使われるラスタオペコードはSRCCOPYで、転送元をそのままコピーします。
ダブルバッファリングを実装する手順としては、CreateCompatibleDCでメモリHDCを作り、画面と同サイズのビットマップをSelectObjectで割り当てます。そこに背景やスプライトを順に描画し、最後に画面HDCへBitBltで一括コピーすると画面のちらつきを抑えられます。逆に、画面HDCの内容をメモリHDCへコピーすればスクリーンキャプチャの仕組みになります。
BitBltで指定できる代表的なラスタオペコードには以下のようなものがあります。
- SRCCOPY: 転送元をそのままコピーする基本モード
- SRCPAINT: 転送元と転送先をOR演算で合成
- SRCAND: 転送元と転送先をAND演算で合成
- SRCINVERT: 転送元と転送先をXOR演算で合成
- BLACKNESS: 転送先領域を黒で塗りつぶし
アルファチャンネル付きの32ビットビットマップの半透明値はラスタオペコードでは扱えないため、半透明表現にはAlphaBlendを使う必要があります。
StretchBltによる拡大縮小転送
StretchBltのシグネチャはBOOL StretchBlt(HDC, int, int, int, int, HDC, int, int, int, int, DWORD)で、転送先と転送元の矩形をそれぞれ指定する点がBitBltと異なります。両者のサイズが一致している必要はなく、ピクセル数が異なっていてもBitBlt同様の一括転送で処理されます。
ストレッチの品質を制御するのがSetStretchBltMode関数で、COLORONCOLOR、HALFTONE、BLACKONWHITE、WHITEONBLACKなどのモードが用意されています。COLORONCOLORは高速ですがジャギーが目立ち、HALFTONEは処理は重くなりますが高品質です。Vista以降ではHALFTONEモードを使う場合、SetBrushOrgExでブラシ原点を調整する必要があります。
SetStretchBltModeで選択できるストレッチモードには以下の種類があります。
- BLACKONWHITE: ピクセル値とAND演算して縮小、線画向け
- WHITEONBLACK: ピクセル値とOR演算して縮小、線画向け
- COLORONCOLOR: 単純コピー、速度最優先だがジャギーが目立つ
- HALFTONE: 高品質なリサイズ処理、CPU負荷が高い
ファイルIOと組み合わせた画像データの取り扱いでは、非同期IOの実装がメモリマップドファイルとの連携を考えるうえで参考になります。大容量の画像ファイルを扱う場合、非同期IOによってUIスレッドをブロックせず描画できることがあります。
透過処理とアルファ合成の拡張
単純なBitBltやStretchBltでは背景を完全に上書きしてしまい、半透明画像やマスク画像は扱えません。このような場合に使うのがTransparentBltで、指定した一色を透過として扱うことができます。引数はStretchBltに透過色を追加した形となり、ゲームのスプライトやUIアイコンの表示に適しています。
アルファチャンネル付きの32ビットビットマップを扱うにはAlphaBlend関数を使います。BOOL AlphaBlend(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION)で、BLENDFUNCTION構造体にソースのアルファ値や形式を指定します。Windows XP以降であればMSIMG32.DLL経由で利用できますが、Vista以前ではLayeredWindowを使った代替手法も必要になります。
高機能な画像処理を必要とする場合は、GDI+のBitmapクラスが便利です。アルファチャンネルや色空間の管理を自動化してくれますが、パフォーマンスはGDIの方が軽量なため、用途に応じた使い分けが肝心です。ゲームやアニメーションのような高速描画にはAlphaBlendを、写真加工のような柔軟性重視の処理にはGDI+を選ぶとバランスの良い構成になります。
メモリ管理と落とし穴
BitBltやStretchBltを使う上で最も多いバグは、HDCやHBITMAPの解放忘れです。SelectObjectで選択したビットマップは、DeleteDCやDeleteObjectを呼ぶ前に必ずデフォルトの1x1ビットマップへ戻す必要があります。戻し忘れるとメモリリークとなり、長時間動作するアプリケーションでは深刻な問題を引き起こします。
CreateCompatibleDCで作成したメモリコンテキストは使い終わったらDeleteDCで解放し、CreateCompatibleBitmapで作成したビットマップもDeleteObjectで確実に削除しなければなりません。デバッグ時にはGDIオブジェクトの使用状況をタスクマネージャーやProcess Explorerで確認すると、解放漏れの原因を特定しやすくなります。
GDIはWindowsプログラミングの中でも歴史あるAPIであり、BitBltとStretchBltはその基礎を支える重要な技術です。透過やアルファ合成が必要であればTransparentBltやAlphaBlendに拡張し、リサイズが求められればStretchBltで柔軟に対応できます。サイズ変更の有無、転送モード、メモリ管理を意識して使い分けることで、意図した描画結果を安定して得られるようになります。メモリマップドファイルや非同期IOと組み合わせた大容量データの扱いについては、関連リファレンスが包括的な情報を提供しています。