# 帳票設計書 12-画像WebP

## 概要

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

### 本帳票の処理概要

本機能は、Godot Engineの Image クラスが保持する画像データを WebP 形式（.webp）でファイルまたはメモリバッファに出力する。libwebp ライブラリを使用して可逆圧縮（lossless）と非可逆圧縮（lossy）の両方をサポートしている。

**業務上の目的・背景**：WebP形式はGoogleが開発した現代的な画像フォーマットであり、JPEGやPNGと比較して優れた圧縮率を実現する。ゲーム開発において、テクスチャの効率的な保存、アセットのエクスポート、高効率なスクリーンショット保存など、ファイルサイズを抑えつつ高品質を維持したい場面で活用される。WebPはアルファチャンネル（透過）をサポートしながらも高い圧縮率を維持できるため、PNGの代替としても有用である。

**帳票の利用シーン**：テクスチャのエクスポート、スクリーンショットの高効率保存、ImageTexture リソースのファイル保存、ウェブ配信用画像の生成、アルファチャンネル付き画像の効率的な圧縮保存など。

**主要な出力内容**：
1. WebP圧縮された画像データ（RGB8またはRGBA8）
2. 可逆圧縮（lossless）または非可逆圧縮（lossy）の選択
3. 品質パラメータによる圧縮率の調整（lossy時：0.0〜1.0）
4. 圧縮メソッド（0〜6）による速度/品質トレードオフ

**帳票の出力タイミング**：GDScriptまたはC++コードから `Image.save_webp()` または `Image.save_webp_to_buffer()` メソッドを呼び出した際、または ResourceSaverWebP を通じて ImageTexture を保存した際に出力される。

**帳票の利用者**：ゲーム開発者、プラグイン開発者、テクスチャアーティスト、エンドユーザー（ゲーム内機能として）

## 帳票種別

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

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | GDScript/C++ API | Image.save_webp() | save_webp(path, lossy, quality) メソッド呼び出し |
| - | GDScript/C++ API | Image.save_webp_to_buffer() | save_webp_to_buffer(lossy, quality) メソッド呼び出し |
| - | リソース保存 | ResourceSaver.save() | ImageTexture を .webp 拡張子で保存 |

## 出力形式

### 基本仕様

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

### WebP固有設定

| 項目 | 内容 |
|-----|------|
| 圧縮モード | lossy（非可逆）/ lossless（可逆） |
| 品質値（lossy） | 0〜100（引数 quality * 100） |
| 圧縮係数（lossless） | project設定 rendering/textures/webp_compression/lossless_compression_factor |
| 圧縮メソッド | 0〜6（project設定 rendering/textures/webp_compression/compression_method） |
| カラースペース | RGB / RGBA（アルファチャンネル対応） |

## 帳票レイアウト

### レイアウト概要

WebP形式はRIFFコンテナ構造に従う。

