首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

MFC 雙緩沖繪圖無輸出:GDI+ 初始化缺失與坐標陷阱

     編輯:程式設計綜合問答

在 MFC 的 `OnDraw` 中使用雙緩沖繪製圍棋盤(19×19 線條),代碼邏輯看似正確但窗口一片空白。這通常是由於 **GDI+ 未初始化** 或 **BitBlt 坐標計算錯誤** 導致。本文逐步拆解問題並給出修正代碼。

問題代碼回顧

關鍵片段:

CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
CBitmap GoBoardMap;
CRect rect;
GetClientRect(&rect);
GoBoardMap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
// ... 計算 gostartx, goblenth 等 ...
Graphics graphics(dcMemory); // 直接用 HDC 構造 Graphics
Pen* myPen = new Pen(Color::Black, 1);
// 繪製線條迴圈 ...
pDC->BitBlt(gostartx, gostarty, goblenth, goblenth, &dcMemory, 0, 0, SRCCOPY);
dcMemory.DeleteDC();

核心問題 1:GDI+ 必須顯式啟動/關閉

MFC 應用若使用 GDI+ (`Graphics`, `Pen` 等類),**必須**在應用程序啟動時調用 `GdiplusStartup`,退出時調用 `GdiplusShutdown`。否則 `Graphics` 構造失敗,繪圖無效。

在 `CWinApp` 派生類中添加:

// 頭文件
#include 
#pragma comment(lib, "gdiplus.lib")
ULONG_PTR m_gdiplusToken;

// CPP 文件 - InitInstance
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

// CPP 文件 - ExitInstance
Gdiplus::GdiplusShutdown(m_gdiplusToken);
CWinApp::ExitInstance();

核心問題 2:BitBlt 目標矩形與源矩形不匹配

代碼中 `BitBlt(gostartx, gostarty, goblenth, goblenth, ...)` 將內存 DC 的 `(0,0)` 區域複製到屏幕 DC 的 `(gostartx, gostarty)`。但繪圖時坐標已包含 `gostartx/gostarty` 偏移,導致內容繪在內存位圖的右下角區域,而複製時卻取左上角,結果為空。

修正策略:內存位圖繪製時坐標歸零,BitBlt 直接全客戶區複製

void CMyView::OnDraw(CDC* pDC) {
    CRect rect;
    GetClientRect(&rect);
    
    // 1. 創建兼容 DC 與位圖(覆蓋整個客戶區)
    CDC dcMem;
    dcMem.CreateCompatibleDC(pDC);
    CBitmap bmp;
    bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
    CBitmap* pOldBmp = dcMem.SelectObject(&bmp);
    
    // 2. 填充背景(可選)
    dcMem.FillSolidRect(&rect, RGB(255, 255, 255)); 
    
    // 3. GDI+ 繪圖:Graphics 綁定內存 DC,坐標從 (0,0) 開始
    Graphics graphics(dcMem.GetSafeHdc());
    Pen pen(Color::Black, 1);
    
    float boardLen = min(rect.Width(), rect.Height()) * 0.9f;
    float cellSize = boardLen / 18.0f;
    float startX = (rect.Width() - boardLen) / 2.0f;
    float startY = (rect.Height() - boardLen) / 2.0f;
    
    for (int i = 0; i <= 18; ++i) {
        // 橫線:坐標相對內存 DC 原點
        graphics.DrawLine(&pen, startX, startY + i * cellSize, startX + boardLen, startY + i * cellSize);
        // 豎線
        graphics.DrawLine(&pen, startX + i * cellSize, startY, startX + i * cellSize, startY + boardLen);
    }
    
    // 4. 一次性 BitBlt 整個客戶區
    pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
    
    // 5. 清理
    dcMem.SelectObject(pOldBmp);
    // dcMem.DeleteDC() 會由析構自動調用
}

總結

  1. **GDI+ 初始化是前提**,缺一不可。
  2. **雙緩沖繪圖坐標體系要統一**:內存 DC 視為一張完整畫布,繪圖坐標以內存 DC 原點為準;`BitBlt` 直接整區複製。
  3. 資源管理:使用 `SelectObject` 保存/恢復舊位圖,防止 GDI 物件洩漏。

應用以上修正,您的圍棋盤將完美顯示。

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