パレットアニメ


色のアニメーション

パレットの機能を駆使することで、面白いタイプのアニメーションを作ることができます
通常、アニメーションとは連続した描画処理やビットマップの表示によって表現されるもので
AVI や MPEG は連続したビットマップの表示によるアニメーションだし
3DCG アニメーションなどは、連続した線と面の描画によるものです

パレットアニメーションは、これらとは異なる方法で動的にプログラムを見せかける方法です
ずばり、連続した描画ではなく連続した色の変更でアニメーションします

原理は簡単で、特定のパレットエントリの色を変更することで
描画されているウィンドウの特定の領域の色が変更するというものです

エントリ番号01020304
ブラシで描いた矩形         : 1番が黒で、それ以外は白 |
|


|
        : 1番を白に変え、2番を黒にする
        : 2番を白に変え、3番を黒にする
        : 3番を白に変え、4番を黒にする

このように、特定のエントリの色を変え、他のエントリを背景にとけ込ませれば
色がついている特定のエントリが動的に動いているかのように見せることができるのです

パレットアニメーションは連続したGDIの描画に比べ
再描画することなく連続的にウィンドウの色を変更させることができるため高速です
この技術は、任天堂のファミリーコンピュータ時代風のゲームを作る時にも応用できるでしょう

パレットアニメーションはそれぞれのエントリが指定した色で固定されなければなりません
PALETTEENTRY 構造体の peFlags メンバに 0 を指定した場合
システムパレットの色と同一の場合、論理パレットのインデックスは勝手にマッピングされます
システムパレット内では、基本的に同一の色が存在しない様に管理されています

しかし、パレットアニメを行う場合、勝手にマッピングされては困ります
その場合は、peFlags に PC_RESERVED を指定しなければなりません

また、パレットの色の変更は AnimatePalette() 関数を使います
この関数は、論理パレットの指定範囲を変更し
同時にシステムパレットにそれを設定して制御を戻します
BOOL AnimatePalette(
	HPALETTE hpal , UINT iStartIndex ,
	UINT cEntries , CONST PALETTEENTRY *ppe
);
hpal には、論理パレットのハンドルを指定します
iStartIndex は論理パレットの変更する最初のエントリ番号を
cEntries は変更するエントリ数を指定します
ppe は置き換える色の情報が入った PALETTEENTRY 構造体の配列へのポインタを指定します
関数が成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

#define MAX_PAL 30

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HBRUSH hBrush;
	int count , x = 10;
	LOGPALETTE * lpPalette;
	PALETTEENTRY palEntry;

	static HPALETTE hPalette;
	static int black = 0 , white = MAX_PAL;
	
	switch (msg) {
	case WM_DESTROY:
		KillTimer(hWnd , 1);
		DeleteObject(hPalette);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		lpPalette = malloc(sizeof (LOGPALETTE) + MAX_PAL * sizeof (PALETTEENTRY));
		lpPalette->palVersion = 0x0300;
		lpPalette->palNumEntries = MAX_PAL;

		for (count = 0 ; count < MAX_PAL ; count++) {
			lpPalette->palPalEntry[count].peRed =
			lpPalette->palPalEntry[count].peGreen =
			lpPalette->palPalEntry[count].peBlue = (count == 0 ? 0 : 255);
			lpPalette->palPalEntry[count].peFlags = PC_RESERVED;
		}

		hPalette = CreatePalette(lpPalette);
		free(lpPalette);
		SetTimer(hWnd , 1 , 100 , NULL);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);
		SelectPalette(hdc , hPalette , FALSE);
		RealizePalette(hdc);

		for (count = 0 ; count < MAX_PAL ; count++ , x += 10) {
			hBrush = CreateSolidBrush(PALETTEINDEX(count));
			SelectObject(hdc , hBrush);
			SelectObject(hdc , GetStockObject(NULL_PEN));

			Rectangle(hdc , x , 10 , x + 10 , 30);
			SelectObject(hdc , GetStockObject(WHITE_BRUSH));
			DeleteObject(hBrush);
		}

		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;
	case WM_TIMER:
		black = black == MAX_PAL ? 0 : black + 1;
		white = white == MAX_PAL ? 0 : white + 1;
		palEntry.peFlags = PC_RESERVED;

		palEntry.peRed =
		palEntry.peGreen =
		palEntry.peBlue = 0;

		AnimatePalette(hPalette , black , 1 , &palEntry);

		palEntry.peRed =
		palEntry.peGreen =
		palEntry.peBlue = 0xFF;

		AnimatePalette(hPalette , white , 1 , &palEntry);

		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	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 色モードで実行してください。それ以外では正常に動作しません)

セットされたタイマーによって、一定間隔で WM_TIMER が実行されます
このメッセージの処理では、黒色のエントリの次のパレットインデックスを黒色に変更し
現在黒色のパレットインデックスを白に変更するとで、ブロックの移動を表現しています

この他にも、あるエントリの色でブラシを作成して、これで矩形を描画し
そのブラシのエントリの色を連続的に変更することで、フェードを表現することもできます


AnimatePalette()

BOOL AnimatePalette(
	HPALETTE hpal , UINT iStartIndex ,
	UINT cEntries , CONST PALETTEENTRY *ppe
);
論理パレットの指定範囲を置き換えます

hpal - 論理パレットのハンドルを指定します
iStartIndex - 論理パレットの変更する最初のエントリ番号を指定します
cEntries - 変更するエントリ数を指定します
ppe - PALETTEENTRY 構造体の配列へのポインタを指定します

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



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