汎用パレット


256色でビットマップの表示

以前、DIB が256色モード時に表示された時の Windows の動作は説明しました
特に指定がない場合、Windows は標準の20色のパレットエントリを駆使して表示します
もちろん、通常のフルカラービットマップを表示するには不十分な数です

そこで、アプリケーションが DIB を極力正常に表示する方法が必要になります
DIB の情報ヘッダの次に、カラーテーブルが指定されている場合は
このカラーテーブルを読み込んで、論理パレットを作成してリアライズすれば問題はありません

しかし、フルカラーの DIB にカラーパレットは存在しません
カラーテーブルが存在しない DIB の場合は汎用パレットを作成します
汎用パレットとは、表示されるビットマップの全てが参照するパレットを指します

実は、ビットマップを表示するデバイスコンテキストにパレットが設定されている場合
Windows は標準の20色ではなく、設定されているパレットの中から近似色を検索します
これは、次のプログラムを実行すれば明らかになるでしょう
#include <windows.h>

BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO bmpInfo;
BYTE * bPixelBits;

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(bPixelBits);
		DeleteObject(hPalette);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		lpPalette = malloc(sizeof (LOGPALETTE) + 63 * sizeof (PALETTEENTRY));
		lpPalette->palVersion = 0x0300;
		lpPalette->palNumEntries = 64;

		for (count = 0 ; count < 64 ; count++) {
			lpPalette->palPalEntry[count].peRed =
			lpPalette->palPalEntry[count].peGreen =
			lpPalette->palPalEntry[count].peBlue = 4 * count;
			lpPalette->palPalEntry[count].peFlags = NULL;
		}

		hPalette = CreatePalette(lpPalette);
		free(lpPalette);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		SelectPalette(hdc , hPalette , FALSE);
		RealizePalette(hdc);

		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;

	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;
	}

	ReadFile(hFile , &bmpInfo , sizeof (BITMAPINFOHEADER) , &dwBytes , NULL);
	bPixelBits = (BYTE *) malloc (bmpFileHeader.bfSize - bmpFileHeader.bfOffBits);
	ReadFile(hFile ,bPixelBits ,
		bmpFileHeader.bfSize - bmpFileHeader.bfOffBits , &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;
}


このプログラムは、コマンドライン引数から入力されたビットマップを表示するものですが
256色モードの場合、Windows は設定されているパレットを参照します
このプログラムでは、64色のグレーシェードの論理パレットをリアライズしているため
結果として、上のような白黒のビットマップが表示されます

このプログラムは、デバイスコンテキストに論理パレットが設定されていれば
Windows は標準の20色を参照しないことを証明するために作りましたが
実際に汎用パレットを実用する時は、色の配分に注意する必要があります


ハーフトーン

汎用パレットによって、できるだけフルカラービットマップの色を忠実に表示したい場合
自分で汎用パレットを作るという方法も一つですが
実は、Win32 API がすでに最適な汎用パレットを提供しています

様々なビットマップに適した汎用パレットとして
Windows は CreateHalftonePalette() 関数を提供しています
この関数が返すパレットをハーフトーンパレットと呼びます

HPALETTE CreateHalftonePalette(HDC hdc);

hdc には、デバイスコンテキストのハンドルを指定します
成功すれば論理ハーフトーンパレットのハンドルが、失敗すると 0 が返ります
#include <windows.h>

BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO bmpInfo;
BYTE * bPixelBits;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static HPALETTE hPalette;

	switch (msg) {
	case WM_DESTROY:
		free(bPixelBits);
		DeleteObject(hPalette);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hdc = GetDC(hWnd);
		hPalette = CreateHalftonePalette(hdc);
		ReleaseDC(hWnd , hdc);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		SelectPalette(hdc , hPalette , FALSE);
		RealizePalette(hdc);

		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;

	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;
	}

	ReadFile(hFile , &bmpInfo , sizeof (BITMAPINFOHEADER) , &dwBytes , NULL);
	bPixelBits = (BYTE *) malloc (bmpFileHeader.bfSize - bmpFileHeader.bfOffBits);
	ReadFile(hFile ,bPixelBits ,
		bmpFileHeader.bfSize - bmpFileHeader.bfOffBits , &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;
}


