DIB→DDB変換

概要:DIBの描画内容を反映したDDB作成、またはコピー

DIBからDDBに変換する必要があるのなら最初からDIBSectionで作成してよーって感じですが、
それでもこの処理が必要な時があるかもしれません。

DIBからDDBに変換するには
DDBをBMPファイルに保存する」でやったように、DIBSectionを仲介させる方法もありますが、
もっと直接的な関数が用意されています。

■8ビットDIB作成

何はともあれ、変換元のDIBを作成しましょう。
32ビット、24ビットについてはソースファイルをご覧頂くとして、
ここでは一番難しい8ビットDIBについて見ていきましょう。

今までのプログラムとはちょっと違って、
BITMAPINFO構造体とカラーテーブルの領域を確保する計算式が、
sizeof(BITMAPINFOHEADER)+256*4 になっている事に注目して下さい。
sizeof(BITMAPINFO)+(256-1)*4 より直感的ですよね。

#define WIDTH   197
#define HEIGHT  100

static LPBYTE lpPixel;
static LPBITMAPINFO lpBmpInfo;
int x,y;
static int length;

//4の倍数に補正
if(WIDTH%4) length=WIDTH+(4-WIDTH%4);
else length=WIDTH;
//DIB作成
lpPixel=(LPBYTE)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
    length*HEIGHT+sizeof(BITMAPINFOHEADER)+256*4);
lpBmpInfo=(LPBITMAPINFO)(lpPixel+length*HEIGHT);
//DIBの情報を設定する
lpBmpInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
lpBmpInfo->bmiHeader.biWidth=WIDTH;
lpBmpInfo->bmiHeader.biHeight=HEIGHT;
lpBmpInfo->bmiHeader.biPlanes=1;
lpBmpInfo->bmiHeader.biBitCount=8;
lpBmpInfo->bmiHeader.biCompression=BI_RGB;
//カラーテーブルを設定する
for(x=0;x<256;x++){
    lpBmpInfo->bmiColors[x].rgbBlue=x%128;
    lpBmpInfo->bmiColors[x].rgbGreen=128-x%128;
    lpBmpInfo->bmiColors[x].rgbRed=x%256;
}

■CreateDIBitmap 関数

CreateDIBitmap 関数は、DIBの描画内容で初期化したDDBを作成します。
DIBを作りそうな名前ですが、DIBからDDBを作る関数である事に注意して下さい。

HBITMAP CreateDIBitmap(
  HDC hdc,                     // デバイスコンテキストのハンドル
  CONST BITMAPINFOHEADER *lpbmih,  // ビットマップのデータ
  DWORD fdwInit,                 // 初期化オプション
  CONST VOID *lpbInit,             // 初期化データ
  CONST BITMAPINFO *lpbmi,        // 色形式のデータ
  UINT fuUsage                  // 色データの使い方
);


fdwInit には CBM_INIT を指定します。
 DIBの描画内容で作成するDDBを初期化する指示を意味します。

fdwInit に 0 を指定した場合は、lpbInit , lpbmi , fuUsage は無視されます。
関数はDDBを作るだけになり、DIBの描画内容で初期化しません。

CreateDIBitmap 関数の返却値はビットマップのハンドルです。

HDC hdc;
static HDC hMemDC;
HBITMAP hBitmap;

//DIB→DDB変換
hdc=GetDC(hWnd);
hBitmap=CreateDIBitmap(hdc,&lpBmpInfo->bmiHeader,CBM_INIT,lpPixel,lpBmpInfo,DIB_RGB_COLORS);
hMemDC=CreateCompatibleDC(hdc);
SelectObject(hMemDC,hBitmap);
DeleteObject(hBitmap);
ReleaseDC(hWnd,hdc);

■描画

CreateDIBitmap 関数はDIBからDDBを新たに作成する関数なので、
DIBが無効になったりする事はありません。
それを証明する為にも、両方に対して描画してみましょう。

DIBのカラーテーブルの番号を指定する計算式では 0,1,2 〜 255 がループするようになっています。

for(y=25;y<50;y++){    //DIBに描画
    for(x=25;x<50;x++){
        lpPixel[x+y*length]=( (x-25)+(y-25)*25 )%256;
    }
}
Rectangle(hMemDC,25,25,50,50);    //DDBに描画

■表示&削除

表画面への転送と削除する方法はいつもと同じです。

case WM_PAINT:
    hdc=BeginPaint(hWnd,&ps);
    BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);    //DDBを表画面へ転送
    StretchDIBits(hdc,250,0,WIDTH,HEIGHT,    //DIBを表画面へ転送
        0,0,WIDTH,HEIGHT,lpPixel,lpBmpInfo,DIB_RGB_COLORS,SRCCOPY);
    EndPaint(hWnd,&ps);
    return 0;
case WM_DESTROY:
    DeleteDC(hMemDC);    //DDB削除
    HeapFree(GetProcessHeap(),0,lpPixel);    //DIB削除
    PostQuitMessage(0);
    return 0;

★☆ ダウンロード ☆★

一面緑のDIBがあって、
それで初期化したDDBに白い四角形を描画したのを左、
初期化に使われた後にカラーテーブル総動員で四角形を描画したDIBを右に表示しています。

■DDBとメモリデバイスコンテキスト

DDBとメモリデバイスコンテキストという言葉の区別はややこしく、
単純にDDB作成やメモリデバイスコンテキスト作成などと言った場合は、
「DDBを作ってメモリデバイスコンテキストに選択する」事をどちらも意味している場合が多いです。

