# 帳票設計書 11-画像JPG/JPEG

## 概要

本ドキュメントは、Godot EngineにおけるJPEG形式画像ファイルの出力機能に関する設計書である。ImageクラスのデータをJPEG形式でファイルシステムに保存する処理について詳細に記述する。

### 本帳票の処理概要

本機能は、Godot Engineの Image クラスが保持する画像データを JPEG 形式（.jpg / .jpeg）でファイルまたはメモリバッファに出力する。libjpeg-turbo ライブラリを使用して高速なJPEGエンコーディングを実現している。

**業務上の目的・背景**：ゲーム開発において、スクリーンショット機能、テクスチャのエクスポート、ユーザー生成コンテンツの保存など、様々な場面で画像をJPEG形式で保存する必要がある。JPEGは広くサポートされた不可逆圧縮形式であり、ファイルサイズと画質のバランスを調整できるため、Web公開やSNS共有などの用途に適している。

**帳票の利用シーン**：スクリーンショットの撮影と保存、レンダリング結果のエクスポート、テクスチャのファイル出力、ゲーム内カメラ機能での写真保存、ユーザーがカスタマイズした画像の永続化など。

**主要な出力内容**：
1. JPEG圧縮された画像データ（RGB8またはグレースケール）
2. 品質パラメータによる圧縮率の調整（0.0〜1.0）
3. 4:2:0 クロマサブサンプリングによる効率的な圧縮

**帳票の出力タイミング**：GDScriptまたはC++コードから `Image.save_jpg()` または `Image.save_jpg_to_buffer()` メソッドを呼び出した際に出力される。

**帳票の利用者**：ゲーム開発者、プラグイン開発者、エンドユーザー（ゲーム内機能として）

## 帳票種別

画像エクスポート / バイナリファイル出力

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | GDScript/C++ API | Image.save_jpg() | save_jpg(path, quality) メソッド呼び出し |
| - | GDScript/C++ API | Image.save_jpg_to_buffer() | save_jpg_to_buffer(quality) メソッド呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JPEG（.jpg / .jpeg） |
| 用紙サイズ | N/A（画像データ） |
| 向き | N/A |
| ファイル名 | 任意（呼び出し元で指定） |
| 出力方法 | ファイル保存 / メモリバッファ返却 |
| 文字コード | N/A（バイナリ） |

### JPEG固有設定

| 項目 | 内容 |
|-----|------|
| 圧縮品質 | 0〜100（引数 quality * 100） |
| ビット深度 | 8ビット固定 |
| サブサンプリング | TJSAMP_420（4:2:0） |
| カラースペース | RGB / Grayscale |

## 帳票レイアウト

### レイアウト概要

JPEG形式は標準的なJFIF/Exif構造に従う。

