用Visual C++实现图象渐显和渐隐

摘 要 图 象 的 渐 显/ 渐 隐 被 广 泛 运 用 与 图 象 处 理 和 多 媒 提 娱 乐 软 件。 本 文 基 于Windows 的 调 色 板 动 画 和 时 间 码 技 术 设 计 了 通 用 的 图 象 渐 显 和 渐 隐 算 法, 并 实 现 了 其Visual C++ 程 序 编 码。

---- 关 键 词 渐 显、 渐 隐、 调 色 板、 调 色 板 动 画、 时 间 码

---- 图 象 的 渐 显/ 渐 隐 是 十 分 重 要 的 图 象 效 果, 广 泛 运 用 于 图 象 处 理 和 多 媒 提 娱 乐 软 件。 渐 显/ 渐 隐 算 法 设 计 的 最 大 困 难 是 速 度 控 制, 包 括 定 时 和 快 速 改 变 图 象 中 各 象 素 的 颜 色。 如 采 用 普 通 的 全 图 扫 描 算 法, 则 速 度 较 慢, 很 难 真 正 体 现 渐 显/ 渐 隐 效 果。

---- 利 用Windows(3.x.95/98/NT) 操 作 系 统 特 殊 的 调 色 板 管 理 和 时 间 码 定 时 机 制 能 设 计 出 有 效 的 图 象 渐 显/ 渐 隐 算 法。Windows 提 供 一 种 被 称 为 调 色 板 动 画(palette animation) 的 颜 色 处 理 技 术, 它 通 过 快 速 改 变 颜 色 调 色 板 中 所 选 取 的 表 项 中 的 颜 色 能 模 拟 颜 色 的 变 化。 设 置 时 间 码, 定 时 调 用 该 技 术 使 图 象 颜 色 渐 变 就 能 实 现 图 象 的 渐 显 和 渐 隐。

一、 调 色 板 动 画

---- 在Visual C++ 中 实 现 调 色 板 动 画 依 赖 于MFC 类 库 提 供 的CPalette 类 和CDC 类 中 的 若 干 成 员 函 数, 其 基 本 步 骤 如 下:

  1. 调 用CPalette::CreatePalette(LPLOGPALETTE lpLogPalette) 函 数 创 建 逻 辑 调 色 板, 注 意 将 参 数LPLOGPALETTE 所 指 向 的 各 颜 色 表 项 结 构 的peFlags 域 设 置 为PC_RESERVED, 以 防 止 其 它 窗 口 同 该 调 色 板 匹 配 颜 色。;
  2. 调 用CDC::SelectPalette 和CDC::RealizePalette 函 数 选 择 和 实 现 所 创 建 的 逻 辑 调 色 板;
  3. 调 用CPalette::AnimatePalette 函 数 改 变 颜 色, 实 现 调 色 板 动 画;
  4. 动 画 完 成 后 应 恢 复 系 统 调 色 板。

---- CPalette::AnimatePalette 是 其 中 最 关 键 的 函 数, 其 原 型 如 下: void AnimatePalette( UINT nStartIndex, //起始的表项号 UINT nNumEntries, //变化的表项数 LPPALETTEENTRY lpPaletteColors ); //逻辑调色板表项指针 ---- lpPaletteColors 为 指 向PALETTEENTRY 结 构 的 指 针, 其 中 存 储 着 逻 辑 调 色 板 将 要 更 新 的 颜 色 信 息。PALETTEENTRY 结 构 定 义 如 下:

 
typedef struct tagPALETTEENTRY { // pe BYTE peRed; BYTE peGreen; BYTE peBlue; BYTE peFlags; } PALETTEENTRY;

---- peRed、peGreen、peBlue 分 别 表 示 逻 辑 调 色 板 项 的R、G、B 颜 色 分 量 值。peFlags 应 被 置 为PC_RESERVED 。

---- nStartIndex 为lpPaletteColors 中 将 变 化 的 起 始 表 项 号,nNumEntries 为lpPaletteColors 中 将 变 化 的 表 项 数。

二、 时 间 码 定 时

---- CWnd::SetTimer 函 数 可 设 置 一 个 系 统 时 间 码, 并 指 定 每 经 过 一 定 的 时 间 间 隔 使Windows 系 统 发 送 一 个WM_TIMER 消 息 到 窗 口 的 消 息 队 列 中。 窗 口 在 每 当 接 收 到 相 应 的WM_TIMER 消 息 时 做 一 定 的 处 理, 便 实 现 了 定 时 处 理。

---- 通 常 应 在 窗 口 的 消 息 循 环 中 接 受 和 处 理WM_TIMER 消 息, 这 样 将 很 难 编 制 通 用 的 定 时 操 作。 通 用 的 定 时 操 作 应 将 定 时 处 理 封 装 在 一 个 函 数 中, 而 不 与 其 它 的 代 码 纠 缠 在 一 起。 笔 者 实 现 这 一 技 术 的 技 巧 是, 在 循 环 操 作 中 截 获 窗 口 消 息, 如 消 息 为 指 定 的 时 间 码 消 息, 则 进 行 定 时 处 理; 否 则 分 发 消 息 给 窗 口 消 息 处 理 机 制。 如 果 定 时 操 作 已 结 束, 则 修 改 循 环 标 志, 退 出 循 环。 具 体 的 代 码 如 下:

 
.................................... //设置时间码,pWnd为处理定时操作的窗口对象指针 pWnd- >SetTimer(0x100, uTimeOut, NULL); //屏蔽鼠标操作,使定时操作不受影响 pWnd- >SetCapture(); //开始定时操作 BOOL bDone = FALSE; MSG msg; while (! bDone) { if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_TIMER && msg. WParam == 0x100) { ....................... 定时操作代码 ....................... //如定时操作完成,则设置循环标志,结束操作 if (定时操作完成) bDone = TRUE; } ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } //释放鼠标 ::ReleaseCapture(); //删除时间码 pWnd- >KillTimer(0x100); ................................

---- 函 数PeekMessage 截 获 窗 口 消 息,TranslateMessage 和DispatchMessage 函 数 解 释 和 分 发 除 指 定 时 间 码 消 息 之 外 的 所 有 消 息, 以 避 免 丢 失 消 息。

三、 渐 显

---- 渐 显 就 是 将 显 示 颜 色 由 黑 色(RGB(0, 0, 0)) 逐 渐 变 化 为 图 象 各 象 素 的 颜 色 的 过 程。 开 始 时 调 用CPalette::GetPaletteEntries 函 数 保 存 图 象 调 色 板 的 各 逻 辑 表 项 信 息, 然 后 调 用CPalette::SetPaletteEntries 函 数 将 逻 辑 调 色 板 中 各 逻 辑 表 项 的peRed、peGreen、peBlue 置 为0, 定 时 调 用CPalette::AnimatePalette, 每 次 将 各 逻 辑 表 项 的peRed、peGreen、peBlue 值 增 加 一 个 变 化 量, 直 到 它 们 分 别 等 于 图 象 逻 辑 调 色 板 中 各 逻 辑 表 项 的peRed、peGreen、peBlue 值。

---- 下 面 的 函 数FadeIn 通 过 对 调 色 板 颜 色 表 项 中 的 各 颜 色 分 量 值 先 设 为0, 然 后 进 行 递 增, 直 到 所 有 颜 色 值 都 恢 复 成 原 调 色 板 中 颜 色 值 来 实 现 渐 显。

 
// 图象渐显效果 // 参数: // pWnd -显示图象的窗口 // pPal -调色板指针 // nDeta -各颜色分量的减小量 // uTimeOut -时间的变化量 void FadeIn(CWnd *pWnd, CPalette *pPal, int nDeta, UINT uTimeOut) { //保留原来的调色板颜色表项 int nTotalColors = pPal- >GetEntryCount(); PALETTEENTRY PaletteColors0[256]; pPal- >GetPaletteEntries(0, nTotalColors, PaletteColors0); //先将调色板表项中各颜色分量置为0 PALETTEENTRY PaletteColors1[256]; for (int i=0; iSetPaletteEntries(0, nTotalColors, PaletteColors1); pPal- >AnimatePalette(0, nTotalColors, PaletteColors1); //设置时间码 pWnd- >SetTimer(0x100, uTimeOut, NULL); //开始渐显 pWnd- >SetCapture(); BOOL bDone = FALSE; MSG msg; while (! bDone) { if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_TIMER && msg.wParam == 0x100) { CClientDC dc(pWnd); CPalette *pOldPal = dc.SelectPalette(pPal, FALSE); dc.RealizePalette(); //递增各颜色分量 PALETTEENTRY PaletteColors[256]; pPal- >GetPaletteEntries(0, nTotalColors, PaletteColors); BOOL bRedZero=FALSE; BOOL bGreenZero=FALSE; BOOL bBlueZero=FALSE; for (int i=0; i PaletteColors0[i].peRed) { PaletteColors[i].peRed +="nDeta;" bRedZero="FALSE;" } else if (PaletteColors[i].peRed + 1 < PaletteColors0[i].peRed) { PaletteColors[i].peRed++; bRedZero="FALSE;" } else bRedZero="TRUE;" if (PaletteColors[i].peGreen + nDeta < PaletteColors0[i].peGreen) { PaletteColors[i].peGreen +="nDeta;" bGreenZero="FALSE;" } else if (PaletteColors[i].peGreen + 1 < PaletteColors0[i].peGreen) { PaletteColors[i].peGreen++; bGreenZero="FALSE;" } else bGreenZero="TRUE;" if (PaletteColors[i].peBlue + nDeta < PaletteColors0[i].peBlue) { PaletteColors[i].peBlue +="nDeta;" bBlueZero="FALSE;" } else if (PaletteColors[i].peBlue +1 < PaletteColors0[i].peBlue) { PaletteColors[i].peBlue++; bBlueZero="FALSE;" } else bBlueZero="TRUE;" } //直到恢复原始值结束 bDone="bRedZero" && bGreenZero && bBlueZero; //使系统改变调色板 pPal->AnimatePalette(0, nTotalColors, PaletteColors); } ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } ::ReleaseCapture(); pWnd- >KillTimer(0x100); //恢复原始调色板 pPal- >SetPaletteEntries(0, nTotalColors, PaletteColors0); pPal- >AnimatePalette(0, nTotalColors, PaletteColors0); }

四、 渐 隐

---- 渐 隐 就 是 将 显 示 颜 色 由 图 象 各 象 素 的 颜 色 逐 渐 变 化 为 黑 色(RGB(0, 0, 0)) 的 过 程, 即 定 时 调 用CPalette::AnimatePalette, 每 次 将 各 逻 辑 表 项 的peRed、peGreen、peBlue 值 减 小 一 个 变 化 量, 直 到 它 们 都 为0。

---- 下 面 的 函 数FadeOut 通 过 对 调 色 板 颜 色 表 项 中 的 各 颜 色 分 量 值 进 行 递 减, 直 到 所 有 颜 色 值 都 变 成0( 即 黑 色) 来 实 现 渐 隐。

 
// 图象渐隐效果 // 参数: // pWnd -显示图象的窗口 // pPal -调色板指针 // nDeta -各颜色分量的减小量 // uTimeOut -时间的变化量 void FadeOut(CWnd *pWnd, CPalette *pPal, int nDeta, UINT uTimeOut) { //保留原来的调色板颜色表项 int nTotalColors = pPal- >GetEntryCount(); PALETTEENTRY PaletteColors0[256]; pPal- >GetPaletteEntries(0, nTotalColors, PaletteColors0); //设置时间码 pWnd- >SetTimer(0x100, uTimeOut, NULL); //开始渐隐 pWnd- >SetCapture(); BOOL bDone = FALSE; MSG msg; while (! bDone) { if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_TIMER && msg.wParam == 0x100) { CClientDC dc(pWnd); CPalette *pOldPal = dc.SelectPalette(pPal, FALSE); dc.RealizePalette(); PALETTEENTRY PaletteColors[256]; pPal- >GetPaletteEntries(0, nTotalColors, PaletteColors); BOOL bRedZero=FALSE; BOOL bGreenZero=FALSE; BOOL bBlueZero=FALSE; //递减颜色分量 for (int i=0; i nDeta) { PaletteColors[i].peRed -= nDeta; bRedZero = FALSE; } else if (PaletteColors[i].peRed > 1) { PaletteColors[i].peRed--; bRedZero = FALSE; } else bRedZero = TRUE; if (PaletteColors[i].peGreen > nDeta) { PaletteColors[i].peGreen -= nDeta; bGreenZero = FALSE; } else if (PaletteColors[i].peGreen > 1) { PaletteColors[i].peGreen--; bGreenZero = FALSE; } else bGreenZero = TRUE; if (PaletteColors[i].peBlue > nDeta) { PaletteColors[i].peBlue -= nDeta; bBlueZero = FALSE; } else if (PaletteColors[i].peBlue > 1) { PaletteColors[i].peBlue--; bBlueZero = FALSE; } else bBlueZero = TRUE; } //如所有颜色分量都为0,则结束渐隐 bDone = bRedZero && bGreenZero && bBlueZero; //使系统改变调色板 pPal- >AnimatePalette(0, nTotalColors, PaletteColors); } ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } ::ReleaseCapture(); pWnd- >KillTimer(0x100); //恢复原始调色板 pPal- >SetPaletteEntries(0, nTotalColors, PaletteColors0); pPal- >AnimatePalette(0, nTotalColors, PaletteColors0); }