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程序】
效果图

第16章 调色板管理器_16.2 调色板动画_i++

/*------------------------------------------------------------
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

第16章 调色板管理器_16.2 调色板动画_#include_02

/*------------------------------------------------------------
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

第16章 调色板管理器_16.2 调色板动画_#include_03

/*------------------------------------------------------------
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程序】——模拟隧道效果

第16章 调色板管理器_16.2 调色板动画_#include_04

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