このプログラムは、基本的に先ほどのプログラムと同様のものですが
デバイスコンテキストにハーフトーンパレットを指定している点で異なります

ハーフトーンパレットは、伸縮モードの指定で HALFTONE を指定した時にも使います
SetStretchBltMode() は以前やったので、「拡大と縮小」を参照してください

ただし、SetStretchBltMode() で HALFTONE をセットした場合
同時に SetBrushOrgEx() でブラシの原点を修正する必要があります
この関数は、ブラシのズレを修正するためのものですが
単純に SetStretchBltMode() で HALFTONE をセットした時のお約束と思ってもかまいません
BOOL SetBrushOrgEx(
	HDC hdc , int nXOrg ,
	int nYOrg , LPPOINT lppt
);
hdc にはデバイスコンテキストのハンドルを
nXOrg には X 座標、nYOrg には Y 座標をそれぞれ指定します
lppt には、以前の原点を取得するための POINT 構造体へのポインタを指定します
以前の原点を取得する必要がない場合は NULL を指定してください
関数が成功すると 0 以外、失敗すると 0 が返ります

Windws NT では、ブラシの原点は自動的に設定されます
この関数は、伸縮モードで HALFTONE を指定した時に呼び出します

伸縮モードの HALFTONE は、他の伸縮モードよりも高度な処理で
処理速度は犠牲になりますが、高品質なイメージを提供できます
#include <windows.h>

BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO bmpInfo;
BYTE * bPixelBits;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	static HPALETTE hPalette;

	switch (msg) {
	case WM_DESTROY:
		free(bPixelBits);
		DeleteObject(hPalette);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hdc = GetDC(hWnd);
		hPalette = CreateHalftonePalette(hdc);
		ReleaseDC(hWnd , hdc);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		GetClientRect(hWnd , &rect);
		SetStretchBltMode(hdc , HALFTONE);
		SetBrushOrgEx(hdc , 0 , 0 , NULL);
		SelectPalette(hdc , hPalette , FALSE);
		RealizePalette(hdc);

		StretchDIBits(
			hdc , 0 , 0 , rect.right , rect.bottom , 0 , 0 ,
			bmpInfo.bmiHeader.biWidth , bmpInfo.bmiHeader.biHeight ,
			bPixelBits , &bmpInfo , DIB_RGB_COLORS , SRCCOPY
		);
		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;

	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;
	}

	ReadFile(hFile , &bmpInfo , sizeof (BITMAPINFOHEADER) , &dwBytes , NULL);
	bPixelBits = (BYTE *) malloc (bmpFileHeader.bfSize - bmpFileHeader.bfOffBits);
	ReadFile(hFile ,bPixelBits ,
		bmpFileHeader.bfSize - bmpFileHeader.bfOffBits , &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;
}
このプログラムは、先ほどのプログラムの SetDIBitsToDevice() 関数を
StretchDIBits() 関数に置き換え、伸縮モードを変更したものです


CreateHalftonePalette()

HPALETTE CreateHalftonePalette(HDC hdc);

論理ハーフトーンパレットを作成して返します

hdc - デバイスコンテキストのハンドルを指定します

戻り値 - 論理ハーフトーンのハンドル。失敗すると 0

SetBrushOrgEx()

BOOL SetBrushOrgEx(
	HDC hdc , int nXOrg ,
	int nYOrg , LPPOINT lppt
);
ブラシの原点を変更します

hdc - デバイスコンテキストのハンドルを指定します
nXOrg - 新しい原点となる X 座標を指定します
nYOrg - 新しい原点となる Y 座標を指定します
lppt - 以前の原点を取得する POINT 構造体へのポインタを指定します

戻り値 - 関数が成功すると 0 以外、失敗すると 0



前のページへ戻る次のページへ