Chapter I: ExcludeClipRect在一个剪切区域排除一个矩形,导致绘制该剪切区域时,不绘制该矩形.
ExcludeClipRect函数详情请参考MSDN,这个函数用于排除一个区域的一部分,常用于绘制图画,例如,在一个窗口的客户区绘制一幅图片,如下面代码所示:
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- RECT rc = {0};
- GetClientRect (hWnd, &rc) ;
- HDC hMemDC = CreateCompatibleDC(ps.hdc);
- SelectObject(hMemDC, g_hBmpAllWstDskWallpaper);
-
-
- BitBlt(ps.hdc,0,0,300,200,hMemDC,0,0,SRCCOPY);
- DeleteDC(hMemDC);
- EndPaint(hWnd,&ps);
- }
这段代码在窗口客户区绘制一个位图,这没问题.但是注意一下A行,它是注释掉的,然后去掉这个注释,再次运行程序,可以观察到,这个位图虽然还是绘制在了窗口的客户区,但是矩形区域{0, 0, 200, 100}显示的仍然是窗口的背景色,换句话说,位图缺了这一块.这是因为ExcludeClipRect将{0, 0, 200, 100}矩形从窗口剪切区域排除掉了,这样GDI是不会绘制这个矩形区域的.
Chaper II: ExcludeClipRect函数不释放排除的矩形区域
将Chapter I代码中的A行改为:
- BOOL g_bCall = TRUE;
- if (g_bCall)
- {
- g_bCall = FALSE;
- ExcludeClipRect(ps.hdc,0,0,200,100);
- }
再次运行程序,并改变窗口大小,可以发现,客户区矩形区域{0, 0, 200, 100}始终不会被绘制.可见这个函数一旦被调用,那么这个函数排除的矩形区域将永远不会在该DC上绘制,那么有没有什么办法恢复该区域呢? 其实很简单,将那块被"Exclude"掉的矩形区域再"找回来"就行了.办法就是程序再创建一个剪切区域,使其大小和位置和之前"Exclude"掉的相同,然后调用ExtSelectClipRgn,注意最后一个参数要使用RGN_OR,这表示合并两个剪切区域.这样就相当于找回了这个"Exclude"掉的矩形区域.下面是代码:
- #define RECT_WIDTH(rt) (rt.right-rt.left)
- #define RECT_HEIGHT(rt) (rt.bottom-rt.top)
- RECT g_rcExclud = {0};
- BOOL g_bCancelExcludeRect = FALSE;
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- RECT rc = {0};
- GetClientRect (hWnd, &rc) ;
- HDC hMemDC = CreateCompatibleDC(ps.hdc);
- SelectObject(hMemDC, g_hBmpAllWstDskWallpaper);
-
-
- g_rcExclud.left = rc.right-200;
- g_rcExclud.top = rc.bottom-100;
- g_rcExclud.right = rc.right;
- g_rcExclud.bottom = rc.bottom;
-
- HRGN hrgn = NULL;
- if (g_bCancelExcludeRect && RECT_WIDTH(g_rcExclud) && RECT_HEIGHT(g_rcExclud))
- {
- hrgn = CreateRectRgn(g_rcExclud.left, g_rcExclud.top, g_rcExclud.right, g_rcExclud.bottom);
- ExtSelectClipRgn(ps.hdc, hrgn, RGN_OR);
- FillRect(ps.hdc,&g_rcExclud,(HBRUSH)GetStockObject(COLOR_WINDOW));
- }
-
- (!g_bCancelExcludeRect)
- ExcludeClipRect(ps.hdc,g_rcExclud.left, g_rcExclud.top, RECT_WIDTH(g_rcExclud), RECT_HEIGHT(g_rcExclud));
- BitBlt(ps.hdc,0,0,400,300,hMemDC,0,0,SRCCOPY);
-
- DeleteDC(hMemDC);
- if (hrgn != NULL) DeleteObject(hrgn);
- EndPaint(hWnd,&ps);
- }
Chapter III: 使用ExcludeClipRect实现无闪烁图像
有网友写过相关文章:http://dev.10086.cn/cmdn/wiki/index.php?edition-view-6349-1.html
这篇文章些的不错,不过还是有一个问题,就是Chapter II所提到的ExcludeClipRect不会释放"exclude"掉的区域,这样如果因为窗口大小的改变导致位图位置的变化,而"exclude"掉的区域是不会被背景擦除的.
而且这篇文章还有一个地方没有说清楚,那就是解决图像闪烁的办法其实是不用擦除窗口背景,而绘制窗口前景色,图像区域用位图绘制,其它区域用窗口背景色绘制,这相当于把一幅挖了一个洞(这个部分就是图像,其它部分使用背景色绘制)的画布贴到窗口,这样不产生绘制重叠部分,下面是修改后的代码:
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- RECT rc = {0};
- GetClientRect (hWnd, &rc) ;
-
- HDC hMemDC = CreateCompatibleDC(ps.hdc);
- SelectObject(hMemDC, g_hBmpAllWstDskWallpaper);
-
- HRGN hrgn = NULL;
- if (RECT_WIDTH(g_rcExclud) && RECT_HEIGHT(g_rcExclud))
- {
- hrgn = CreateRectRgn(g_rcExclud.left, g_rcExclud.top, g_rcExclud.right, g_rcExclud.bottom);
- ExtSelectClipRgn(ps.hdc, hrgn, RGN_OR);
- FillRect(ps.hdc,&g_rcExclud,(HBRUSH)GetStockObject(COLOR_WINDOW));
- }
-
- g_rcExclud.left = rc.right-300;
- g_rcExclud.top = rc.bottom-200;
- g_rcExclud.right = rc.right;
- g_rcExclud.bottom = rc.bottom;
-
- BitBlt(ps.hdc,g_rcExclud.left,g_rcExclud.top,g_rcExclud.right-g_rcExclud.left,g_rcExclud.bottom-g_rcExclud.top,hMemDC,0,0,SRCCOPY);
- ExcludeClipRect(ps.hdc,g_rcExclud.left,g_rcExclud.top, g_rcExclud.right, g_rcExclud.bottom);
- FillRect(ps.hdc,&rc,(HBRUSH)GetStockObject(COLOR_WINDOW));
-
- DeleteObject(hrgn);
- DeleteDC (hMemDC) ;
- EndPaint(hWnd, &ps);
- return 0;
- }
- break;
- case WM_SIZE:
- InvalidateRect(hWnd, NULL, FALSE);
- break;
最后,改变窗口的大小,观察一下这幅图片,始终在窗口右下角,而且图片的确不再闪烁.