DIBSectionを作る

概要:DIBとDDBの両方の性質を併せ持つBMP

DIBにはGDI関数を使うことができないという弱点があります。
わざわざDIBで作成しておいて、GDI関数を使う機会はあまり無いように思いますが、
時にはGDI関数が使えないと困る場面に遭遇するかもしれません。

GDI関数が使えるDIBが欲しい……そんな欲張りな願いを叶えてくれるのがDIBSectionです。

■DIBSectionの特徴

DIBSectionはDIBとしてもDDBとしても振る舞う事ができるビットマップです。
CreateDIBSection という DIBSection 作成関数を実行すると、
DIB用のピクセル列(各画素の色に直接アクセスできる)と、
DDB用のビットマップを両方作成してくれます。

ビットマップからメモリデバイスコンテキストを作成すればDDBとして振る舞うことができます。
このDIBとDDBは相互に干渉しあっていて、
どちらに描画しても、もう一方にも反映されます。


■CreateDIBSection関数

DIBSectionの作り方はDIBとDDBの作り方をミックスした感じです。
ただし、DIB用のピクセル列の確保の仕方、解放の仕方が違います。

DIBを作る時はピクセル列の領域を自ら確保していましたが、
DIBSectionを作る時は
CreateDIBSection 関数がピクセル列の領域を自動的に確保してくれます。

HBITMAP CreateDIBSection(
  HDC hdc,              // デバイスコンテキストのハンドル
  CONST BITMAPINFO *pbmi,  // ビットマップデータ
  UINT iUsage,            // データ種類のインジケータ
  VOID **ppvBits,          // ビット値
  HANDLE hSection,        // ファイルマッピングオブジェクトのハンドル
  DWORD dwOffset        // ビットマップのビット値へのオフセット
);


hdc は iUsage が DIB_RGB_COLORS の場合は NULL を指定する事もできます。
*pbmi には作成されるビットマップを指すポインタを渡します。
iUsage には DIB_RGB_COLORS を指定して下さい。
**ppvBits には作成されるビットマップのピクセル列を指すポインタを渡します。
hSection には NULL を指定します。
dwOffset には 0 を指定します。

それでは最も基本的な32ビットDIBSectionを作ってみましょう。

#define WIDTH   200
#define HEIGHT  100

static BITMAPINFO bmpInfo;
static LPDWORD lpPixel;
static HBITMAP hBitmap;
static HDC hMemDC;

//DIBの情報を設定する
bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth=WIDTH;
bmpInfo.bmiHeader.biHeight=HEIGHT;
bmpInfo.bmiHeader.biPlanes=1;
bmpInfo.bmiHeader.biBitCount=32;
bmpInfo.bmiHeader.biCompression=BI_RGB;

//DIBSection作成
hdc=GetDC(hWnd);
hBitmap=CreateDIBSection(hdc,&bmpInfo,DIB_RGB_COLORS,(void**)&lpPixel,NULL,0);
hMemDC=CreateCompatibleDC(hdc);
SelectObject(hMemDC,hBitmap);
ReleaseDC(hWnd,hdc);

前半にDIBを、後半にDDBを作っているのがわかると思います。

■DIBSectionの破棄

DIBSectionを破棄する時、ピクセル列を自ら解放してはいけません。
ビットマップを削除する事でピクセル列も自動的に解放されます。

//自らlpPixelを解放するべからず
DeleteDC(hMemDC);
DeleteObject(hBitmap);  //BMPを削除した時、lpPixelも自動的に解放される

■描画

描画はDIBとしてもDDBとしても行えますが、座標系の違いに注意して下さい。

//描画
for(y=10;y<40;y++){
    for(x=10;x<40;x++){
        lpPixel[x+y*WIDTH]=0x00ff0000;    //赤
    }
}
Rectangle(hMemDC,10,10,40,40);

■転送

表画面への転送もDIBとしてもDDBとしても行えます。

//表画面へ転送
StretchDIBits(hdc,0,0,WIDTH,HEIGHT,
    0,0,WIDTH,HEIGHT,lpPixel,&bmpInfo,DIB_RGB_COLORS,SRCCOPY);
