图形界面编程,常见棘手问题就是出现绘图出现闪屏。这一现象也严重影响用户体验。前段时间刚接触minigui就出现一个闪屏半天不得其解的一个问题,查了很多网络资料使用方法都尽人意。
双缓冲绘图原理:
双缓冲
双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板。首先我们在内存环境中建立一个“虚拟“的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形“拷贝”到另一块黑板(屏幕)上。采取这种方法可以提高绘图速度,极大的改善绘图效果。下面是原理图:
图一 双缓冲原理示意图
minigui关于DC
内存 DC 和 BitBlt
新的 GDI 函数增强了内存 DC 操作函数。GDI 函数在建立内存 DC 时,将调用 GAL 的相应接口。如前所述,GAL 将尽量把内存 DC 建立在显示卡的显示内存当中。这样,可以充分利用显示卡的硬件加速功能,实现显示内存中两个不同区域之间位块的快速移动、复制等等,包括透明处理和 Alpha 混和。应用程序可以建立一个具有逐点 Alpha 特性的内存 DC(每个点具有不同的 Alpha 值),也可以通过 SetMemDCAlpha 设置内存 DC 所有象素的 Alpha 值(或者称为"Alpha 通道"),然后利用 BitBlt 和 StretchBlt 函数实现 DC 之间的位块传送。应用程序还可以通过 SetMemDCColorKey 函数设置源 DC 的透明色,从而在进行 BitBlt 时跳过这些透明色。
有关内存 DC 的 GDI 函数有(include/gdi.h): #define MEMDC_FLAG_NONE 0x00000000 /* None. */ #define MEMDC_FLAG_SWSURFACE 0x00000000 /* DC is in system memory */ #define MEMDC_FLAG_HWSURFACE 0x00000001 /* DC is in video memory */ #define MEMDC_FLAG_SRCCOLORKEY 0x00001000 /* Blit uses a source color key */ #define MEMDC_FLAG_SRCALPHA 0x00010000 /* Blit uses source alpha blending */ #define MEMDC_FLAG_RLEACCEL 0x00004000 /* Surface is RLE encoded */ HDC GUIAPI CreateCompatibleDC (HDC hdc); HDC GUIAPI CreateMemDC (int width, int height, int depth, DWORD flags, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); BOOL GUIAPI ConvertMemDC (HDC mem_dc, HDC ref_dc, DWORD flags); BOOL GUIAPI SetMemDCAlpha (HDC mem_dc, DWORD flags, Uint8 alpha); BOOL GUIAPI SetMemDCColorKey (HDC mem_dc, DWORD flags, Uint32 color_key); void GUIAPI DeleteMemDC (HDC mem_dc);
CreateCompatibleDC 函数创建一个和给定 DC 兼容的内存 DC。兼容的含义是指,新创建的内存 DC 的象素格式、宽度和高度与给定 DC 是相同的。利用这种方式建立的内存 DC 可以快速 Blit 到与之兼容的 DC 上。
这里需要对象素格式做进一步解释。象素格式包含了颜色深度(即每象素点的二进制位数)、调色板或者象素点中 RGBA(红、绿、蓝、Alpha)四个分量的组成方式。其中的 Alpha 分量,可以理解为一个象素点的透明度,0 表示完全透明,255 表示完全不透明。在 MiniGUI 中,如果颜色深度低于 8,则 GAL 会默认创建一个调色板,并且可以调用 SetPalette 函数修改调色板。如果颜色深度高于 8,则通过四个变量分别指定象素点中 RGBA 分量所占的位。如果是建立兼容 DC,则兼容内存 DC 和给定 DC 具有一样的颜色深度,同时具有一样的调色板或者一样的 RGBA 分量组成方式。
如果调用 CreateMemDC 函数,则可以指定新建内存 DC 的高度、宽度、颜色深度,以及必要的 RGBA 组成方式。在 MiniGUI 中,是通过各自在象素点中所占用的位掩码来表示 RGBA 四个分量的组成方式的。比如,如果要创建一个包含逐点 Alpha 信息的16 位内存 DC,则可以用每分量四个二进制位的方式分配 16 位的象素值,这样,RGBA 四个分量的掩码分别为:0x0000F000, 0x00000F00, 0x000000F0, 0x0000000F。
ConvertMemDC 函数用来将一个任意的内存 DC 对象,根据给定的参考 DC 的象素格式进行转换,使得结果 DC 具有和参考 DC 一样的象素格式。这样,转换后的 DC 就能够快速 Blit 到与之兼容的 DC 上。
SetMemDCAlpha 函数用来设定或者取消整个内存 DC 对象的 Alpha 通道值。我们还可以通过 MEMDC_FLAG_RLEACCEL 标志指定内存 DC 采用或者取消 RLE 编码方式。Alpha 通道值将作用在 DC 的所有象素点上。
SetMemDCColorKey 函数用来设定或者取消整个内存 DC 对象的 ColorKey,即透明象素值。我们还可以通过 MEMDC_FLAG_RLEACCEL 标志指定内存 DC 采用或者取消 RLE 编码方式。
刚开始尝试创建兼容DC来尝试解决此问题但是效果还是闪屏,最后采用单独创建DC 函数: CreateMemDC 得以解决
hdc = BeginPaint (hwnd); GetClientRect (hwnd, &rc); //hdcMem = CreateCompatibleDC (hdc ); //hdcMem = CreateCompatibleDCEx(hdc,RECTW(rc), RECTH(rc)); //hdcMem = CreateMemDC( RECTW(rc), RECTH(rc), 16,MEMDC_FLAG_HWSURFACE |MEMDC_FLAG_SRCALPHA , //0x0000F000, 0x00000F00, 0x000000F0, 0x000000F ); hdcMem = CreateMemDC (RECTW(rc), RECTH(rc), 32, MEMDC_FLAG_HWSURFACE | MEMDC_FLAG_SRCALPHA | MEMDC_FLAG_SRCCOLORKEY,0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000); // subMenuData = (subMenuData_t*)GetWindowAdditionalData(hwnd); //pWin->we_rdr->draw_header(hwnd,hdcMem,(const RECT*)&rc,0xFF808080); //SetWindowBkColor(hwnd, gp_normal_bgc); //SetBrushColor (hdcMem, 0xFF808080); // FillBox (hdcMem, 0, 0, RECTW(rc), RECTH(rc)); pData = (PLISTBOXDATA)pCtrl->dwAddData2; SelectFont(hdcMem, GetWindowFont(hwnd)); lstOnDrawSListBoxItems (hwnd, hdcMem, pData, RECTW (rc)); lstDrawFocusRect (hwnd, hdcMem, pData); BitBlt (hdcMem, 0, 0, RECTW(rc), RECTH(rc),hdc , 0, 0, 0); //DeleteCompatibleDC (hdcMem); EndPaint (hwnd, hdc); DeleteMemDC( hdcMem );