DIB とパレット
DIB パレットインデックス
DIB 関連の関数には、DIB_*_COLORS というフラグを指定する引数がありました
* には RGB、または PAL のいずれかで、この二つのいずれかを指定するものでしたが
通常、フルカラービットマップを表示する時は DIB_RGB_COLORS を使いましたね
もう一つの DIB_PAL_COLORS は、8 ビット以下のカラーテーブルを保有する DIB を
256 色モードの状態で表示するためのものです
多少アルゴリズムが複雑になりますが、処理速度を向上させることができるようになります
DIB_PAL_COLORS は、カラーテーブルを論理パレットに設定し
さらに、カラーテーブルをパレットインデックスに変更します
DIB のカラーテーブルを論理パレットのインデックスに変換することで
Windows は検索やマッピングといった処理から解放され、描画速度の向上が期待できます
これを実現するには、まず DIB のカラーテーブルを読み込み
カラーテーブル配列と同様のエントリを持つ論理パレットを生成します
次に、DIB カラーテーブルを論理パレットのインデックスに置き換えます
方法は、カラーテーブルの先頭から WORD サイズごとに
生成した論理パレットのインデックスにマッピングします
#include <windows.h>
#define PAL_SIZE (1 << bmpInfo->bmiHeader.biBitCount)
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO * bmpInfo;
BYTE * bPixelBits;
LOGPALETTE * NewLogPal(const RGBQUAD *pColorTable , int iSize) {
LOGPALETTE *lpPalette;
int count;
lpPalette = malloc(sizeof (LOGPALETTE) + iSize * sizeof (PALETTEENTRY));
lpPalette->palVersion = 0x0300;
lpPalette->palNumEntries = iSize;
for (count = 0 ; count < iSize ; count++) {
lpPalette->palPalEntry[count].peRed = pColorTable[count].rgbRed;
lpPalette->palPalEntry[count].peGreen = pColorTable[count].rgbGreen;
lpPalette->palPalEntry[count].peBlue = pColorTable[count].rgbBlue;
lpPalette->palPalEntry[count].peFlags = NULL;
}
return lpPalette;
}
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
int count;
LOGPALETTE * lpPalette;
static HPALETTE hPalette;
switch (msg) {
case WM_DESTROY:
free(bmpInfo);
free(bPixelBits);
DeleteObject(hPalette);
PostQuitMessage(0);
return 0;
case WM_CREATE:
if (PAL_SIZE <= 256) {
lpPalette = NewLogPal(bmpInfo->bmiColors , PAL_SIZE);
hPalette = CreatePalette(lpPalette);
free(lpPalette);
for (count = 0 ; count < PAL_SIZE ; count++)
*(((WORD *)bmpInfo->bmiColors) + count) = (WORD)count;
}
else {
hdc = GetDC(hWnd);
hPalette = CreateHalftonePalette(hdc);
ReleaseDC(hWnd , hdc);
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd , &ps);
SelectPalette(hdc , hPalette , FALSE);
RealizePalette(hdc);
if (PAL_SIZE <= 256) SetDIBitsToDevice(
hdc , 0 , 0 ,
bmpInfo->bmiHeader.biWidth , bmpInfo->bmiHeader.biHeight ,
0 , 0 , 0 , bmpInfo->bmiHeader.biHeight ,
bPixelBits , bmpInfo , DIB_PAL_COLORS
);
else SetDIBitsToDevice(
hdc , 0 , 0 ,
bmpInfo->bmiHeader.biWidth , bmpInfo->bmiHeader.biHeight ,
0 , 0 , 0 , bmpInfo->bmiHeader.biHeight ,
bPixelBits , bmpInfo , DIB_RGB_COLORS
);
EndPaint(hWnd , &ps);
return 0;
case WM_QUERYNEWPALETTE:
if (!hPalette) return FALSE;
hdc = GetDC(hWnd);
SelectPalette(hdc , hPalette , FALSE);
RealizePalette(hdc);
InvalidateRect(hWnd , NULL , TRUE);
ReleaseDC(hWnd , hdc);
return TRUE;
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow) {
HWND hWnd;
MSG msg;
WNDCLASS winc;
HANDLE hFile;
DWORD dwBytes;
int iBmpInfoLength , iPixelBitLength;
hFile = CreateFile(lpCmdLine , GENERIC_READ , 0 , NULL ,
OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);
if (hFile == INVALID_HANDLE_VALUE) return 1;
ReadFile(hFile , &bmpFileHeader , sizeof (BITMAPFILEHEADER) , &dwBytes , NULL);
if (bmpFileHeader.bfType != 0x4D42) {
MessageBox(NULL , TEXT("This is not a bitmap file") , NULL , MB_OK);
return 1;
}
iBmpInfoLength = bmpFileHeader.bfOffBits - sizeof (BITMAPFILEHEADER);
bmpInfo = malloc(iBmpInfoLength);
ReadFile(hFile , bmpInfo , iBmpInfoLength , &dwBytes , NULL);
iPixelBitLength = bmpFileHeader.bfSize - bmpFileHeader.bfOffBits;
bPixelBits = malloc (iPixelBitLength);
ReadFile(hFile ,bPixelBits , iPixelBitLength , &dwBytes , NULL);
CloseHandle(hFile);
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return 1;
hWnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hWnd == NULL) return 1;
while (GetMessage(&msg , NULL , 0 , 0 )) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムは、コマンドライン引数から 8 ビット DIB を指定します
すると、カラーテーブルから論理パレットを生成し
さらに、カラーテーブルを生成した論理パレットとマッピングして
SetDIBitsToDevice() で DIB_PAL_COLORS フラグを用いてウィンドウに描画します
上の図は、ビットマップテストプログラムでいつも登場してもらっている二人組の DIB を
画像処理ソフトで 256 色の 8 ビット DIB に変換して表示したものです
因みに、このプログラムはフルカラーの DIB が渡されるとハーフトーンで描画します
ソースはいささか冗長ですが、重要な部分は WM_CREATE メッセージの次の部分です
if (PAL_SIZE <= 256) {
lpPalette = NewLogPal(bmpInfo->bmiColors , PAL_SIZE);
lpPalette = CreatePalette(lpPalette);
free(lpPalette);
for (count = 0 ; count < PAL_SIZE ; count++)
*(((WORD *)bmpInfo->bmiColors) + count) = (WORD)count;
}
NewLogPal() はカラーテーブルとカラーテーブルのサイズを渡すことで
そのカラーテーブルを再現した LOGPALETTE 構造体を返すお手製の関数です
この構造体から論理パレットを作成するというところまではお馴染みです
その後、for ループでカラーテーブルを大胆にも書き換えてしまいます
NewLogPal() は、カラーテーブルの要素順にパレットのエントリを作成しているため
カラーテーブルを論理パレットにマップする時は、0 から要素数だけ代入します
この時、カラーテーブルのサイズは符号なし 16 ビットでなければなりません