# 機能設計書 91-GDI レンダリングエンジン

## 概要

本ドキュメントは、Windows Terminal / Console Hostにおける GDI (Graphics Device Interface) ベースのテキストレンダリングエンジンの機能設計を記述する。GdiEngineクラスの構造、描画パイプライン、フォント管理、およびダブルバッファリングの仕組みを解説する。

### 本機能の処理概要

GDI レンダリングエンジンは、従来のWindowsコンソール（Console Host / conhost.exe）向けのテキスト描画を担当するレンダリングバックエンドである。Windows GDI APIを使用して、ターミナルバッファの内容を画面に描画する。

**業務上の目的・背景**：Windows Terminalは高性能な Atlas レンダリングエンジン（DirectXベース）を主力とするが、Console Host（conhost.exe）では従来のGDI APIを使用したレンダリングが必要である。GDIレンダリングエンジンは、DirectXが利用できない環境やレガシーコンソールアプリケーションのホスティングにおいて不可欠な役割を果たす。セッション0（サービスセッション）などの非インタラクティブ環境でも安定して動作する必要がある。

**機能の利用シーン**：conhost.exeがレガシーモードで動作する場合、またはDirectXレンダリングが利用できない環境でのターミナル表示に使用される。GPU非搭載環境やリモートデスクトップセッションなど、ハードウェアアクセラレーションに制約がある場面で活用される。

**主要な処理内容**：
1. GDI デバイスコンテキスト（DC）を使用したテキスト描画（PolyTextOut）
2. ダブルバッファリングによるちらつき防止（メモリビットマップへの描画後に画面転送）
3. フォント管理（通常・太字・斜体・太字斜体の4種類 + ソフトフォント）
4. 選択範囲、カーソル、グリッド線（下線・取り消し線等）の描画
5. DPIスケーリング対応
6. 無効化領域（dirty region）の管理による部分更新の最適化
7. ダブル幅文字・ダブル高さ行の行変換処理

**関連システム・外部連携**：IRenderEngine インターフェースを通じてRendererクラスから呼び出される。TextBuffer、RenderSettings、IRenderDataとデータ連携する。

**権限による制御**：特になし。描画対象のHWNDが有効であることが前提条件となる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | TermControl（ターミナル表示） | 参照画面 | Console Hostモードでの文字描画 |

## 機能種別

レンダリング処理 / グラフィックス描画

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hwnd | HWND | Yes | 描画対象のウィンドウハンドル | INVALID_HANDLE_VALUE でないこと |
| clusters | std::span\<const Cluster\> | Yes | 描画するテキストのクラスタ配列 | 空でないこと |
| coord | til::point | Yes | 描画開始座標（文字座標） | ビューポート内であること |
| textAttributes | TextAttribute | Yes | テキストの属性（色、太字等） | - |
| FontInfoDesired | FontInfoDesired | Yes | フォント設定情報 | - |
| iDpi | int | Yes | DPI値 | 正の整数 |

### 入力データソース

- Renderer から IRenderEngine インターフェース経由で描画指示を受領
- TextBuffer からテキストデータと属性情報を取得
- RenderSettings から描画設定を取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 描画結果 | 画面出力 | GDI経由でウィンドウに描画されたテキスト・カーソル・選択範囲 |
| HRESULT | HRESULT | 各操作の成否 |

### 出力先

ウィンドウクライアント領域への画面描画（GDI経由）

## 処理フロー

### 処理シーケンス

```
1. StartPaint() - 描画開始
   └─ ウィンドウの有効性チェック、メモリビットマップ準備、デバイスコンテキスト取得
2. ScrollFrame() - スクロール処理
   └─ 無効化されたスクロール量に基づいてメモリサーフェスをBitBlt
3. PaintBackground() - 背景描画
   └─ 無効化領域の背景色を塗りつぶし
4. ResetLineTransform() / PrepareLineTransform() - 行変換
   └─ ダブル幅/ダブル高さ行のためのXFORM設定
5. PaintBufferLine() - テキスト行描画
   └─ PolyTextOutバッファにテキストを蓄積（最大80行キャッシュ）
6. PaintBufferGridLines() - グリッド線描画
   └─ 下線、取り消し線、波線等の装飾線を描画
7. PaintSelection() - 選択範囲描画
   └─ 選択領域の反転表示
8. PaintCursor() - カーソル描画
   └─ カーソル形状に応じた描画
9. EndPaint() - 描画終了
   └─ PolyTextOutのフラッシュ
10. Present() - 画面転送
    └─ メモリビットマップからウィンドウDCへBitBlt
```

### フローチャート

