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

C++Builder 雙緩衝繪圖技術:消除閃爍、大幅提升繪圖效能

     編輯:關於C++

問題背景

在 Windows GDI 繪圖中,直接在視窗的 Canvas 上繪製大量圖形(如數千個資料點的曲線圖)會造成:

  • 閃爍:使用者看見背景清除 → 逐步繪製的過程。
  • 效能低落:每個 GDI 呼叫皆涉及使用者/核心模式切換、視窗裁剪區計算。
  • 重繪不完整:WM_PAINT 處理過程中若被中斷,畫面殘缺。

雙緩衝 將所有繪圖操作導向記憶體位圖,完成後一次 BitBlt 複製到螢幕,完美解決上述問題。

實作步驟 (C++Builder VCL)

1. 建立記憶體位圖緩衝區

Graphics::TBitmap* BufferBmp = new Graphics::TBitmap();
try {
    // 設定尺寸與畫布屬性
    BufferBmp->Width  = ClientWidth;   // 或指定繪圖區寬高
    BufferBmp->Height = ClientHeight;
    BufferBmp->PixelFormat = pf24bit;  // 依需求選擇 pf32bit/pf16bit

    // 2. 在緩衝區繪圖 (完整繪製邏輯)
    BufferBmp->Canvas->Brush->Color = clBtnFace;
    BufferBmp->Canvas->FillRect(Rect(0, 0, ClientWidth, ClientHeight));

    // 繪製座標軸、格線、曲線、標註...
    BufferBmp->Canvas->MoveTo(x0, y0);
    BufferBmp->Canvas->LineTo(x1, y1);
    // ... 大量繪圖指令 ...

    // 3. 一次性複製到螢幕
    BitBlt(Canvas->Handle,
           0, 0, ClientWidth, ClientHeight,
           BufferBmp->Canvas->Handle,
           0, 0, SRCCOPY);
}
__finally {
    delete BufferBmp; // 4. 釋放記憶體
}

進階優化技巧

永久緩衝區 (避免頻繁配置/釋放記憶體)

將 BufferBmp 宣告為表單成員變數,在 FormCreate 建立,FormDestroy 釋放,FormResize 時調整尺寸:

// .h
private:
    Graphics::TBitmap* FBufferBmp;

// .cpp
void __fastcall TForm1::FormCreate(TObject* Sender)
{
    FBufferBmp = new Graphics::TBitmap();
    FBufferBmp->PixelFormat = pf32bit;
}

void __fastcall TForm1::FormDestroy(TObject* Sender)
{
    delete FBufferBmp;
}

void __fastcall TForm1::FormResize(TObject* Sender)
{
    if (FBufferBmp) {
        FBufferBmp->Width  = ClientWidth;
        FBufferBmp->Height = ClientHeight;
    }
    Invalidate(); // 觸發重繪
}

void __fastcall TForm1::Paint()
{
    // 直接使用 FBufferBmp 繪圖並 BitBlt
}

使用 TPaintBox + OnPaint 替代表單直接繪圖

TPaintBox 專為自訂繪圖設計,無視窗背景擦除開銷,配合雙緩衝效能最佳。

GDI+ 替代方案 (抗鋸齒、Alpha 混合)


#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

// 初始化 (FormCreate)
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

// 繪圖
Gdiplus::Graphics g(BufferBmp->Canvas->Handle);
g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen pen(Gdiplus::Color(255, 0, 0, 255), 2.0f);
g.DrawLine(&pen, x0, y0, x1, y1);

// 關閉 (FormDestroy)
Gdiplus::GdiplusShutdown(gdiplusToken);

僅重繪變更區域 (Dirty Rect)

若只有部分資料更新,計算最小包含矩形 TRect DirtyRect,BitBlt 時僅複製該區域:


BitBlt(Canvas->Handle,
       DirtyRect.Left, DirtyRect.Top,
       DirtyRect.Width(), DirtyRect.Height(),
       BufferBmp->Canvas->Handle,
       DirtyRect.Left, DirtyRect.Top,
       SRCCOPY);

常見錯誤與除錯

現象原因解決
畫面全白/全黑BitBlt 來源/目的座標錯誤、緩衝區尺寸為 0檢查 Width/Height > 0,座標對應正確
仍有閃爍未攔截 WM_ERASEBKGND、或 Invalidate(true) 擦除背景表單/控制項設定 DoubleBuffered = true (VCL 內建),或自行處理 WM_ERASEBKGND 回傳 TRUE
記憶體洩漏例外發生時未釋放 BufferBmp使用 try/__finally 或智慧指標 std::unique_ptr
縮放模糊BitBlt 不支援縮放改用 StretchBlt、SetStretchBltMode(HALFTONE) 或 GDI+

VCL 內建雙緩衝支援

自 BCB 6 / Delphi 6 起,TWinControl 提供 DoubleBuffered 屬性:


// 表單或自訂控制項建構子
__fastcall TMyControl::TMyControl(TComponent* Owner)
    : TCustomControl(Owner)
{
    DoubleBuffered = true; // 自動處理 WM_ERASEBKGND + 記憶體位圖雙緩衝
}

若僅需一般業務繪圖,開啟此屬性即可,無需手寫 BitBlt。高效能/特殊需求 (遊戲、即時波形) 仍建議手動控制。

總結

  • 雙緩衝 = 記憶體位圖繪圖 + BitBlt 一次複製。
  • 核心優點:零閃爍、高效能、畫面原子性更新。
  • 生產環境建議:永久緩衝區成員變數 + DoubleBuffered 屬性/手動 BitBlt + Dirty Rect 最小化重繪。
  • 需抗鋸齒/透明度/向量圖形 → 引入 GDI+ 或 Direct2D (Win7+)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved