DDBを作る

概要:裏画面の作成

Windowsが扱えるBMPにはいくつか種類があって、
普段何気なく使っているのがDDBです。
DDBは Device Dependent Bitmap (デバイス依存ビットマップ)の略です。

タイトルは「DDBを作る」ですが、DDBを作っただけでは意味がありません。
DDBには描画できないからです。
真の目的はメモリデバイスコンテキストの作成であり、
DDBはその情報を設定する為だけに使います。

■メモリデバイスコンテキストの作成

メモリデバイスコンテキストとは、メモリ上に用意された仮想的な画面です。
仮想的な画面ですから、そのままでは表示される事がありません。

メモリデバイスコンテキストにはデバイスコンテキスト同様に描画する事ができます。
BeginPaint 関数が返すデバイスコンテキストのハンドルを使って描画したように、
メモリデバイスコンテキストのハンドルを使って描画する事ができるのです。
従って、メモリデバイスコンテキストを複数個作れば、
画像処理ソフトのレイヤーのような使い方ができます。

メモリデバイスコンテキストを作成するには CreateCompatibleDC 関数を使います。
CreateCompatibleDC 関数は、
指定されたデバイスと互換性のあるメモリデバイスコンテキストを作成します。

HDC CreateCompatibleDC(
  HDC hdc  // デバイスコンテキストのハンドル
);


作成されたばかりのメモリデバイスコンテキストには、
横×縦=1×1ピクセルのモノクローム(1ビット)ビットマップが選択されています。

このままでは使い物にならないので、
任意の大きさ、色数のビットマップを作成して選択し直しましょう。

ビットマップを作成するには CreateCompatibleBitmap 関数を使います。
CreateCompatibleBitmap 関数は、
指定されたデバイスコンテキストに関連付けられているデバイスと互換性のある、ビットマップを作成します。

HBITMAP CreateCompatibleBitmap(
  HDC hdc,   // デバイスコンテキストのハンドル
  int nWidth,  // ピクセル単位のビットマップの幅
  int nHeight  // ピクセル単位のビットマップの高さ
);


ビットマップを選択するには SelectObject 関数を使います。
選択されたビットマップの大きさ、色数がそのままメモリデバイスコンテキストの大きさ、色数になります。
また、ビットマップに描画されている内容もメモリデバイスコンテキストにコピーされます

選択されたビットマップはメモリデバイスコンテキストに関連付けられているように思われますが、
どうもそうじゃないみたいです。
冒頭でも言ったように、ビットマップはメモリデバイスコンテキストの情報を設定するためだけに使います。

もう情報の設定は完了したので、もうビットマップは不要です。
従って、削除してしまいましょう。
ビットマップを削除するには DeleteObject 関数を使います。

選択されたオブジェクトを解放することなく削除するのはルール違反では?
確かにそうです。
この件に関してさる凄いお方に質問したことがありますが、
やっぱり駄目な気がする、みたいな返答でした。
しかし DeleteObject 関数の説明には次のようにあります。

デバイスコンテキストで選択されている描画オブジェクト(ペンまたはブラシ)は削除しないでください。

どこにもビットマップを削除しちゃいかんとは書いてありません。
事実ビットマップを削除してもメモリデバイスコンテキストは全く問題なく動作しますし、
DeleteObject 関数も成功します。

ビットマップは多くのメモリを消費するので早々に削除してしまう方が良いと私は考えます。

#define WIDTH   200
#define HEIGHT  100

HDC hdc;
HBITMAP hBitmap;
static HDC hMemDC;

//メモリデバイスコンテキストを作る
hdc=GetDC(hWnd);
hMemDC=CreateCompatibleDC(hdc);
hBitmap=CreateCompatibleBitmap(hdc,WIDTH,HEIGHT);
ReleaseDC(hWnd,hdc);
SelectObject(hMemDC,hBitmap);
DeleteObject(hBitmap);

■DDB

DDBの特徴は原点が左上にある安全簡単なビットマップです。
ウィンドウのクライアント領域もDDB(デバイスコンテキスト)だと考えられます。
DDBの大きさを超えた領域に描画しても平気だったりする事を
当たり前のように思っているかもしれませんが、よく考えてみれば凄い事です。
本来なら存在しない領域に対する参照は重大なエラーを引き起こすハズです。
DDBは自由度が低いという欠点の代わりに、
安全性を保証され、作成が簡単であるという利点があります。

自由度が低いとされる理由は
「画素値が格納されているメモリ領域に直接アクセスできない」
事に依ります。
もちろん GetPixel 関数や SetPixel 関数を使えば間接的にアクセスできますが、
これらの関数は非常に遅く、実用に供しません。

■裏画面と表画面