```mermaid
flowchart TD
    A[StartPaint] --> B{ウィンドウ有効?}
    B -->|No| C[S_FALSE 返却]
    B -->|Yes| D[メモリビットマップ準備]
    D --> E[DC取得]
    E --> F[ScrollFrame]
    F --> G[PaintBackground]
    G --> H[行ごとの描画ループ]
    H --> I[PrepareLineTransform]
    I --> J[PaintBufferLine]
    J --> K[PaintBufferGridLines]
    K --> L{次の行あり?}
    L -->|Yes| H
    L -->|No| M[PaintSelection]
    M --> N[PaintCursor]
    N --> O[EndPaint]
    O --> P[Present - BitBlt]
    P --> Q[DC解放]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-91-01 | ダブルバッファリング | メモリDCに描画してからウィンドウDCに転送する | 常時 |
| BR-91-02 | PolyTextOutキャッシュ | 最大80行分のテキストをバッチ描画する | PaintBufferLine呼び出し時 |
| BR-91-03 | フォント選択 | 通常/太字/斜体/太字斜体を属性に応じて切り替える | テキスト描画時 |
| BR-91-04 | DPIスケーリング | s_ScaleByDpiでピクセル値をDPIに応じて拡大する | フォント・線メトリクス計算時 |
| BR-91-05 | 不可視ウィンドウスキップ | ウィンドウが非表示かつタイトル未変更の場合は描画をスキップ | StartPaint時 |

### 計算ロジック

- フォントサイズ計算: `s_ScaleByDpi(iPx, iDpi) = MulDiv(iPx, iDpi, s_iBaseDpi)`
- 無効化領域の文字座標変換: `_invalidCharacters = til::rect{_psInvalidData.rcPaint}.scale_down(_GetFontSize())`
- 全角判定: TrueTypeフォントの場合はGetCharABCWidthsW、ラスターフォントの場合はGetCharWidth32Wで幅を計測

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（データベースへのアクセスは行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E_FAIL | DC取得失敗 | GetDCがNULLを返した場合 | RETURN_HR_IF_NULLで即座にリターン |
| S_FALSE | 描画不要 | ウィンドウが無効/最小化/非表示の場合 | 描画をスキップして正常復帰 |
| E_FAIL | メモリビットマップ作成失敗 | CreateCompatibleBitmap失敗 | HRESULT error返却 |

### リトライ仕様

リトライは行わない。次のフレームで再描画を試みる。

## トランザクション仕様

該当なし。描画操作はトランザクション管理対象外。

## パフォーマンス要件

- PolyTextOutのバッチサイズ: 最大80行分のキャッシュによりGDIコール回数を削減
- メモリプール（std::pmr::unsynchronized_pool_resource）によるPolyText用文字列・幅配列のアロケーション最適化
- ダブルバッファリングにより描画のちらつきを防止

## セキュリティ考慮事項

- セッション0（非インタラクティブセッション）での動作に対応: GdiEngine コンストラクタでGetTextFaceWを呼び出し、GDI テキストAPIの準備完了を待機する
- ウィンドウハンドルの有効性を各描画フレームで確認

## 備考

- GdiEngine は Console Host 専用であり、Windows Terminal（WinUI/XAML ベース）では Atlas レンダリングエンジンが使用される
- デバッグビルドでは `_fDebug` フラグにより別ウィンドウでの描画確認が可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

GDI レンダリングエンジンの基底クラスと、描画に使用される主要な型を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RenderEngineBase.hpp | `src/renderer/inc/RenderEngineBase.hpp` | IRenderEngineの共通実装基盤。タイトル変更管理等の共通動作 |
| 1-2 | IRenderEngine.hpp | `src/renderer/inc/IRenderEngine.hpp` | レンダリングエンジンのインターフェース定義。全メソッドのシグネチャ |

**読解のコツ**: RenderEngineBase は Template Method パターンを適用しており、_DoUpdateTitle のような protected virtual メソッドをサブクラスで実装する構造になっている。

#### Step 2: エントリーポイントを理解する

GdiEngine クラスのヘッダーファイルで全体構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gdirenderer.hpp | `src/renderer/gdi/gdirenderer.hpp` | GdiEngineクラスの全メンバー定義。FontType列挙体、LineMetrics構造体 |

**主要処理フロー**:
1. **22-26行目**: GdiEngine が RenderEngineBase を継承する final クラスであることを確認
2. **106-120行目**: FontType列挙体（Default/Bold/Italic/BoldItalic + Soft）
3. **135-148行目**: LineMetrics構造体（グリッド線・下線・取り消し線の各メトリクス）
4. **127-130行目**: PolyTextOutキャッシュ（s_cPolyTextCache = 80）

#### Step 3: 初期化と状態管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | state.cpp | `src/renderer/gdi/state.cpp` | コンストラクタ・デストラクタ、フォント更新、DPI更新 |

**主要処理フロー**:
- **21-67行目**: コンストラクタ。メモリDC作成、GM_ADVANCED設定、セッション0対応のGetTextFaceW呼び出し
- **75-80行目**: デストラクタ。PolyText配列のクリーンアップ

#### Step 4: 描画パイプラインを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | paint.cpp | `src/renderer/gdi/paint.cpp` | 描画の主処理。StartPaint、ScrollFrame、PaintBufferLine、PaintSelection、PaintCursor、EndPaint、Present |

**主要処理フロー**:
- **28-68行目**: StartPaint() - ウィンドウ有効性チェック、PolyTextカウンタリセット、メモリビットマップ準備、DC取得
- **78行目以降**: ScrollFrame() - 無効化スクロール量をBitBltで処理

#### Step 5: 無効化管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | invalidate.cpp | `src/renderer/gdi/invalidate.cpp` | 無効化領域の管理。InvalidateSystem、InvalidateScroll、InvalidateSelection |

**主要処理フロー**:
- **21-24行目**: InvalidateSystem() - システムからのWM_PAINT無効化領域を内部領域とマージ
- **32-42行目**: InvalidateScroll() - スクロールデルタをフォントサイズ倍してピクセル座標に変換
- **50-57行目**: InvalidateSelection() - 選択範囲の各矩形をInvalidate呼び出し

#### Step 6: 数学/ユーティリティ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | math.cpp | `src/renderer/gdi/math.cpp` | GetDirtyArea、IsGlyphWideByFont、DPIスケーリング関数 |

**主要処理フロー**:
- **19-26行目**: GetDirtyArea() - ピクセル座標の無効化領域をフォントサイズで割って文字座標に変換
- **36-60行目**: IsGlyphWideByFont() - TrueType/ラスターフォントでの全角判定

### プログラム呼び出し階層図

```
Renderer::PaintFrame()
    │
    ├─ GdiEngine::StartPaint()
    │      ├─ _IsWindowValid()
    │      ├─ _PrepareMemoryBitmap()
    │      └─ GetDC()
    │
    ├─ GdiEngine::ScrollFrame()
    │      └─ BitBlt() (メモリDC上)
    │
    ├─ GdiEngine::PaintBackground()
    │      └─ _PaintBackgroundColor()
    │
    ├─ [行ごとのループ]
    │   ├─ GdiEngine::PrepareLineTransform()
    │   │      └─ SetWorldTransform()
    │   ├─ GdiEngine::UpdateDrawingBrushes()
    │   │      ├─ SetTextColor()
    │   │      └─ SelectObject() (フォント選択)
    │   ├─ GdiEngine::PaintBufferLine()
    │   │      └─ _pPolyText[] にキャッシュ
    │   └─ GdiEngine::PaintBufferGridLines()
    │          ├─ LineTo() (直線)
    │          └─ PolyBezier() (波線)
    │
    ├─ GdiEngine::PaintSelection()
    │      └─ InvertRect()
    │
    ├─ GdiEngine::PaintCursor()
    │      └─ InvertRect() / FillRect()
    │
    ├─ GdiEngine::EndPaint()
    │      └─ _FlushBufferLines()
    │             └─ PolyTextOutW()
    │
    └─ GdiEngine::Present()
           ├─ BitBlt() (メモリDC → ウィンドウDC)
           └─ ReleaseDC()
