# 機能設計書 68-Kittyグラフィックス

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおける Kitty グラフィックスプロトコル実装の設計を記述する。

### 本機能の処理概要

Kitty グラフィックスプロトコルは、ターミナル内で画像を表示するための標準化されたプロトコルである。Ghostty は、画像の送信（transmit）、表示（display）、削除（delete）、クエリ（query）などの主要コマンドをサポートし、ターミナル上での画像表示を可能にする。

**業務上の目的・背景**：現代のターミナルアプリケーションでは、テキストだけでなく画像やグラフィックスを表示する需要が増加している。Kitty グラフィックスプロトコルは、Kitty ターミナルエミュレータが開発した標準プロトコルで、多くのアプリケーション（matplotlib、fzf、yazi など）がサポートしている。

**機能の利用シーン**：
- ターミナル上での画像プレビュー
- ファイルマネージャでのサムネイル表示
- グラフやチャートの表示
- sixel の代替として高速な画像表示

**主要な処理内容**：
1. APC（Application Program Command）エスケープシーケンスの解析
2. Base64 エンコードされた画像データのデコード
3. PNG/RGB/RGBA 形式の画像処理
4. 画像のストレージ管理と配置追跡
5. レンダラーへの画像描画指示

**関連システム・外部連携**：
- wuffs: PNG デコード
- zlib: 画像データの圧縮展開
- OpenGL/Metal: GPU での画像レンダリング

**権限による制御**：`image-storage-limit` 設定で画像ストレージの上限を制御可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインターミナルウィンドウ | 主機能 | 画像の表示、配置、削除 |

## 機能種別

画像表示 / プロトコル処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| a | u8 | No | アクション種別（t=transmit, T=transmit_and_display, p=display, d=delete, q=query, f=frame, a=animation, c=compose） | 有効なアクション文字 |
| f | u32 | No | フォーマット（24=RGB, 32=RGBA, 100=PNG） | 24, 32, 100 のいずれか |
| t | u8 | No | 転送媒体（d=direct, f=file, t=temporary_file, s=shared_memory） | 有効な媒体文字 |
| s | u32 | No | 画像幅（ピクセル） | 0-10000 |
| v | u32 | No | 画像高さ（ピクセル） | 0-10000 |
| i | u32 | No | 画像ID | - |
| I | u32 | No | 画像番号 | - |
| p | u32 | No | 配置ID | - |
| o | u8 | No | 圧縮（z=zlib） | z のみ |
| m | u32 | No | チャンク継続フラグ | 0 or 1 |
| q | u32 | No | quiet モード | 0, 1, 2 |
| c | u32 | No | 表示列数 | - |
| r | u32 | No | 表示行数 | - |
| x | u32 | No | ソース X オフセット | - |
| y | u32 | No | ソース Y オフセット | - |
| w | u32 | No | ソース幅 | - |
| h | u32 | No | ソース高さ | - |
| X | u32 | No | 配置 X オフセット | - |
| Y | u32 | No | 配置 Y オフセット | - |
| z | i32 | No | Z インデックス（レイヤー順序） | - |
| C | u32 | No | カーソル移動（0=after, 1=none） | 0 or 1 |
| U | u32 | No | 仮想配置（0=false, 1=true） | 0 or 1 |

### 入力データソース

- APC シーケンス: `ESC _ G <parameters> ; <base64_data> ESC \`
- ファイル: `t=f` または `t=t` で指定されたファイルパス
- 共有メモリ: `t=s` で指定されたパス（POSIX shm_open）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Response | struct | コマンド応答（i=画像ID, I=画像番号, p=配置ID, message） |

### 出力先

- PTY: レスポンスエスケープシーケンス `ESC _ G i=<id>; <message> ESC \`
- レンダラー: 画像描画指示

## 処理フロー

### 処理シーケンス

```
1. APC シーケンス受信
   └─ ESC _ G で開始検出
   └─ graphics_command.Parser でパース

2. コマンド解析
   └─ key=value ペア抽出
   └─ Base64 ペイロードデコード
   └─ Command 構造体生成

3. コマンド実行
   └─ query: 画像読み込みテスト
   └─ transmit: 画像データ受信・保存
   └─ display: 画像配置作成
   └─ delete: 画像/配置削除

4. チャンク処理（m=1 の場合）
   └─ LoadingImage に追加
   └─ 最終チャンク（m=0）で complete

5. 画像保存
   └─ ImageStorage に追加
   └─ total_bytes 更新
   └─ 制限超過時は古い画像を evict

6. レスポンス生成
   └─ quiet 設定に基づく応答
