# Double Buffer Architecture Research v3
## Focus: DATA FLOW — 每一個 BitBlt 的來源與目的

---

## 完整 BitBlt 路線圖（v4 模式）

### Frame N（穩定狀態，非 resize）

```
Step  Source          → Dest            Size              Purpose
───── ─────────────── ─────────────────── ──────────────── ────────────────────
A     hdcScreen       → s_hdcPaintBuf    ps.rcPaint       PaintText capture
B     (TextOut)       → s_hdcPaintBuf    per-character    PaintText ASCII render
C     (StretchBlt)    → g_hPaintBuf      per-glyph       UDA glyph overlay
D     g_hPaintBuf     → hdcScreen        dirty rows       ScanAndDrawUda writeback
```

### 問題：Step A 的 capture 覆蓋了上一幀的 UDA

- Frame N-1: buffer 有 ASCII + UDA（步驟 B + C 的結果）
- Frame N Step A: 從螢幕 capture → buffer，覆蓋 ps.rcPaint 區域
- 但螢幕上的內容是 Frame N-1 Step D 寫的（= buffer 的內容）
- 所以 capture 是**自己抄自己**（screen 等於 buffer 的 writeback 結果）
- **結論：Step A 的 capture 在穩定狀態下是冗餘的**

### 為什麼 Step A 仍然必要

1. **首次啟動**：buffer 剛建立時沒內容，需從螢幕取得基底
2. **Resize**：buffer 重建後是空的
3. **其他視窗覆蓋**：Alt+Tab 回來時螢幕內容可能被破壞
4. **非 v4 模式**（hook 未啟用）：PaintText 直接寫螢幕，buffer 需從螢幕更新

### 優化機會

如果能確認「buffer 內容 = 螢幕內容」（穩定狀態），Step A 可以跳過。
判斷條件：
- buffer 大小未變（非 resize）
- 上一幀是 v4 模式（buffer 有完整內容）
- 沒有 WM_ACTIVATEAPP（沒切換視窗）

```c
static int s_bBufferValid = 0;  // 1 = buffer has valid content from last frame
if( !s_bBufferValid || size_changed )
{
   BitBlt( s_hdcPaintBuf ← hdcScreen );  // capture
   s_bBufferValid = 0;  // will be set to 1 after successful writeback
}
// ... TextOut to buffer ...
// ... ScanAndDrawUda to buffer ...
// ... BitBlt buffer → screen ...
s_bBufferValid = 1;  // buffer is now valid for next frame
```

### 風險

- WM_ACTIVATEAPP 時需要 reset s_bBufferValid
- WM_DISPLAYCHANGE 時需要重建所有 DC
- WM_ERASEBKGND 被我們攔截（return 1），但 Windows 可能仍清除了螢幕
