16.2.1 弹球
(1)AnimatePallette(hPalette,uStart,uNum,&pe);
①必须运行在支持调色板的视频模式下(即256色,兼容256色不行)
②每个调色板条目PALETTEENTRY的peFlags要设为pC_RESERVED,才能出现动画
③uStart是原始逻辑调色板表的索引,不是PALETTEENTRY数组的索引
④该函数即改变了逻辑调色板的条目,又改变了系统调色板和映射表。所有窗口无需重绘,更改结果直接从视频显示器上反映出来。(可与SetPaletteEntries函数对比,后者只改变逻辑调色板)
(2)本例的改进——因实际上只改变两个条目(球先前位置和当前位置的颜色)
iBallMin = min(iBall,iBallOld);
AnimatePalette(hPalette,iBallMin,2,plp->palPalEntry+ iBallMin);
【Bounce程序】
效果图
/*------------------------------------------------------------ PALANIM.C -- Palette Animation Shell Program (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> extern HPALETTE CreateRoutine(HWND); extern void PaintRoutine(HDC, int, int); extern void TimerRoutine(HDC, HPALETTE); extern void DestroyRoutine(HWND, HPALETTE); extern TCHAR szAppName[]; extern TCHAR szTitle[]; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd; MSG msg; WNDCLASSEX wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hIconSm = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name szTitle, // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters if (!hwnd) return 0; ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } BOOL CheckDisplay(HWND hwnd) { HDC hdc; int iPalSize; hdc = GetDC(hwnd); iPalSize = GetDeviceCaps(hdc, SIZEPALETTE); ReleaseDC(hwnd, hdc); if (iPalSize != 256) { MessageBox(hwnd, TEXT("This program requires that the video ") TEXT("display mode have a 256-color palette."), szAppName, MB_ICONERROR); return FALSE; } return TRUE; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static HPALETTE hPalette; static int cxClient, cyClient; switch (message) { case WM_CREATE: //if (!CheckDisplay(hwnd)) //return -1; hPalette = CreateRoutine(hwnd); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_DISPLAYCHANGE: if (!CheckDisplay(hwnd)) DestroyWindow(hwnd); return 0; case WM_TIMER: hdc = GetDC(hwnd); SelectPalette(hdc, hPalette, FALSE); TimerRoutine(hdc, hPalette); ReleaseDC(hwnd, hdc); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); PaintRoutine(hdc, cxClient, cyClient); 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; //返回TRUE,表示己经实现了调色板 case WM_PALETTECHANGED: if (!hPalette || (HWND)wParam == hwnd) break; hdc = GetDC(hwnd); SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); UpdateColors(hdc); ReleaseDC(hwnd, hdc); break; case WM_DESTROY: DestroyRoutine(hwnd, hPalette); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
//Bounce.c
/*---------------------------------------------------------- Bounce.C -- Palette Animation Demo (c) Charles Petzold,1998 ----------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 TCHAR szAppName[] = TEXT("Bounce"); TCHAR szTitle[] = TEXT("Bounce:Palette Animation Demo"); static LOGPALETTE* plp; HPALETTE CreateRoutine(HWND hwnd) { HPALETTE hPalette; int i; //逻辑调色板,共34种颜色,其中33种给33个小球,最后一种留作背景颜色 plp = malloc(sizeof(LOGPALETTE) + 33 * sizeof(PALETTEENTRY)); plp->palVersion = 0x0300; plp->palNumEntries = 34; for (i = 0; i < 34; i++) { plp->palPalEntry[i].peRed = 255; plp->palPalEntry[i].peGreen = (i == 0 ? 0 : 255); //第1个小球为红色,其余为白色 plp->palPalEntry[i].peBlue = (i == 0 ? 0 : 255); plp->palPalEntry[i].peFlags = (i == 33 ? 0 : PC_RESERVED);//除背景色外,所有小球的颜色都 //不共享(私有的),会改变,作动画用! } hPalette = CreatePalette(plp); SetTimer(hwnd, ID_TIMER, 50, NULL); return hPalette; } void PaintRoutine(HDC hdc, int cxClient, int cyClient) { HBRUSH hBrush; int i, x1, x2, y1, y2; RECT rect; //用调色板索引号为33的颜色画背景颜色 SetRect(&rect, 0, 0, cxClient, cyClient); hBrush = CreateSolidBrush(PALETTEINDEX(33)); FillRect(hdc, &rect, hBrush); DeleteObject(hBrush); //画33个球 SelectObject(hdc, GetStockObject(NULL_PEN)); for (i = 0; i < 33; i++) { x1 = i*cxClient / 33; x2 = (i + 1)*cxClient / 33; //33个小球,按“W”形状排列 if (i < 9) //画W的第1笔,共9个 { y1 = i*cyClient / 9; y2 = (i + 1)*cyClient / 9; } else if (i < 17) { y1 = (16 - i)*cyClient / 9; y2 = (17 - i)*cyClient / 9; } else if (i < 25) { y1 = (i - 16)*cyClient / 9; y2 = (i - 15)*cyClient / 9; } else { y1 = (32 - i)*cyClient / 9; y2 = (33 - i)*cyClient / 9; } hBrush = CreateSolidBrush(PALETTEINDEX(i)); SelectObject(hdc, hBrush); Ellipse(hdc, x1, y1, x2, y2); DeleteObject(hBrush); } return; } void TimerRoutine(HDC hdc, HPALETTE hPalette) { static BOOL bLeftToRight = TRUE; static int iBall; TCHAR szBuff[20]; //将旧的球设为白色 plp->palPalEntry[iBall].peGreen = 255; plp->palPalEntry[iBall].peBlue = 255; iBall += (bLeftToRight ? 1 : -1); //从左向右时,iBall++,相反iBall--; if (iBall == (bLeftToRight ? 33 : -1)) //最左边或最右边 { iBall = (bLeftToRight ? 31 : 1); //最左边时,iBall =31,最右边时iBall =1; bLeftToRight ^= TRUE; //bLeftToRight取反,相当于bLeftToRight =!bLeftToRight; } //设置新球为红色 plp->palPalEntry[iBall].peGreen = 0; plp->palPalEntry[iBall].peBlue = 0; //改变系统调色板 AnimatePalette(hPalette, 0, 33, plp->palPalEntry); wsprintf(szBuff, TEXT("%d"), iBall); TextOut(hdc, 100, 100, szBuff, wsprintf(szBuff, TEXT("%d"), iBall)); return; } void DestroyRoutine(HWND hwnd, HPALETTE hPalette) { KillTimer(hwnd, ID_TIMER); DeleteObject(hPalette); free(plp); return; }
【Fader程序】——只需调色板的一个条目,实现文字的淡入淡出
//PalAnim.c文件:参考Bounce程序
//Fader.c文件
//PalAnim.c文件:参考Bounce程序 //Fader.c文件 /*------------------------------------------------------------ FADER.C -- Palette Animation Demo (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 TCHAR szAppName[] = TEXT("Fader"); TCHAR szTitle[] = TEXT("Fader:Palette Animation Demo"); static LOGPALETTE lp; HPALETTE CreateRoutine(HWND hwnd) { HPALETTE hPalette; //逻辑调色板,只有一种颜色 lp.palVersion = 0x0300; lp.palNumEntries = 1; lp.palPalEntry[0].peRed = 255; lp.palPalEntry[0].peGreen = 255; lp.palPalEntry[0].peBlue = 255; lp.palPalEntry[0].peFlags = PC_RESERVED; hPalette = CreatePalette(&lp); SetTimer(hwnd, ID_TIMER, 50, NULL); return hPalette; } void PaintRoutine(HDC hdc, int cxClient, int cyClient) { static TCHAR szText[] = TEXT("Fade In and Out"); SIZE sizeText; int x, y; SetTextColor(hdc, PALETTEINDEX(0)); GetTextExtentPoint32(hdc, szText, lstrlen(szText), &sizeText); for (x = 0; x < cxClient; x += sizeText.cx) for (y = 0; y < cyClient; y += sizeText.cy) { TextOut(hdc, x, y, szText, lstrlen(szText)); } return; } void TimerRoutine(HDC hdc, HPALETTE hPalette) { static BOOL bFadeIn = TRUE; if (bFadeIn) { lp.palPalEntry[0].peRed -= 4; lp.palPalEntry[0].peGreen -= 4; if (lp.palPalEntry[0].peRed == 3) { bFadeIn = FALSE; } } else { lp.palPalEntry[0].peRed += 4; lp.palPalEntry[0].peGreen += -4; if (lp.palPalEntry[0].peRed == 255) { bFadeIn = TRUE; } } AnimatePalette(hPalette, 0, 1, lp.palPalEntry); return; } void DestroyRoutine(HWND hwnd, HPALETTE hPalette) { KillTimer(hwnd, ID_TIMER); DeleteObject(hPalette); return; }
【AllColor程序】——列出所有颜色
//PalAnim.c文件:参考Bounce程序
//AllColor.c
/*------------------------------------------------------------ AllCOLOR.C -- Palette Animation Demo (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 TCHAR szAppName[] = TEXT("AllColor"); TCHAR szTitle[] = TEXT("AllColor:Palette Animation Demo"); static int iIncr; static PALETTEENTRY pe; HPALETTE CreateRoutine(HWND hwnd) { HPALETTE hPalette; HDC hdc; LOGPALETTE lp; hdc = GetDC(hwnd); //根据颜色分辨率,设置步长。如24位,时iIncr=1;16位时,iIncr=4; iIncr = 1 << (8 - GetDeviceCaps(hdc, COLORRES) / 3); ReleaseDC(hwnd, hdc); //创建逻辑调色板 lp.palVersion = 0x0300; lp.palNumEntries = 1; lp.palPalEntry[0].peRed = 0; lp.palPalEntry[0].peGreen = 0; lp.palPalEntry[0].peBlue = 0; lp.palPalEntry[0].peFlags = PC_RESERVED; hPalette = CreatePalette(&lp); //为方便书写,保存第一个颜色条目 pe = lp.palPalEntry[0]; SetTimer(hwnd, ID_TIMER, 10, NULL); return hPalette; } void DisplayRGB(HDC hdc, PALETTEENTRY* ppe) { TCHAR szBuffer[16]; wsprintf(szBuffer, TEXT(" %02X-%02X-%02X "), ppe->peRed, ppe->peGreen, ppe->peBlue); TextOut(hdc, 0, 0, szBuffer, lstrlen(szBuffer)); } void PaintRoutine(HDC hdc, int cxClient, int cyClient) { HBRUSH hBrush; RECT rect; //将调色板第1个条目的颜色画在整个客户区 hBrush = CreateSolidBrush(PALETTEINDEX(0)); SetRect(&rect, 0, 0, cxClient, cyClient); FillRect(hdc, &rect, hBrush); DeleteObject(SelectObject(hdc, GetStockObject(WHITE_BRUSH))); DisplayRGB(hdc, &pe); return; } void TimerRoutine(HDC hdc, HPALETTE hPalette) { static BOOL bRedUp = TRUE, bGreenUp = TRUE, bBlueUp = TRUE; //计算新颜色 pe.peBlue += (bBlueUp ? iIncr : -iIncr); //B先从0->255->0,再G从0->255->0,最后R if (pe.peBlue == (BYTE)(bBlueUp ? 0 : 256 - iIncr)) //peBlue是否到边界 { //向上到边界时,将peBlue设为255,向下到边界时,设为0 pe.peBlue = (bBlueUp ? 256 - iIncr : 0); bBlueUp ^= TRUE; //取反 pe.peGreen += (bGreenUp ? iIncr : -iIncr); if (pe.peGreen == (BYTE)(bGreenUp ? 0 : 256 - iIncr))//peGreen是否到边界 { //向上到边界时,将peGreen设为255,向下到边界时,设为0 pe.peGreen = (bGreenUp ? 256 - iIncr : 0); bGreenUp ^= TRUE; //取反 pe.peRed += (bRedUp ? iIncr : -iIncr); if (pe.peRed == (BYTE)(bRedUp ? 0 : 256 - iIncr)) //peRed是否到边界 { //向上到边界时,将peRed设为255,向下到边界时,设为0 pe.peRed = (bRedUp ? 256 - iIncr : 0); bRedUp ^= TRUE; } } } AnimatePalette(hPalette, 0, 1, &pe); DisplayRGB(hdc, &pe); return; } void DestroyRoutine(HWND hwnd, HPALETTE hPalette) { KillTimer(hwnd, ID_TIMER); DeleteObject(hPalette); return; }
16.2.3 工程应用
【Pipes程序】——模拟管道内流体的流动现象
//PalAnim.c文件:参考Bounce程序
//Pipes.c
/*------------------------------------------------------------ PIPES.C -- Palette Animation Demo (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 TCHAR szAppName[] = TEXT("Pipes"); TCHAR szTitle[] = TEXT("Pipes:Palette Animation Demo"); static LOGPALETTE* plp; HPALETTE CreateRoutine(HWND hwnd) { HPALETTE hPalette; PALETTEENTRY* ppe; int i; //创建33种颜色的调色板 plp = malloc(sizeof(LOGPALETTE) + 32 * sizeof(PALETTEENTRY)); //初始化逻辑调色板的颜色 plp->palVersion = 0x0300; plp->palNumEntries = 16; //这里给33也没错 ppe = plp->palPalEntry; //i : 0 1 2 3 4 5 6 7 8 //16-i: 16 15 14 13 12 11 10 9 8 //16+i:16 17 18 19 20 21 22 23 24 //32-i: 32 31 30 29 28 27 26 25 24 for (i = 0; i <= 8; i++) { ppe[i].peRed = (BYTE)min(255, 0x20 * i); //0-256 ppe[i].peGreen = 0; ppe[i].peBlue = (BYTE)min(255, 0x20 * i); ppe[i].peFlags = PC_RESERVED; ppe[16 - i] = ppe[i]; ppe[16 + i] = ppe[i]; //这以后的颜色是为了重复颜色,以达到动画的连续性 ppe[32 - i] = ppe[i]; } hPalette = CreatePalette(plp); SetTimer(hwnd, ID_TIMER, 100, NULL); return hPalette; } void PaintRoutine(HDC hdc, int cxClient, int cyClient) { HBRUSH hBrush; int i; RECT rect; //画窗口背景 SetRect(&rect, 0, 0, cxClient, cyClient); hBrush = SelectObject(hdc, GetStockObject(WHITE_BRUSH)); FillRect(hdc, &rect, hBrush); //画内部的管道 for (i = 0; i < 128; i++) { hBrush = CreateSolidBrush(PALETTEINDEX(i % 16)); SelectObject(hdc, hBrush); //从右向左画 rect.left = (127 - i)*cxClient / 128; rect.right = (128 - i)*cxClient / 128; rect.top = 4 * cyClient / 14; rect.bottom = 5 * cyClient / 14; FillRect(hdc, &rect, hBrush); //从左向右画 rect.left = i * cxClient / 128; rect.right = (i + 1)*cxClient / 128; rect.top = 9 * cyClient / 14; rect.bottom = 10 * cyClient / 14; FillRect(hdc, &rect, hBrush); DeleteObject(SelectObject(hdc, GetStockObject(WHITE_BRUSH))); } //画管道的边缘 MoveToEx(hdc, 0, 4 * cyClient / 14, NULL); LineTo(hdc, cxClient, 4 * cyClient / 14); MoveToEx(hdc, 0, 5 * cyClient / 14, NULL); LineTo(hdc, cxClient, 5 * cyClient / 14); MoveToEx(hdc, 0, 9 * cyClient / 14, NULL); LineTo(hdc, cxClient, 9 * cyClient / 14); MoveToEx(hdc, 0, 10 * cyClient / 14, NULL); LineTo(hdc, cxClient, 10 * cyClient / 14); return; } void TimerRoutine(HDC hdc, HPALETTE hPalette) { static int iIndex; //共33种颜色,逻辑调色板的第1种颜色的入口点每次往后一位,相当于颜色在移动 //比如当iIndex=7时,则逻辑调色板的入口点从数组palPalEntry[7]-palPalEntry[23] AnimatePalette(hPalette, 0, 16, plp->palPalEntry + iIndex); iIndex = (iIndex + 1) % 16; return; } void DestroyRoutine(HWND hwnd, HPALETTE hPalette) { free(plp); KillTimer(hwnd, ID_TIMER); DeleteObject(hPalette); return; }
【Tunnel程序】——模拟隧道效果
//PalAnim.c文件:参考Bounce程序
//Tunnel.c
/*------------------------------------------------------------ TUNNEL.C -- Palette Animation Demo (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #define ID_TIMER 1 TCHAR szAppName[] = TEXT("Tunnel"); TCHAR szTitle[] = TEXT("Tunnel:Palette Animation Demo"); static LOGPALETTE* plp; HPALETTE CreateRoutine(HWND hwnd) { BYTE byGrayLevel; HPALETTE hPalette; PALETTEENTRY* ppe; int i; //创建256种颜色的调色板 plp = malloc(sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)); //初始化逻辑调色板的颜色 plp->palVersion = 0x0300; plp->palNumEntries = 128; ppe = plp->palPalEntry; for (i = 0; i < 128; i++) { //byGrayLevel =0,4,8,16,24,...,252,255,252,...,4 if (i < 64) byGrayLevel = (BYTE)(i * 4); else byGrayLevel = (BYTE)min(255, 4 * (128 - i)); ppe[i].peRed = byGrayLevel; ppe[i].peGreen = byGrayLevel; ppe[i].peBlue = byGrayLevel; ppe[i].peFlags = PC_RESERVED; //这以后的颜色是为了重复颜色,以达到动画的连续性 ppe[i + 128].peRed = byGrayLevel; ppe[i + 128].peGreen = byGrayLevel; ppe[i + 128].peBlue = byGrayLevel; ppe[i + 128].peFlags = PC_RESERVED; } hPalette = CreatePalette(plp); SetTimer(hwnd, ID_TIMER, 50, NULL); return hPalette; } void PaintRoutine(HDC hdc, int cxClient, int cyClient) { HBRUSH hBrush; int i; RECT rect; //画128个矩形 for (i = 0; i < 127; i++) { hBrush = CreateSolidBrush(PALETTEINDEX(i)); //从外向里画各个矩形 rect.left = i *cxClient / 255; rect.top = i* cyClient / 255; rect.right = cxClient - i* cxClient / 255; rect.bottom = cyClient - i* cyClient / 255; FillRect(hdc, &rect, hBrush); DeleteObject(hBrush); } return; } void TimerRoutine(HDC hdc, HPALETTE hPalette) { static int iLevel; //共256种颜色,逻辑调色板的第1种颜色的入口点每次往后一位,相当于颜色在移动 //比如当iLevel=60时,则逻辑调色板的入口点从数组palPalEntry[60]-palPalEntry[208] iLevel = (iLevel + 1) % 128; AnimatePalette(hPalette, 0, 128, plp->palPalEntry + iLevel); return; } void DestroyRoutine(HWND hwnd, HPALETTE hPalette) { free(plp); KillTimer(hwnd, ID_TIMER); DeleteObject(hPalette); return; }