```

### フローチャート

```mermaid
flowchart TD
    A[APC ESC_G 受信] --> B[Parser.feed でバイト処理]
    B --> C{制御/データ?}
    C -->|制御| D[key=value 抽出]
    C -->|データ| E[Base64 蓄積]
    D --> F[KV HashMap に格納]
    F --> G{; セパレータ?}
    G -->|Yes| E
    G -->|No| B
    E --> H[Parser.complete]
    H --> I[Command 構造体生成]
    I --> J{action?}
    J -->|query| K[LoadingImage.init テスト]
    J -->|transmit| L[loadAndAddImage]
    J -->|display| M[addPlacement]
    J -->|delete| N[ImageStorage.delete]

    L --> O{more_chunks?}
    O -->|Yes| P[storage.loading に保存]
    O -->|No| Q[complete & addImage]

    Q --> R[ImageStorage 更新]
    R --> S{total_bytes > limit?}
    S -->|Yes| T[evictImage]
    S -->|No| U[Response 生成]
    T --> U

    K --> U
    M --> U
    N --> U

    U --> V{quiet 設定?}
    V -->|no| W[レスポンス送信]
    V -->|ok| X{エラー?}
    V -->|failures| Y[応答なし]
    X -->|Yes| W
    X -->|No| Y
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-68-01 | 画像サイズ制限 | 最大 10000x10000 ピクセル | 常時 |
| BR-68-02 | ストレージ制限 | 最大 320MB（デフォルト） | 常時 |
| BR-68-03 | 自動ID割当 | ID 未指定時は 2147483647 から開始 | ID=0 の場合 |
| BR-68-04 | 一時ファイル制限 | tty-graphics-protocol を含むパス名のみ | medium=temporary_file |
| BR-68-05 | チャンク制限 | direct medium のみチャンク対応 | medium=direct |
| BR-68-06 | 安全パス制限 | /proc, /sys, /dev（/dev/shm除く）は禁止 | ファイル読み込み時 |

### 計算ロジック

- **画像データサイズ**: `width * height * bpp` （bpp は format に依存: RGB=3, RGBA=4）
- **グリッドサイズ**: `(pixel_size + offset) / cell_size` の ceiling
- **アスペクト比維持**: columns または rows のみ指定時は比率を維持して計算

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

該当なし（本機能はデータベースを使用しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EINVAL | InvalidData | Base64 デコード失敗 | エラーレスポンス返却 |
| EINVAL | DimensionsRequired | 幅/高さが未指定 | エラーレスポンス返却 |
| EINVAL | DimensionsTooLarge | 10000 を超える次元 | エラーレスポンス返却 |
| EINVAL | DecompressionFailed | zlib 展開失敗 | エラーレスポンス返却 |
| EINVAL | TemporaryFileNotInTempDir | 一時ファイルが /tmp 外 | エラーレスポンス返却 |
| EINVAL | TemporaryFileNotNamedCorrectly | パス名が不正 | エラーレスポンス返却 |
| ENOMEM | OutOfMemory | メモリ確保失敗 | エラーレスポンス返却 |
| ENOENT | ImageNotFound | 存在しない画像ID指定 | エラーレスポンス返却 |

### リトライ仕様

該当なし

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

- ImageStorage の操作はスレッドセーフではない（single-threaded）
- レンダラーとの同期は dirty フラグで管理

## パフォーマンス要件

- Base64 デコードは SIMD 最適化されたライブラリを使用
- PNG デコードは wuffs ライブラリを使用（高速・メモリセーフ）
- チャンク転送で大きな画像を分割受信可能
- 画像 eviction は最も古い未使用画像を優先

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

- ファイル読み込み時のパス検証（/proc, /sys, /dev 禁止）
- 一時ファイルの名前パターン検証
- 最大データサイズ制限（400MB）
- 共有メモリの自動 unlink

## 備考

- アニメーションフレーム機能は未実装
- 共有メモリ転送は Windows 未対応
- Unicode プレースホルダ（仮想配置）はサポート

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | graphics.zig | `src/terminal/kitty/graphics.zig` | モジュール全体のエントリーポイント（1-37行目） |
| 1-2 | graphics_command.zig | `src/terminal/kitty/graphics_command.zig` | Command, Parser, Response 構造体（316-382行目） |
| 1-3 | graphics_image.zig | `src/terminal/kitty/graphics_image.zig` | Image, LoadingImage 構造体（31-506行目） |

**読解のコツ**: `Command` は `Control` union で各アクション（query, transmit, display, delete 等）を表現。`Parser` は state machine でキー値ペアを解析。

#### Step 2: コマンドパーサーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | graphics_command.zig | `src/terminal/kitty/graphics_command.zig` | Parser.feed 関数（97-141行目） |
| 2-2 | graphics_command.zig | `src/terminal/kitty/graphics_command.zig` | Parser.complete 関数（148-199行目） |

