保持渐变时间为常量的方法是固定持续时间——假设为 3,000 毫秒——然后根据实际逝去的时间计算 alpha 值,假设第一次迭代发生在
t 20 毫秒。那么你可以用的 alpha 值为 20*255/3000 = 1 (取最近似的一个整数)。然后根据当前时间计算的 alpha
值立即进行另一次渐变。如果逝去的时间超过一半,你最终的 alpha 值是 .5。通过用实际逝去的时间计算 alpha
值,你可以保证渐变总是按时完成,但缺点是较大的图像无法平滑地完成渐变,因为它们的迭代过程更少,在 AlphaBlend 中花的时间更多。 逆风编程精品
所有这些实现难易程度不一。Figure 3 和
Figure 4 是详细代码。当 CPictureView::OnUpdate 获得 PREOPENDOC
通知时,保存旧图像之后,它将数据成员 m_iStartTime
置为当前时钟时间。时钟时间是自该进程启动后的“时钟嘀嗒”数。每秒嘀嗒数为 CLOCKS_PER_SEC(通常为 1,000)。当 OnUpdate
返回时,控制传回到文档和 MFC,它调用视图的 OnInitialUpdate 函数,该函数调用 OnUpdate,它重画窗口。最后,Windows
向你的视图发送 MW_PAINT 消息,MFC 通过调用视图的虚拟 OnDraw 方法处理该消息。这是 MFC 的基本常识:在某个视图中,绘图在
OnDraw 进行,而不是 OnPaint 中。CPictureView 是这样绘制的:
void CPictureView::OnDraw(CDC* pDC)
{
CPicture* ppic = // get current picture
if (m_iStartTime) {
// do blend
} else {
// render as normal
ppic->Render(pDC,rc);
}
}
我省略了渐变的细节,主要突出 CPictureView 如何用 m_iStartTime
作为标志来确定是否渐变。以下是实现渐变需要的基本步骤。
- 创建一个内存 DC;
- 在该内存 DC 中绘制位图;
- 计算渐变的 alpha 值;
- 在该内存 DC 中
AlphaBlend 位图;
- 将结果拷贝到屏幕(BitBlt);
在画面以外的内存 DC 中进行渐变然后拷贝到屏幕这一步是很重要的;否则用户将会看到一闪而过的中间图像。因为 AlphaBlend
需要设备上下文,而不是 CPicture 对象,首先绘制新图像(所以我调用 CPicture::Render
),然后在其上渐变旧图像要方便一些。所以我用的 alpha 值与先从旧的图像开始显示所用的 alpha 值相反转(1-alpha)
,换句话说,不是先从旧图像开始,然后在上面以越来越多的效果渐变新图像。我是先从新图像开始,然后在上面以越来越少的效果渐变旧图像。很聪明,不是吗?网格效果处理方法一样。以下是计算
AlpahBlend alpha 值的关键代码行: int alpha = ((clock() - m_iStartTime) * 255) / BLEND_DURATION;
alpha = max(255-alpha,0);
渐变之后,如果计算的 alpha 值大于 0,那么就需要处理更多的渐变效果。所以 OnDraw 调用 Invalidate(FALSE)
在不擦除背景的情况下而重画窗口。Windows 发送另一个 WM_PAINT 消息——只是要等到当前消息处理完成。这样一来(使 WM_PAINT
为有效消息),没有阻塞。在渐变期间,用户仍然能使用应用程序。你可以在渐变期间改变窗口大小来证明这一点。CPictureView
在新的窗口尺寸下保持渐变。
如果算出的 alpha 值为 0,渐变完成。计时器停止。这时,OnDraw 调用辅助函数 StopBlending,该函数删除旧图像并将
m_iStartTime 设置为 0,暗示 OnDraw 停止渐变。现在当视图需要绘制时,OnDraw 通过调用 CPicture::Render
进行常规绘制,直接呈现新图像,不发生渐变。
如果你使用活动模板库(ATL)CImage 类来保存图像(而不是用 CPicture,这是我很久以前实现的一个类,当时 CImage
还未出现),你可以用 CImage::AlphaBlend,不过用它来进行渐变会产生一些开销。
本文章更多内容:<<上一页 - 1 - 2 - 3 - 4 - 下一页>> |