私もあやふやに使っていますが、ヘルプでは、
ビットマップの事をDDB (上記プログラムでは、そのハンドルが hBitmap )、
メモリにあるデバイスコンテキストの事をメモリデバイスコンテキスト(同上、hMemDC )
というように使い分けています。

解説の都合上、あやふやな記述があるのはご容赦下さい。

■SetDIBits 関数

DIBからDDBに変換する方法はもう一つあります。
DIBの描画内容を、あらかじめ作成しておいたDDBにコピーする方法です。

SetDIBits 関数は、DIBの描画内容をDDBにコピーします。
DDBを作成する関数ではありません。

int SetDIBits(
  HDC hdc,               // デバイスコンテキストのハンドル
  HBITMAP hbmp,           // ビットマップのハンドル
  UINT uStartScan,          // 最初の走査行
  UINT cScanLines,          // 走査行の数
  CONST VOID *lpvBits,      // ビットマップのビットからなる配列
  CONST BITMAPINFO *lpbmi,  // ビットマップのデータ
  UINT fuColorUse          // カラーインデックスの種類
);


hdc には NULL を指定します。
 デバイスコンテキストが必要なのは fuColorUse に DIB_PAL_COLORS を指定した時だけです。
uStartScan , cScanLines はコピーする縦の範囲です。
fuColorUse には DIB_RGB_COLORS を指定します。

■DDB作成

DDB作成には、先程の CreateDIBitmap 関数を使ってみましょう。
DIBの描画内容で初期化されていないDDBを作るには、
初期化オプション fdwInit に 0 を指定します。

それでは、8ビットDIB作成、DDB作成、描画までのプログラムを見てみましょう。

#define WIDTH   197
#define HEIGHT  100

HDC hdc;
static LPBYTE lpPixel;
static LPBITMAPINFO lpBmpInfo;
static HDC hMemDC;
static HBITMAP hBitmap;
int x,y;
static int length;

//4の倍数に補正
if(WIDTH%4) length=WIDTH+(4-WIDTH%4);
else length=WIDTH;
//DIB作成
lpPixel=(LPBYTE)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
    length*HEIGHT+sizeof(BITMAPINFOHEADER)+256*4);
//DIBの情報を設定する
lpBmpInfo=(LPBITMAPINFO)(lpPixel+length*HEIGHT);
lpBmpInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
lpBmpInfo->bmiHeader.biWidth=WIDTH;
lpBmpInfo->bmiHeader.biHeight=HEIGHT;
lpBmpInfo->bmiHeader.biPlanes=1;
lpBmpInfo->bmiHeader.biBitCount=8;
lpBmpInfo->bmiHeader.biCompression=BI_RGB;
//カラーテーブルを設定する
for(x=0;x<256;x++){
    lpBmpInfo->bmiColors[x].rgbBlue=x%128;
    lpBmpInfo->bmiColors[x].rgbGreen=128-x%128;
    lpBmpInfo->bmiColors[x].rgbRed=x%256;
}

//DDB作成
hdc=GetDC(hWnd);
hBitmap=CreateDIBitmap(hdc,&lpBmpInfo->bmiHeader,0,NULL,NULL,0);
hMemDC=CreateCompatibleDC(hdc);
SelectObject(hMemDC,hBitmap);
ReleaseDC(hWnd,hdc);

for(y=25;y<50;y++){    //DIBに描画
    for(x=25;x<50;x++){
        lpPixel[x+y*length]=( (x-25)+(y-25)*25 )%256;
    }
}
Rectangle(hMemDC,25,25,50,50);    //DDBに描画

hBitmap=CreateDIBitmap(hdc,&lpBmpInfo->bmiHeader,0,NULL,NULL,0);
hBitmap=CreateCompatibleBitmap(hdc,WIDTH,HEIGHT); と書いたのと同じです。

表示&削除の方法はいつもと同じです。

case WM_DESTROY:
    DeleteObject(hBitmap);    //DDB削除
    DeleteDC(hMemDC);         //メモリデバイスコンテキスト削除
    HeapFree(GetProcessHeap(),0,lpPixel);    //DIB削除
    PostQuitMessage(0);
    return 0;
case WM_PAINT:
    hdc=BeginPaint(hWnd,&ps);
    BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);    //DDBを表画面へ転送
    StretchDIBits(hdc,250,0,WIDTH,HEIGHT,    //DIBを表画面へ転送
        0,0,WIDTH,HEIGHT,lpPixel,lpBmpInfo,DIB_RGB_COLORS,SRCCOPY);
    EndPaint(hWnd,&ps);
    return 0;

それでは、マウスの左ボタンをクリックしたら、DIBの描画内容をDDBにコピーする処理を組み込んでみましょう。

case WM_LBUTTONDOWN:
    //DIB→DDB変換
    SetDIBits(NULL,hBitmap,0,HEIGHT,lpPixel,lpBmpInfo,DIB_RGB_COLORS);
    InvalidateRect(hWnd,NULL,FALSE);
    return 0;

★☆ ダウンロード ☆★

ご覧の通り、コピーされた事が確認できます。
32ビット、24ビットについてはソースファイルをご覧下さい。

冒頭でも言いましたが、DIBをDDBに変換する必要があるのなら、
最初からDIBSectionで作成しておくべきだと思います。

また、今回の処理は変換ではなく、作成とコピーである事を間違えないで下さい。
CreateDIBitmap 関数、SetDIBits 関数を実行した後もDIBに描画できる、
つまり、DIBはまだ有効である事がプログラムからも確認できます。


戻る / ホーム