```

### データフロー図

```
[入力]                    [処理]                          [出力]

TextBuffer          ───▶ Renderer                  ───▶ GdiEngine
(テキスト・属性)           (描画指示の発行)                 (GDI APIによる描画)
                                                           │
RenderSettings      ───▶ UpdateDrawingBrushes      ───▶ メモリDC
(色・フォント設定)         (ブラシ・フォント設定)            (ダブルバッファ)
                                                           │
                                                         BitBlt
                                                           │
                                                         ウィンドウDC
                                                         (画面表示)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gdirenderer.hpp | `src/renderer/gdi/gdirenderer.hpp` | ソース | GdiEngineクラス定義 |
| paint.cpp | `src/renderer/gdi/paint.cpp` | ソース | 描画処理（StartPaint/EndPaint/Present/PaintBufferLine等） |
| state.cpp | `src/renderer/gdi/state.cpp` | ソース | 状態管理（コンストラクタ/フォント更新/DPI更新） |
| invalidate.cpp | `src/renderer/gdi/invalidate.cpp` | ソース | 無効化領域管理 |
| math.cpp | `src/renderer/gdi/math.cpp` | ソース | 数学ユーティリティ（DirtyArea計算/グリフ幅判定） |
| RenderEngineBase.hpp | `src/renderer/inc/RenderEngineBase.hpp` | ソース | レンダリングエンジン基底クラス |
| IRenderEngine.hpp | `src/renderer/inc/IRenderEngine.hpp` | ソース | レンダリングエンジンインターフェース |
| FontResource.hpp | `src/renderer/inc/FontResource.hpp` | ソース | ソフトフォントリソース管理 |
| precomp.h | `src/renderer/gdi/precomp.h` | ソース | プリコンパイルヘッダー |