```
┌─────────────────────────────────────┐
│         JPEG Header (SOI)           │
├─────────────────────────────────────┤
│      Quantization Tables (DQT)      │
├─────────────────────────────────────┤
│        Frame Header (SOF0)          │
├─────────────────────────────────────┤
│      Huffman Tables (DHT)           │
├─────────────────────────────────────┤
│         Scan Data (SOS)             │
│      (Compressed Image Data)        │
├─────────────────────────────────────┤
│         JPEG Trailer (EOI)          │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | SOI | Start of Image マーカー | 固定値 | 0xFFD8 |
| 2 | DQT | 量子化テーブル | libjpeg-turbo生成 | バイナリ |
| 3 | SOF0 | フレームヘッダー | Image width/height | バイナリ |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | 画像データ | 圧縮済みスキャンデータ | Image.get_data() | DCT圧縮バイナリ | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | EOI | End of Image マーカー | 固定値 | 0xFFD9 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 有効な画像 | p_img が null でなく、空でないこと | Yes |
| 品質値 | 0.0〜1.0 の範囲 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| - | N/A（単一画像出力） | - |

### 改ページ条件

N/A（バイナリファイル出力）

## データベース参照仕様

### 参照テーブル一覧

N/A（メモリ上の Image オブジェクトから直接出力）

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 品質値変換 | int(p_quality * 100) | 切り捨て | 0-100の整数に変換 |
| 出力バッファサイズ | libjpeg-turbo が動的決定 | - | 圧縮後サイズ |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[save_jpg 呼び出し] --> B[画像有効性チェック]
    B --> C{圧縮済み?}
    C -->|Yes| D[decompress 実行]
    C -->|No| E[フォーマット確認]
    D --> E
    E --> F{RGB8?}
    F -->|No| G[RGB8に変換]
    F -->|Yes| H[TurboJPEG初期化]
    G --> H
    H --> I[品質/精度/サブサンプリング設定]
    I --> J[tj3Compress8 実行]
    J --> K[バッファをファイルに書き込み]
    K --> L[TurboJPEGクリーンアップ]
    L --> M[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 画像無効 | p_img.is_null() または is_empty() | ERR_FAIL_COND_V | 有効な画像を渡す |
| 解凍失敗 | decompress() が OK 以外を返す | "Couldn't decompress image." | 対応形式を確認 |
| TJ初期化失敗 | tj3Init が NULL を返す | "Couldn't create tjhandle" | システムリソースを確認 |
| 品質設定失敗 | tj3Set が負の値を返す | "Couldn't set jpg quality" | 品質値を確認 |
| 圧縮失敗 | tj3Compress8 が負の値を返す | "Couldn't compress jpg" | 画像データを確認 |
| ファイル書き込み失敗 | FileAccess::open 失敗 | "Can't save JPG at path: '%s'." | パスを確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 単一画像 |
| 目標出力時間 | 1920x1080画像で100ms以内（libjpeg-turbo使用） |
| 同時出力数上限 | スレッドセーフではない（呼び出し元で制御） |

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

- 出力パスはユーザー指定のため、パストラバーサル攻撃に注意が必要
- libjpeg-turbo は外部ライブラリのため、セキュリティアップデートへの追従が必要
- メモリバッファ版を使用する場合、バッファの適切な解放が必要

## 備考

- libjpeg-turbo を使用しており、標準 libjpeg より高速に動作する
- グレースケール画像の場合は L8 フォーマットが保持される可能性があるが、現実装では RGB8 に変換される
- 精度は 8 ビット固定（16 ビット以上は未サポート）

---

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

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

### 推奨読解順序

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

まず、Image クラスのデータ構造と JPEG 保存用関数ポインタの仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | image.h | `core/io/image.h` | Image クラスの save_jpg_func、save_jpg_buffer_func 関数ポインタ宣言 |
| 1-2 | image.cpp | `core/io/image.cpp` | save_jpg()、save_jpg_to_buffer() の実装（関数ポインタ呼び出し） |

**読解のコツ**: Godot では静的関数ポインタを使用してモジュール間の依存を疎結合にしている。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | register_types.cpp | `modules/jpg/register_types.cpp` | モジュール初期化時の ImageLoaderLibJPEGTurbo インスタンス化（行49-50） |
| 2-2 | image_loader_libjpeg_turbo.cpp | `modules/jpg/image_loader_libjpeg_turbo.cpp` | コンストラクタでの関数ポインタ設定（行185-189） |

**主要処理フロー**:
1. **行185-189**: ImageLoaderLibJPEGTurbo コンストラクタで Image クラスの関数ポインタを設定
2. **行173-183**: _jpeg_turbo_save_func がファイル保存の実装
3. **行108-171**: _jpeg_turbo_buffer_save_func がバッファ保存の実装

#### Step 3: JPEG圧縮処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | image_loader_libjpeg_turbo.cpp | `modules/jpg/image_loader_libjpeg_turbo.cpp` | _jpeg_turbo_buffer_save_func の実装詳細 |

**主要処理フロー**:
- **行111-116**: 画像の有効性チェックと解凍処理
- **行119-123**: RGB8 フォーマットへの変換
- **行125-141**: TurboJPEG の初期化と設定（品質、精度、サブサンプリング）
- **行148-156**: tj3Compress8 による圧縮実行
- **行164-168**: 結果のコピーとクリーンアップ

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

```
Image::save_jpg(path, quality)
    │
    └─ Image::save_jpg_func (関数ポインタ)
           │
           └─ _jpeg_turbo_save_func(path, image, quality)
                  │
                  ├─ FileAccess::open(path, WRITE)
                  │
                  └─ _jpeg_turbo_buffer_save_func(image, quality)
                         │
                         ├─ image->decompress() [必要時]
                         │
                         ├─ image->convert(FORMAT_RGB8) [必要時]
                         │
                         ├─ tj3Init(TJINIT_COMPRESS)
                         │
                         ├─ tj3Set() [品質、精度、サブサンプリング]
                         │
                         ├─ tj3Compress8()
                         │
                         └─ tj3Destroy() / tj3Free()
```

### データフロー図

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

Image オブジェクト
    │
    ├─ width ─────────┐
    ├─ height ────────┼──▶ tj3Compress8 ───▶ JPEG バイナリデータ
    ├─ format ────────┤         │
    └─ data[] ────────┘         │
                                ▼
quality パラメータ ────────▶ tj3Set(QUALITY) ───▶ 圧縮品質設定
                                │
                                ▼
                        FileAccess::store_buffer() ───▶ .jpg ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| image_loader_libjpeg_turbo.cpp | `modules/jpg/image_loader_libjpeg_turbo.cpp` | ソース | JPEG保存の主実装 |
| image_loader_libjpeg_turbo.h | `modules/jpg/image_loader_libjpeg_turbo.h` | ヘッダー | クラス宣言 |
| register_types.cpp | `modules/jpg/register_types.cpp` | ソース | モジュール初期化 |
| image.h | `core/io/image.h` | ヘッダー | Image クラス宣言・関数ポインタ |
| image.cpp | `core/io/image.cpp` | ソース | Image クラス実装 |
| turbojpeg.h | 外部ライブラリ | ヘッダー | libjpeg-turbo API |
