首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

ExcludeClipRect 實現 GDI 無閃爍繪圖:原理、陷阱與完整解法

     編輯:關於C語言

核心概念

ExcludeClipRect 可從裁剪區域中排除一個矩形,導致該區域不被繪製。常用於「在背景上繪製前景圖片,避免重疊閃爍」。

Chapter I:基本用法

case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    RECT rc; GetClientRect(hWnd, &rc);
    HDC hMemDC = CreateCompatibleDC(ps.hdc);
    SelectObject(hMemDC, g_hBmp);
    // ExcludeClipRect(ps.hdc, 0, 0, 200, 100); // 排除左上角 200x100
    BitBlt(ps.hdc, 0, 0, 300, 200, hMemDC, 0, 0, SRCCOPY);
    DeleteDC(hMemDC);
    EndPaint(hWnd, &ps);
    break;

啟用註解行後,左上角 200x100 區域將保持背景色,不被位圖覆蓋。

Chapter II:排除區域不會自動釋放

ExcludeClipRect 一旦調用,該排除區域在該 DC 上永久生效,即使視窗大小改變也不會恢復。必須手動「找回」該區域:

// 記錄上次排除區域
RECT g_rcExclud = {0};
BOOL g_bCancelExcludeRect = FALSE;

case WM_PAINT:
    // ... 建立 DC、選擇位圖 ...

    // 1. 先恢復上次被排除的區域
    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));
    }

    // 2. 計算新位圖位置(右下角)
    g_rcExclud.left   = rc.right - 300;
    g_rcExclud.top    = rc.bottom - 200;
    g_rcExclud.right  = rc.right;
    g_rcExclud.bottom = rc.bottom;

    // 3. 繪製位圖
    BitBlt(ps.hdc, g_rcExclud.left, g_rcExclud.top,
           RECT_WIDTH(g_rcExclud), RECT_HEIGHT(g_rcExclud),
           hMemDC, 0, 0, SRCCOPY);

    // 4. 排除剛繪製的圖片區域
    ExcludeClipRect(ps.hdc, g_rcExclud.left, g_rcExclud.top,
                    RECT_WIDTH(g_rcExclud), RECT_HEIGHT(g_rcExclud));

    // 5. 用背景色填滿其餘區域
    FillRect(ps.hdc, &rc, (HBRUSH)GetStockObject(COLOR_WINDOW));

    if (hrgn) DeleteObject(hrgn);
    DeleteDC(hMemDC);
    EndPaint(hWnd, &ps);
    break;

Chapter III:完整無閃爍繪圖方案

關鍵原則:不擦除背景(WM_ERASEBKGND 返回 TRUE),在 WM_PAINT 中先畫前景圖片,排除該區域,再用背景色填滿其餘區域。配合 InvalidateRect(hWnd, NULL, FALSE)(最後參數 FALSE 表示不擦除背景)。

case WM_SIZE:
    InvalidateRect(hWnd, NULL, FALSE); // 關鍵:不擦除背景
    break;

實戰效果

  • 圖片固定停駐於視窗右下角。
  • 拖曳調整視窗大小時,圖片完全無閃爍。
  • 解決了網路上常見方案「排除區域隨視窗縮放殘留」的缺陷。

本文整理自「零一小築」博客,原文出處:https://jetyi.blog.51cto.com/1460128/642401 | 參考:相關討論

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved