闪烁的原因与双缓冲
做客户端的时候经常碰到闪烁的问题。以前直接就用双缓冲技术给解决了,压根就没有去思考里面的原理。前几天看了个资料,里面讲的蛮透彻,整理下吧。
闪烁产生的原因是在极短的时间内,显示设备上的内容反差太大照成的。当绘图过程是 按 填充背景色->绘制图像 这种流程,并且绘制是直接作用在显示设备上时,就会产生闪烁现象。因为绘图过程是直接作用在显示设备上的,所以人眼会直接观察到 填充背景色 与 绘制图像 这2步。往往背景色与绘制图像的内容反差是很大的,从而闪烁就产生了。
双缓冲技术的原理就是将 填充背景色->绘制图像 这个过程作用在后台缓存中,人眼所能观察到的是绘制完地图像。绘制完地图像的反差一般是很小的,故而就不会感觉的闪烁了。
- bcb delph中一个 doublebuff的函数就能直接双缓冲了。
- vc mfc 中需要自己用 HBitmap 去建立内存dc,这个dc就后台缓存
- ddraw 中交换链的设置 就是为了建立后台缓存
- d3d 中创建d3d设备时就能指定后台缓存的数目(默认 1个后台缓存)。
附上 mfc的后台缓存类
class CMemDC : public CDC
{
private:
CBitmap m_bitmap; // Offscreen bitmap
CBitmap *m_oldBitmap; // bitmap originally found in CMemDC
CDC *m_pDC; // Saves CDC passed in constructor
CRect m_rect; // Rectangle of drawing area.
BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.
public:
CMemDC(CDC *pDC, const CRect *pRect=NULL) : CDC()
{
ASSERT(pDC != NULL);
// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC =!pDC->IsPrinting();
// Get the rectangle to draw
if(pRect == NULL)
{
pDC->GetClipBox(&m_rect);
}
else
{
m_rect =*pRect;
}
if(m_bMemDC)
{
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);
SetMapMode(pDC->GetMapMode());
SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());
pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
}
else
{
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}
// Fill background
FillSolidRect(m_rect, pDC->GetBkColor());
}
~CMemDC()
{
if(m_bMemDC)
{
// Copy the offscreen bitmap onto the screen.
m_pDC->BitBlt(m_rect.left,
m_rect.top,
m_rect.Width(),
m_rect.Height(),
this,
m_rect.left,
m_rect.top,
SRCCOPY);
//Swap back the original bitmap.
SelectObject(m_oldBitmap);
}
else
{
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}
// Allow usage as a pointer
CMemDC*operator->()
{
returnthis;
}
// Allow usage as a pointer
operator CMemDC*()
{
returnthis;
}
};
{
private:
CBitmap m_bitmap; // Offscreen bitmap
CBitmap *m_oldBitmap; // bitmap originally found in CMemDC
CDC *m_pDC; // Saves CDC passed in constructor
CRect m_rect; // Rectangle of drawing area.
BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.
public:
CMemDC(CDC *pDC, const CRect *pRect=NULL) : CDC()
{
ASSERT(pDC != NULL);
// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC =!pDC->IsPrinting();
// Get the rectangle to draw
if(pRect == NULL)
{
pDC->GetClipBox(&m_rect);
}
else
{
m_rect =*pRect;
}
if(m_bMemDC)
{
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);
SetMapMode(pDC->GetMapMode());
SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());
pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
}
else
{
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}
// Fill background
FillSolidRect(m_rect, pDC->GetBkColor());
}
~CMemDC()
{
if(m_bMemDC)
{
// Copy the offscreen bitmap onto the screen.
m_pDC->BitBlt(m_rect.left,
m_rect.top,
m_rect.Width(),
m_rect.Height(),
this,
m_rect.left,
m_rect.top,
SRCCOPY);
//Swap back the original bitmap.
SelectObject(m_oldBitmap);
}
else
{
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}
// Allow usage as a pointer
CMemDC*operator->()
{
returnthis;
}
// Allow usage as a pointer
operator CMemDC*()
{
returnthis;
}
};
============================================================
最近在做一个flash项目,发现闪烁的另外一个可能。
如果在控件的呈现过程中,是异步的,也会造成闪烁。
例子:
listbox的item 在normal 与 mousein状态时 如果item背景图是用同一个image控件,在不同的状态指定不同的图路径,则由于载图是个异步过程,载图时listbox的底色便显示出来,造成闪烁。
解决办法:呈现的切换不要使用异步。就上例来说
item背景图用2个image 在不同的状态指定某个image不可见(这个设置是个同步过程,所以不闪烁)。
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="100%" height="39" autoDrawBackground="true" currentState="normal">
<fx:Script>
<![CDATA[
import resource.SourceImages;
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
<s:State name="hovered"/>
</s:states>
<s:Image width="100%" height="100%"
source ="{SourceImages.listItemScheduleNormal}"
scaleMode="stretch" includeIn="normal"/>
<s:Image width="100%" height="100%"
source ="{SourceImages.listItemScheduleDown}"
scaleMode="stretch" includeIn="selected"/>
<s:Image width="100%" height="100%"
source ="{SourceImages.listItemScheduleLight}"
scaleMode="stretch" includeIn="hovered"/>
<s:Label left="20" color="0xff4040" fontFamily="宋体" fontSize="12"
text="{data.time}" verticalCenter="0"
color.hovered="#404040"
color.normal="#404040"
color.selected="#CCD0D6"/>
<s:Label color="0xffff0000" fontFamily="宋体" fontSize="12" horizontalCenter="0" text="vs"
verticalCenter="0"
color.hovered="#404040"
color.normal="#404040"
color.selected="#CCD0D6"/>
<s:Label right="10" color="0x404040" fontFamily="宋体" fontSize="18" text="{data.bout}"
textAlign="right" verticalCenter="0"
fontSize.hovered="12"
color.normal="#404040" fontSize.normal="12"
color.selected="#CCD0D6" fontSize.selected="12"/>
</s:ItemRenderer>