```
┌─────────────────────────────────────┐
│      RIFF Header ("RIFF____WEBP")   │
├─────────────────────────────────────┤
│          VP8/VP8L Chunk             │
│      (Compressed Image Data)        │
├─────────────────────────────────────┤
│      Optional: ALPH Chunk           │
│      (Alpha Channel Data)           │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | RIFF | RIFFコンテナマーカー | 固定値 | "RIFF" (4バイト) |
| 2 | ファイルサイズ | 全体サイズ-8 | libwebp計算 | 32ビットLE |
| 3 | WEBP | WebP識別子 | 固定値 | "WEBP" (4バイト) |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | VP8/VP8L | 圧縮画像チャンク | WebPEncode結果 | バイナリ | 可変 |
| 2 | ALPH | アルファチャンネル | RGBA8画像時 | バイナリ | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| - | N/A | WebP形式にはフッターマーカーなし | - | - |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 有効な画像 | p_img が null でなく、空でないこと | Yes |
| 品質値（lossy時） | 0.0〜1.0 の範囲 | No（デフォルト0.75） |
| lossyフラグ | true: 非可逆、false: 可逆 | No（デフォルトfalse） |

### ソート順

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

### 改ページ条件

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

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

### 参照テーブル一覧

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

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 品質値変換（lossy） | CLAMP(p_quality * 100.0f, 0.0f, 100.0f) | クランプ | 0-100に制限 |
| 圧縮係数（lossless） | CLAMP(compression_factor, 0.0f, 100.0f) | クランプ | project設定から取得 |
| ストライド（RGB） | 3 * width | - | WebPPictureImportRGB用 |
| ストライド（RGBA） | 4 * width | - | WebPPictureImportRGBA用 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[save_webp 呼び出し] --> B[画像有効性チェック]
    B --> C{圧縮済み?}
    C -->|Yes| D[decompress 実行]
    C -->|No| E[アルファチャンネル検出]
    D --> E
    E --> F{アルファあり?}
    F -->|Yes| G[RGBA8に変換]
    F -->|No| H[RGB8に変換]
    G --> I[WebPConfig初期化]
    H --> I
    I --> J{lossless?}
    J -->|Yes| K[lossless=1, exact=1 設定]
    J -->|No| L[quality設定]
    K --> M[WebPPicture設定]
    L --> M
    M --> N{RGB or RGBA?}
    N -->|RGB| O[WebPPictureImportRGB]
    N -->|RGBA| P[WebPPictureImportRGBA]
    O --> Q[WebPEncode実行]
    P --> Q
    Q --> R[結果をバッファにコピー]
    R --> S[ファイルに書き込み]
    S --> T[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 画像無効 | p_image.is_null() または is_empty() | ERR_FAIL_COND_V | 有効な画像を渡す |
| 解凍失敗 | decompress() が OK 以外を返す | "Couldn't decompress image." | 対応形式を確認 |
| 初期化失敗 | WebPConfigInit/WebPPictureInit失敗 | ERR_FAIL_V | システムリソースを確認 |
| エンコード失敗 | WebPEncode が false を返す | "WebP packing failed." | 画像データを確認 |
| ファイル書き込み失敗 | FileAccess::open 失敗 | "Can't save WebP at path: '%s'." | パスを確認 |
| テクスチャ無効 | ImageTexture が無効 | "Can't save invalid texture as WebP." | 有効なテクスチャを渡す |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 単一画像 |
| 目標出力時間 | 圧縮メソッド依存（0=高速、6=高品質低速） |
| 同時出力数上限 | スレッドセーフではない（呼び出し元で制御） |

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

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

## 備考

- use_sharp_yuv = 1 が設定されており、高品質なYUV変換が行われる
- lossless モードでは exact = 1 が設定され、RGB値が正確に保存される
- アルファチャンネルの有無は detect_alpha() で自動判定される
- 圧縮メソッドはプロジェクト設定から取得される（0〜6）

---

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

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

### 推奨読解順序

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

まず、Image クラスと WebP 保存インターフェースの関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | image.h | `core/io/image.h` | Image クラスの save_webp_func、save_webp_buffer_func 関数ポインタ宣言 |
| 1-2 | resource_saver_webp.h | `modules/webp/resource_saver_webp.h` | ResourceSaverWebP クラス宣言、save_image/save_image_to_buffer |

**読解のコツ**: WebP保存は2つのパスがある - Image直接保存とResourceSaver経由。どちらも最終的に同じ関数を使用する。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | resource_saver_webp.cpp | `modules/webp/resource_saver_webp.cpp` | コンストラクタでの関数ポインタ設定（行88-91） |
| 2-2 | resource_saver_webp.cpp | `modules/webp/resource_saver_webp.cpp` | save_image と save_image_to_buffer の実装 |

**主要処理フロー**:
1. **行88-91**: ResourceSaverWebP コンストラクタで Image クラスの関数ポインタを設定
2. **行52-66**: save_image がファイル保存の実装
3. **行68-76**: save_image_to_buffer がバッファ保存の実装

#### Step 3: WebP共通処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | webp_common.h | `modules/webp/webp_common.h` | WebPCommon 名前空間の関数宣言 |
| 3-2 | webp_common.cpp | `modules/webp/webp_common.cpp` | _webp_lossy_pack、_webp_lossless_pack、_webp_packer の実装 |

**主要処理フロー**:
- **行39-43**: _webp_lossy_pack - 非可逆圧縮のエントリーポイント
- **行45-52**: _webp_lossless_pack - 可逆圧縮のエントリーポイント
- **行54-120**: _webp_packer - 共通の圧縮処理実装
- **行58-67**: 画像のフォーマット変換（RGBA8/RGB8）
- **行75-94**: WebPConfig と WebPPicture の設定
- **行96-105**: インポートとエンコード実行

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

```
Image::save_webp(path, lossy, quality)
    │
    └─ Image::save_webp_func (関数ポインタ)
           │
           └─ ResourceSaverWebP::save_image(path, image, lossy, quality)
                  │
                  └─ ResourceSaverWebP::save_image_to_buffer(image, lossy, quality)
                         │
                         ├─ [lossy=true] WebPCommon::_webp_lossy_pack(image, quality)
                         │       │
                         │       └─ WebPCommon::_webp_packer(image, quality*100, false)
                         │
                         └─ [lossy=false] WebPCommon::_webp_lossless_pack(image)
                                 │
                                 └─ WebPCommon::_webp_packer(image, compression_factor, true)
                                        │
                                        ├─ image->decompress() [必要時]
                                        │
                                        ├─ image->detect_alpha()
                                        │
                                        ├─ image->convert(FORMAT_RGBA8 or FORMAT_RGB8)
                                        │
                                        ├─ WebPConfigInit() / WebPPictureInit()
                                        │
                                        ├─ WebPPictureImportRGB/RGBA()
                                        │
                                        ├─ WebPEncode()
                                        │
                                        └─ WebPMemoryWriterClear()
```

### データフロー図

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

Image オブジェクト
    │
    ├─ width ─────────┐
    ├─ height ────────┤
    ├─ format ────────┼──▶ WebPPictureImportRGB/RGBA
    └─ data[] ────────┘           │
                                  ▼
lossy パラメータ ─────────▶ config.lossless 設定
                                  │
quality パラメータ ───────▶ config.quality 設定
                                  │
project設定 ──────────────▶ config.method 設定
    │                             │
    │                             ▼
    │                      WebPEncode()
    │                             │
    │                             ▼
    │                   WebPMemoryWriter.mem ───▶ Vector<uint8_t>
    │                             │
    │                             ▼
    └────────────────▶ FileAccess::store_buffer() ───▶ .webp ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| resource_saver_webp.cpp | `modules/webp/resource_saver_webp.cpp` | ソース | ResourceSaver実装、関数ポインタ設定 |
| resource_saver_webp.h | `modules/webp/resource_saver_webp.h` | ヘッダー | クラス宣言 |
| webp_common.cpp | `modules/webp/webp_common.cpp` | ソース | WebPエンコード共通処理 |
| webp_common.h | `modules/webp/webp_common.h` | ヘッダー | 関数宣言 |
| image.h | `core/io/image.h` | ヘッダー | Image クラス宣言・関数ポインタ |
| image.cpp | `core/io/image.cpp` | ソース | Image クラス実装 |
| webp/encode.h | 外部ライブラリ | ヘッダー | libwebp エンコードAPI |
| webp/decode.h | 外部ライブラリ | ヘッダー | libwebp デコードAPI |
