Chapter I: ExcludeClipRect在一個剪切區域排除一個矩形,導致繪制該剪切區域時,不繪制該矩形.
ExcludeClipRect函數詳情請參考MSDN,這個函數用於排除一個區域的一部分,常用於繪制圖畫,例如,在一個窗口的客戶區繪制一幅圖片,如下面代碼所示:
這段代碼在窗口客戶區繪制一個位圖,這沒問題.但是注意一下A行,它是注釋掉的,然後去掉這個注釋,再次運行程序,可以觀察到,這個位圖雖然還是繪制在了窗口的客戶區,但是矩形區域{0, 0, 200, 100}顯示的仍然是窗口的背景色,換句話說,位圖缺了這一塊.這是因為ExcludeClipRect將{0, 0, 200, 100}矩形從窗口剪切區域排除掉了,這樣GDI是不會繪制這個矩形區域的. Chaper II: ExcludeClipRect函數不釋放排除的矩形區域 將Chapter I代碼中的A行改為:
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- RECT rc = {0};
- GetClientRect (hWnd, &rc) ;
- HDC hMemDC = CreateCompatibleDC(ps.hdc);
- SelectObject(hMemDC, g_hBmpAllWstDskWallpaper);//選擇位圖
- //ExcludeClipRect(ps.hdc,0,0,200,100); //排除部分區域. A line
- BitBlt(ps.hdc,0,0,300,200,hMemDC,0,0,SRCCOPY);
- DeleteDC(hMemDC);
- EndPaint(hWnd,&ps);
- }
再次運行程序,並改變窗口大小,可以發現,客戶區矩形區域{0, 0, 200, 100}始終不會被繪制.可見這個函數一旦被調用,那麼這個函數排除的矩形區域將永遠不會在該DC上繪制,那麼有沒有什麼辦法恢復該區域呢? 其實很簡單,將那塊被"Exclude"掉的矩形區域再"找回來"就行了.辦法就是程序再創建一個剪切區域,使其大小和位置和之前"Exclude"掉的相同,然後調用ExtSelectClipRgn,注意最後一個參數要使用RGN_OR,這表示合並兩個剪切區域.這樣就相當於找回了這個"Exclude"掉的矩形區域.下面是代碼:
- BOOL g_bCall = TRUE;//全局變量.
- if (g_bCall)
- {
- g_bCall = FALSE;
- ExcludeClipRect(ps.hdc,0,0,200,100); //排除部分區域,僅調用一次. A line
- }
Chapter III: 使用ExcludeClipRect實現無閃爍圖像 有網友寫過相關文章:http://dev.10086.cn/cmdn/wiki/index.php?edition-view-6349-1.html 這篇文章些的不錯,不過還是有一個問題,就是Chapter II所提到的ExcludeClipRect不會釋放"exclude"掉的區域,這樣如果因為窗口大小的改變導致位圖位置的變化,而"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);//選擇位圖
- // "exclud"掉的區域在右下角.
- 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; // 將上次"exclud"掉的區域填補回來.
- 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) //"exclud"掉窗口右下角區域.
- 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);
- }
最後,改變窗口的大小,觀察一下這幅圖片,始終在窗口右下角,而且圖片的確不再閃爍.
- 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);//恢復上次被"excude"掉的區域,必須的,否則這一部分不會被繪制.
- 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);//最後一個參數為FALSE,表示不用擦除背景.
- break;
本文出自 “零一小築” 博客,請務必保留此出處http://jetyi.blog.51cto.com/1460128/642401