メモリデバイスコンテキストに限らず、表示されない仮想的な画面の事を裏画面と言います。
それとは逆に常に表示されている画面の事を表画面と言います。
BeginPaint 関数が返すデバイスコンテキストは表画面ですね。
裏画面には描画した内容が勝手に消えたりしないという特徴があります。
理由は簡単で、確保したメモリ領域の値は勝手に変わったりしないからです。
描画の度に内容が失われる表画面との最大の違いはこれです。

■DDBの初期色

作成されたばかりのDDBはで塗りつぶされています。
そしてそれを選択したメモリデバイスコンテキストもまた、
DDBの描画内容がコピーされ、一面黒くなっているのです。
初期色を変えることはできません。

■メモリデバイスコンテキストに描画する

裏画面への描画は表画面の時と全く同じようにできます。
ただ描画先のデバイスコンテキストに裏画面を指定するだけです。

今回はプログラムの起動直後に描画してみます。
裏画面の内容が保持される事と、原点の位置を確認して下さい。

//描画
SelectObject(hMemDC,GetStockObject(GRAY_BRUSH));
Rectangle(hMemDC,25,25,50,50);

DDBの大きさを超えた領域に描画しても大丈夫です。
はみ出した領域は削除されます

■表画面へ転送する

裏画面はそのままでは表示されません。
表示させたくなったら表画面へ転送させるのです。

転送には BitBlt 関数を使います。

BOOL BitBlt(
  HDC hdcDest,   // コピー先デバイスコンテキストのハンドル
  int nXDest,     // コピー先長方形の左上隅の x 座標
  int nYDest,     // コピー先長方形の左上隅の y 座標
  int nWidth,      // コピー先長方形の幅
  int nHeight,     // コピー先長方形の高さ
  HDC hdcSrc,    // コピー元デバイスコンテキストのハンドル
  int nXSrc,      // コピー元長方形の左上隅の x 座標
  int nYSrc,      // コピー元長方形の左上隅の y 座標
  DWORD dwRop  // ラスタオペレーションコード
);


引数が多くてややこしいですが、
裏画面の(0,0)から幅WIDTH,高さHEIGHTまでの画像を、
表画面の(0,0)からそのままの色で表示するには次のようにします。
表画面の(250,0)から幅WIDTH,高さHEIGHTの大きさ、そのままの色で表示するには次のようにします。

hdc に表画面のハンドルを指定すれば表画面への転送、
裏画面のハンドルを指定すれば裏画面への転送になります。

BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);

幅と高さは参照するコピー元の画像の範囲です。

dwRop には通常 SRCCOPY を指定します。
これはそのまま表示しろ、という意味になります。
他にも色々あって、合成やマスク処理などができますが、
かなりややこしいので、特殊な表示方法を希望するなら DIB を使うといいでしょう。
DIB は次節で解説します。

他にも、拡大縮小に対応した転送関数として StretchBlt 関数があります。

BOOL StretchBlt(
  HDC hdcDest,    // コピー先のデバイスコンテキストのハンドル
  int nXOriginDest,  // コピー先長方形の左上隅の x 座標
  int nYOriginDest,  // コピー先長方形の左上隅の y 座標
  int nWidthDest,   // コピー先長方形の幅
  int nHeightDest,  // コピー先長方形の高さ
  HDC hdcSrc,    // コピー元のデバイスコンテキストのハンドル
  int nXOriginSrc,  // コピー元長方形の左上隅の x 座標
  int nYOriginSrc,  // コピー元長方形の左上隅の y 座標
  int nWidthSrc,   // コピー元長方形の幅
  int nHeightSrc,  // コピー元長方形の高さ
  DWORD dwRop  // ラスタオペレーションコード
);


こちらは更にややこしくなっていますが、
裏画面の(0,0)から幅WIDTH,高さHEIGHTまでの画像を、
表画面の(250,0)から幅WIDTH,高さHEIGHTの大きさ、そのままの色で表示するには次のようにします。

StretchBlt(hdc,250,0,WIDTH,HEIGHT,hMemDC,0,0,WIDTH,HEIGHT,SRCCOPY);

拡大縮小はコピー先の幅と高さで指定して、
コピー元の幅と高さは参照するコピー元の画像の範囲になります。

StretchBlt 関数は BitBlt 関数にコピー元長方形の幅と高さが加わっただけです。
どちらも最初はややこしく感じるでしょうが、慣れれば大したことありません。

ただし拡大縮小の精度は悪いので、基本的には原寸大で表示した方がいいでしょう。

//表画面へ転送
BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);    //左
StretchBlt(hdc,250,0,WIDTH,HEIGHT,hMemDC,0,0,WIDTH,HEIGHT,SRCCOPY);    //右

■メモリデバイスコンテキストの削除

使い終わったメモリデバイスコンテキストは削除しなければなりません。
削除には DeleteDC 関数を使います。

DeleteDC(hMemDC);

★☆ ソースファイル表示 ☆★


戻る / ホーム