**主要処理フロー**:
1. **97-141行目**: `feed` でバイトを1つずつ処理、state machine で k=v パースとデータ蓄積
2. **148-199行目**: `complete` で action 判定、各パラメータ構造体を生成
3. **204-229行目**: Base64 デコード（SIMD 最適化）

#### Step 3: 画像読み込みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | graphics_image.zig | `src/terminal/kitty/graphics_image.zig` | LoadingImage.init（50-105行目） |
| 3-2 | graphics_image.zig | `src/terminal/kitty/graphics_image.zig` | LoadingImage.complete（331-368行目） |

**主要処理フロー**:
- **50-105行目**: 転送媒体に応じてデータを読み込み（direct, file, temp_file, shared_memory）
- **202-277行目**: ファイル読み込み、パス検証
- **331-368行目**: 圧縮展開、PNG デコード、画像検証

#### Step 4: ストレージ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | graphics_storage.zig | `src/terminal/kitty/graphics_storage.zig` | ImageStorage 構造体（22-73行目） |
| 4-2 | graphics_storage.zig | `src/terminal/kitty/graphics_storage.zig` | addImage, addPlacement（108-185行目） |
| 4-3 | graphics_storage.zig | `src/terminal/kitty/graphics_storage.zig` | delete 関数（217-428行目） |

**主要処理フロー**:
- **22-58行目**: ImageStorage はメインスクリーンと代替スクリーンそれぞれに存在
- **108-144行目**: `addImage` でストレージ制限チェック、eviction 実行
- **517-609行目**: `evictImage` で古い未使用画像を優先削除

#### Step 5: コマンド実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | graphics_exec.zig | `src/terminal/kitty/graphics_exec.zig` | execute 関数（23-91行目） |
| 5-2 | graphics_exec.zig | `src/terminal/kitty/graphics_exec.zig` | transmit, display 関数（128-280行目） |

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

```
APC シーケンス受信
    │
    └─ apc.Handler.end
           │
           └─ Terminal.kittyGraphics
                  │
                  └─ graphics_exec.execute
                         │
                         ├─ query
                         │     └─ LoadingImage.init (テスト)
                         │
                         ├─ transmit / transmit_and_display
                         │     └─ loadAndAddImage
                         │            │
                         │            ├─ LoadingImage.init
                         │            │     ├─ readFile (file/temp_file)
                         │            │     └─ readSharedMemory (shm)
                         │            │
                         │            ├─ storage.loading (チャンク)
                         │            │
                         │            └─ LoadingImage.complete
                         │                  ├─ decompress (zlib)
                         │                  ├─ decodePng (wuffs)
                         │                  └─ ImageStorage.addImage
                         │
                         ├─ display
                         │     └─ ImageStorage.addPlacement
                         │            └─ pages.trackPin
                         │
                         └─ delete
                               └─ ImageStorage.delete
                                      ├─ deleteById
                                      ├─ deleteIntersecting
                                      └─ deleteIfUnused
```

### データフロー図

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

APC ESC_G ──────────────▶ Parser.init
                               │
                               ▼
パラメータバイト ────────▶ Parser.feed ────▶ KV HashMap
                               │
                               ▼
Base64 ペイロード ───────▶ data ArrayList
                               │
                               ▼
                        Parser.complete ────▶ Command
                               │
                               ▼
                        execute ────▶ Response
                               │
                               ├── LoadingImage.init
                               │        │
                               │        ▼
                               │   decompress/decodePng
                               │        │
                               │        ▼
                               └── ImageStorage
                                        │
                                        ├── images HashMap
                                        │
                                        ├── placements HashMap
                                        │
                                        └── dirty flag ────▶ レンダラー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| graphics.zig | `src/terminal/kitty/graphics.zig` | ソース | モジュールエントリーポイント |
| graphics_command.zig | `src/terminal/kitty/graphics_command.zig` | ソース | コマンドパーサー |
| graphics_image.zig | `src/terminal/kitty/graphics_image.zig` | ソース | 画像読み込み |
| graphics_storage.zig | `src/terminal/kitty/graphics_storage.zig` | ソース | 画像ストレージ |
| graphics_exec.zig | `src/terminal/kitty/graphics_exec.zig` | ソース | コマンド実行 |
| graphics_render.zig | `src/terminal/kitty/graphics_render.zig` | ソース | 描画配置情報 |
| graphics_unicode.zig | `src/terminal/kitty/graphics_unicode.zig` | ソース | Unicode プレースホルダ |
| Terminal.zig | `src/terminal/Terminal.zig` | ソース | kittyGraphics 呼び出し |
| stream_handler.zig | `src/termio/stream_handler.zig` | ソース | APC ハンドラ統合 |
