在 Windows GDI 繪圖中,直接在 WM_PAINT 回應裡對螢幕 DC 繪圖,會因頻繁重繪 (最小化/最大化/遮蓋/移動) 造成嚴重閃爍。雙重緩衝技術將繪圖操作導向記憶體位圖 (TBitmap),完成後一次 BitBlt 複製到螢幕,可徹底消除閃爍並提升效能。
MemoryDC := TCanvas.Create; MemoryDC.Handle := CreateCompatibleDC(0); 或直接使用 TCanvas 封裝。MemoryBitmap := TBitmap.Create; MemoryBitmap.Width := ClientWidth; MemoryBitmap.Height := ClientHeight;OldBitmap := SelectObject(MemoryDC.Handle, MemoryBitmap.Handle);MemoryDC 上執行所有繪圖指令 (Line、TextOut、FillRect、StretchDraw 等)。BitBlt(ScreenDC.Handle, 0, 0, Width, Height, MemoryDC.Handle, 0, 0, SRCCOPY);SelectObject(MemoryDC.Handle, OldBitmap); DeleteDC(MemoryDC.Handle);type
TDoubleBufferedCanvas = class
private
FBitmap: TBitmap;
FMemoryDC: HDC;
FOldBitmap: HBITMAP;
FCanvas: TCanvas;
public
constructor Create(Width, Height: Integer);
destructor Destroy; override;
procedure BeginDraw(TargetDC: HDC; const Rect: TRect);
procedure EndDraw(TargetDC: HDC);
property Canvas: TCanvas read FCanvas;
end;
constructor TDoubleBufferedCanvas.Create(Width, Height: Integer);
begin
FBitmap := TBitmap.Create;
FBitmap.Width := Width;
FBitmap.Height := Height;
FBitmap.PixelFormat := pf32bit; // 建議 32-bit 避免調色盤問題
FMemoryDC := CreateCompatibleDC(0);
FOldBitmap := SelectObject(FMemoryDC, FBitmap.Handle);
FCanvas := TCanvas.Create;
FCanvas.Handle := FMemoryDC;
end;
destructor TDoubleBufferedCanvas.Destroy;
begin
SelectObject(FMemoryDC, FOldBitmap);
DeleteDC(FMemoryDC);
FCanvas.Free;
FBitmap.Free;
inherited;
end;
procedure TDoubleBufferedCanvas.BeginDraw(TargetDC: HDC; const Rect: TRect);
begin
// 可選:清除背景
FCanvas.Brush.Color := clWhite;
FCanvas.FillRect(Rect);
end;
procedure TDoubleBufferedCanvas.EndDraw(TargetDC: HDC);
begin
BitBlt(TargetDC, 0, 0, FBitmap.Width, FBitmap.Height, FMemoryDC, 0, 0, SRCCOPY);
end;
var
DoubleBuffer: TDoubleBufferedCanvas;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffer := TDoubleBufferedCanvas.Create(ClientWidth, ClientHeight);
DoubleBuffered := True; // 開啟 VCL 內建雙重緩衝 (WinXP 以上)
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DoubleBuffer.Free;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
DoubleBuffer.BeginDraw(Canvas.Handle, ClientRect);
// 所有繪圖指令寫在 DoubleBuffer.Canvas 上
DoubleBuffer.Canvas.Pen.Color := clRed;
DoubleBuffer.Canvas.Ellipse(10, 10, 100, 100);
DoubleBuffer.Canvas.TextOut(20, 20, 'Hello Double Buffer!');
DoubleBuffer.EndDraw(Canvas.Handle);
end;
procedure TForm1.FormResize(Sender: TObject);
begin
DoubleBuffer.Free;
DoubleBuffer := TDoubleBufferedCanvas.Create(ClientWidth, ClientHeight);
end;
DoubleBuffered: Boolean 屬性,設為 True 即自動啟用記憶體位圖緩衝,無需手寫類別 (WinXP 以上有效)。OnResize 重建位圖尺寸,避免邊界殘影。AlphaBlend 或 GDI+,普通 BitBlt 不支援 Alpha。OnPaint 只遍歷清單繪製,邏輯與繪圖分離。SelectObject(OldBitmap) 再 DeleteDC,否則 GDI 物件洩漏。雙重緩衝是解決 GDI 閃爍的標準方案。Delphi 透過 TBitmap + CreateCompatibleDC 或內建 DoubleBuffered 屬性,讓開發者以極低成本獲得流暢繪圖體驗。掌握記憶體 DC 生命週期管理,即可在遊戲迴圈、即時圖表、自訂控制項等高頻重繪場景中遊刃有餘。