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

发表于:2007-06-17来源:作者:点击数: 标签:
摘要图象的渐显/渐隐被广泛运用与图象处理和多媒体娱乐软件。本文基于 Windows的调色板动画和时间码技术设计了通用的图象渐显和渐隐算法,并实 现了其VisualC++程序编码。 关键词渐显、渐隐、调色板、调色板动画、时间码 图象的渐显/渐隐是十分重要的图象效

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

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

渐显/渐隐效果。
利用Windows(3.x.95/98/NT)操作系统特殊的调色板管理和时间码定时机制
能设计出有效的图象渐显/渐隐算法。Windows提供一种被称为调色板动画
(palette animation)的颜色处理技术,它通过快速改变颜色调色板中所选
取的表项中的颜色能模拟颜色的变化。设置时间码,定时调用该技术使图象
颜色渐变就能实现图象的渐显和渐隐。
一、调色板动画
在Visual C++中实现调色板动画依赖于MFC类库提供的CPalette类和CDC类中
的若干成员函数,其基本步骤如下:

调用CPalette::CreatePalette(LPLOGPALETTE lpLogPalette)函数创建逻辑
调色板,注意将参数LPLOGPALETTE所指向的各颜色表项结构的peFlags域设置
为PC_RESERVED,以防止其它窗口同该调色板匹配颜色。;
调用CDC::SelectPalette和CDC::RealizePalette函数选择和实现所创建的
逻辑调色板;
调用CPalette::AnimatePalette函数改变颜色,实现调色板动画;
动画完成后应恢复系统调色板。
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; i PaletteColors1[i].peRed = 0;
PaletteColors1[i].peGreen = 0;
PaletteColors1[i].peBlue = 0;
PaletteColors1[i].peFlags = PC_RESERVED;
pPal->SetPaletteEntries(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 if (PaletteColors[i].peRed + nDeta <
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 if (PaletteColors[i].peRed > 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);

原文转自:http://www.ltesting.net