BitBlt(hdc,250,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);

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

■24ビットDIBSection

24ビットDIBSectionの作り方も32ビットDIBSectionの作り方と殆ど同じです。
32ビットDIBが24ビットDIBになっただけ。

ただし描画においてDDBのブラシの色を変えてみました。

後はソースファイルをご覧下さい。

//描画
for(y=10;y<40;y++){
    for(x=10;x<40;x++){
        lpPixel[(x*3  )+y*length]=0;       //B
        lpPixel[(x*3+1)+y*length]=0;       //G
        lpPixel[(x*3+2)+y*length]=0xff;    //R
    }
}
SelectObject(hMemDC,GetStockObject(GRAY_BRUSH));
Rectangle(hMemDC,10,10,40,40);

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

■8ビットDIBSection

8ビットDIBSectionは作れません。

下記プログラムは無理矢理に8ビットDIBSection作成を試みていますが、
DIBとしては正常ですが、DDBとしては異常です。
関数自体は全て成功しているので、何処に問題があるのかは特定できませんが……。

#include<windows.h>
#include<math.h>

#define WIDTH   197
#define HEIGHT  100

#define BIT     8
#define COLOR   ((DWORD)pow(2,BIT))

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static LPBITMAPINFO lpBmpInfo;
    static LPBYTE lpPixel;
    static HBITMAP hBitmap;
    static HDC hMemDC;
    int x,y,i;
    static int length;

    switch(uMsg) {
        case WM_CREATE:
            //4の倍数に補正
            if(WIDTH%4) length=WIDTH+(4-WIDTH%4);
            else length=WIDTH;

            lpBmpInfo=(LPBITMAPINFO)HeapAlloc(GetProcessHeap(),
                HEAP_ZERO_MEMORY,sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(COLOR-1));

            //DIBの情報を設定する
            lpBmpInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
            lpBmpInfo->bmiHeader.biWidth=WIDTH;
            lpBmpInfo->bmiHeader.biHeight=HEIGHT;
            lpBmpInfo->bmiHeader.biPlanes=1;
            lpBmpInfo->bmiHeader.biBitCount=BIT;
            lpBmpInfo->bmiHeader.biCompression=BI_RGB;
            //lpBmpInfo->bmiHeader.biClrUsed=COLOR;

            hdc=GetDC(hWnd);
            hBitmap=CreateDIBSection(hdc,lpBmpInfo,DIB_RGB_COLORS,(void**)&lpPixel,NULL,0);
            hMemDC=CreateCompatibleDC(hdc);
            SelectObject(hMemDC,hBitmap);
            ReleaseDC(hWnd,hdc);

            //カラーテーブルを設定する
            for(i=0;i<COLOR;i++){
                lpBmpInfo->bmiColors[i].rgbBlue=i%128;
                lpBmpInfo->bmiColors[i].rgbGreen=128-i%128;
                lpBmpInfo->bmiColors[i].rgbRed=i%256;
            }

            //描画
            for(i=0,y=25;y<=50;y++){
                for(x=25;x<=50;x++){
                    lpPixel[x+y*length]=(++i)%COLOR;
                }
            }
            return 0;
        case WM_DESTROY:
            HeapFree(GetProcessHeap(),0,lpPixel);
            HeapFree(GetProcessHeap(),0,lpBmpInfo);
            PostQuitMessage(0);
            return 0;
        case WM_PAINT:
            hdc=BeginPaint(hWnd,&ps);
            //表画面へ転送
            StretchDIBits(hdc,0,0,WIDTH,HEIGHT,
                0,0,WIDTH,HEIGHT,lpPixel,lpBmpInfo,DIB_RGB_COLORS,SRCCOPY);
            //BitBlt(hdc,0,0,WIDTH,HEIGHT,hMemDC,0,0,SRCCOPY);       //エラー
            EndPaint(hWnd,&ps);
            return 0;
    }
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

CreateDIBSection 関数はカラーテーブルのメモリー領域を確保しません。

上記プログラムのようにDIBとして処理しているうちは問題ないのですが、
DDBとして処理しようとすると不正な結果が得られます。

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


戻る / ホーム