# 帳票設計書 7-EXRExporter

## 概要

本ドキュメントは、Three.jsライブラリにおけるEXRExporterの帳票設計書である。EXRExporterは画像データをEXR（Extended Dynamic Range）形式でエクスポートするためのモジュールであり、HDR画像出力に対応する。

### 本帳票の処理概要

EXRExporterは、Three.jsのDataTextureまたはRenderTarget（レンダリング結果）をEXR形式に変換してエクスポートする機能を提供する。EXR形式はハイダイナミックレンジ（HDR）画像を正確に保存できるフォーマットである。

**業務上の目的・背景**：EXR形式はILM（Industrial Light & Magic）が開発したオープンソースの画像フォーマットで、映画産業における標準的なHDR画像フォーマットとして広く採用されている。輝度情報を高精度に保持できるため、フォトリアリスティックレンダリング、VFX合成、HDR環境マップの保存、科学的可視化など、高精度な画像データが必要な用途に不可欠である。

**帳票の利用シーン**：PMREMジェネレーターで生成したHDR環境マップの保存、レンダリング結果のHDR保存、物理ベースレンダリングのライティング用テクスチャ作成、後処理用の高精度画像データ出力時に利用される。

**主要な出力内容**：
1. EXRファイルヘッダー（マジックナンバー、バージョン、属性）
2. チャンネル定義（R, G, B, A）
3. 画像サイズ情報（dataWindow, displayWindow）
4. 圧縮設定（NO_COMPRESSION, ZIPS_COMPRESSION, ZIP_COMPRESSION）
5. ピクセルデータ（Half Float 16bitまたはFloat 32bit）

**帳票の出力タイミング**：ユーザーがエクスポート操作を実行した時点でparseメソッドを呼び出すことで非同期に出力される。RenderTargetの場合はピクセル読み取りが必要なため非同期処理となる。

**帳票の利用者**：VFXアーティスト、3Dビジュアライゼーション担当者、ゲーム開発者（HDRテクスチャ用）、映像制作者が主な利用者である。

## 帳票種別

画像データ出力（バイナリファイル）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | Three.js アプリケーション | N/A（ライブラリ使用） | EXRExporter.parse() メソッド呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | OpenEXR (.exr) |
| 用紙サイズ | N/A（画像ファイル） |
| 向き | N/A |
| ファイル名 | 任意（アプリケーション側で指定） |
| 出力方法 | Uint8Array として返却 |
| 文字コード | N/A（バイナリ） |

### EXR固有設定

| 項目 | 内容 |
|-----|------|
| type | 出力データ型（HalfFloatType: 16bit、FloatType: 32bit、デフォルト: HalfFloatType） |
| compression | 圧縮方式（NO_COMPRESSION: 0、ZIPS_COMPRESSION: 2、ZIP_COMPRESSION: 3、デフォルト: ZIP_COMPRESSION） |

## 帳票レイアウト

### レイアウト概要

EXRファイルはヘッダー、オフセットテーブル、スキャンラインデータで構成される。

```
┌─────────────────────────────────────┐
│           マジックナンバー            │
│         (20000630, 4バイト)          │
├─────────────────────────────────────┤
│          バージョン/フラグ            │
│            (2, 4バイト)              │
├─────────────────────────────────────┤
│           ヘッダー属性               │
│  - compression                      │
│  - screenWindowCenter               │
│  - screenWindowWidth                │
│  - pixelAspectRatio                 │
│  - lineOrder                        │
│  - dataWindow                       │
│  - displayWindow                    │
│  - channels (R, G, B, A)            │
│  - null終端                         │
├─────────────────────────────────────┤
│         オフセットテーブル            │
│    (各ブロックへのオフセット)          │
├─────────────────────────────────────┤
│          スキャンラインデータ         │
│    (圧縮または非圧縮ピクセルデータ)    │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | magic | マジックナンバー | 固定値 | Uint32 LE (20000630) |
| 2 | version | バージョンとフラグ | 固定値 | Uint32 LE (2) |
| 3 | compression | 圧縮方式 | options.compression | 属性ブロック |
| 4 | screenWindowCenter | 画面中心 | 固定値 (0, 0) | v2f |
| 5 | screenWindowWidth | 画面幅 | 固定値 (1.0) | float |
| 6 | pixelAspectRatio | ピクセルアスペクト比 | 固定値 (1.0) | float |
| 7 | lineOrder | スキャンライン順序 | 固定値 (0: 上から下) | lineOrder |
| 8 | dataWindow | データ領域 | 画像サイズ | box2i |
| 9 | displayWindow | 表示領域 | 画像サイズ | box2i |
| 10 | channels | チャンネル定義 | 固定 (A, B, G, R) | chlist |

### 明細部（ピクセルデータ）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | サイズ |
|----|-------|------|-------------|---------|-------|
| 1 | y座標 | スキャンラインのY座標 | 計算値 | Uint32 LE | 4バイト |
| 2 | データサイズ | 圧縮後データサイズ | 計算値 | Uint32 LE | 4バイト |
| 3 | A | アルファチャンネル | ピクセルデータ | Half/Float | 幅×2/4バイト |
| 4 | B | 青チャンネル | ピクセルデータ | Half/Float | 幅×2/4バイト |
| 5 | G | 緑チャンネル | ピクセルデータ | Half/Float | 幅×2/4バイト |
| 6 | R | 赤チャンネル | ピクセルデータ | Half/Float | 幅×2/4バイト |

### フッター部

EXRフォーマットには明示的なフッターは存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| arg1 | DataTextureまたはRenderer | Yes |
| arg2 | RenderTarget（Renderer指定時）またはOptions | 状況による |
| arg3 | Options（Renderer+RenderTarget指定時） | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | スキャンライン（Y座標） | 上から下（lineOrder: 0） |
| 2 | チャンネル順 | A, B, G, R（アルファベット順） |

### 改ページ条件

N/A（単一ファイル出力）

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

### 参照テーブル一覧

本エクスポーターはデータベースを使用せず、Three.jsオブジェクトのメモリ上のデータを直接参照する。

| データソース | 用途 | 参照方法 |
|-------------|------|---------|
| DataTexture | 直接テクスチャデータ | isDataTexture で判定 |
| RenderTarget | レンダリング結果 | isRenderTarget で判定 |
| WebGLRenderer | ピクセル読み取り | isWebGLRenderer で判定 |
| WebGPURenderer | ピクセル読み取り | isWebGPURenderer で判定 |

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

#### DataTexture

| 参照項目（プロパティ） | 帳票項目との対応 | 取得条件 | 備考 |
|----------------------|----------------|---------|------|
| image.data | ピクセルデータ | 必須 | Float32ArrayまたはUint16Array |
| image.width | 画像幅 | 必須 | dataWindow, displayWindow |
| image.height | 画像高さ | 必須 | dataWindow, displayWindow |
| type | データ型 | 必須 | FloatTypeまたはHalfFloatType |
| format | フォーマット | 必須 | RGBAFormat |

#### RenderTarget

| 参照項目（プロパティ） | 帳票項目との対応 | 取得条件 | 備考 |
|----------------------|----------------|---------|------|
| width | 画像幅 | 必須 | dataWindow, displayWindow |
| height | 画像高さ | 必須 | dataWindow, displayWindow |
| texture.type | データ型 | 必須 | FloatTypeまたはHalfFloatType |
| texture.format | フォーマット | 必須 | RGBAFormat |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| ブロック数 | Math.ceil(HEIGHT / COMPRESSION_SIZE) | 切り上げ | 圧縮ブロック単位 |
| ブロックライン数 | NO/ZIPS: 1、ZIP: 16 | なし | 圧縮方式依存 |
| Half Float変換 | DataUtils.toHalfFloat(value) | なし | Float32→Float16 |
| Y座標反転 | h - y - 1 | なし | 上下反転（EXR座標系） |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[parse呼び出し] --> B{引数判定}
    B -->|DataTexture| C[supportedDT検証]
    B -->|Renderer+RenderTarget| D[supportedRTT検証]
    C --> E[buildInfoDT]
    D --> F[buildInfoRTT]
    F --> G[getPixelData]
    E --> H[reorganizeDataBuffer]
    G --> H
    H --> I[compressData]
    I --> J{compression?}
    J -->|NO_COMPRESSION| K[compressNONE]
    J -->|ZIP/ZIPS| L[compressZIP]
    K --> M[fillData]
    L --> M
    M --> N[fillHeader]
    N --> O[Uint8Array返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 入力エラー | 非対応の第1引数 | "Unsupported first parameter, expected instance of WebGLRenderer, WebGPURenderer or DataTexture" | 正しいオブジェクトを渡す |
| RenderTarget型エラー | Cube/3D/Array RenderTarget | "Unsupported render target type" | 通常のRenderTargetを使用 |
| テクスチャ型エラー | FloatType/HalfFloatType以外 | "Unsupported...texture type" | FloatTypeまたはHalfFloatTypeを使用 |
| フォーマットエラー | RGBAFormat以外 | "Unsupported...texture format, expected RGBAFormat" | RGBAFormatを使用 |
| データ型不一致 | FloatType + Float32Array以外 | "DataTexture image data doesn't match type" | 正しい配列型を使用 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 解像度: 〜8192×8192 |
| 目標出力時間 | 圧縮方式により変動（ZIP圧縮で数秒程度） |
| 同時出力数上限 | 1（非同期処理） |

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

- EXRファイルは浮動小数点ピクセルデータのみを含み、実行可能コードは含まれない
- 出力ファイルへのアクセス制御はアプリケーション側の責務
- 高解像度画像の処理ではメモリ使用量に注意

## 備考

- サポートする入力: DataTexture、WebGLRenderer+RenderTarget、WebGPURenderer+RenderTarget
- チャンネル順序はA, B, G, R（EXR仕様のアルファベット順）
- Y座標は反転して出力（EXR座標系）
- ZIP圧縮はfflateライブラリを使用

---

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

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

### 推奨読解順序

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

EXRフォーマットの基本構造と圧縮方式を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 11-14行目: 圧縮定数定義 |

**読解のコツ**: EXRは「スキャンラインベース」のフォーマットで、各ラインを個別に圧縮できることを理解する。

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

parseメソッドの入力判定と分岐を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 32-82行目: parseメソッド |

**主要処理フロー**:
1. **49-52行**: 第1引数の型判定（Renderer or DataTexture）
2. **53-65行**: Renderer+RenderTarget処理
3. **66-78行**: DataTexture処理

#### Step 3: データ検証を理解する

supportedRTTとsupportedDT関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 85-112行目: supportedRTT関数 |
| 3-2 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 114-145行目: supportedDT関数 |

**主要処理フロー**:
- **87-91行**: RenderTarget基本検証
- **93-97行**: Cube/3D/Array RenderTarget除外
- **99-103行**: テクスチャ型検証（FloatType/HalfFloatType）
- **105-109行**: フォーマット検証（RGBAFormat）

#### Step 4: ピクセルデータ処理を理解する

データ再構成と圧縮処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 243-288行目: reorganizeDataBuffer関数 |
| 4-2 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 290-334行目: compressData関数 |
| 4-3 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 342-382行目: compressZIP関数 |

**主要処理フロー**:
- **255-284行**: チャンネル分離とY座標反転（A, B, G, R順）
- **299-310行**: 圧縮方式選択
- **348-376行**: ZIP圧縮（リオーダー→プレディクター→deflate）

#### Step 5: ヘッダー生成を理解する

fillHeader関数でのEXRヘッダー構築を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | 384-481行目: fillHeader関数 |

**主要処理フロー**:
- **389-390行**: マジックナンバーとバージョン
- **394-463行**: 属性ブロック（compression、window、channels等）
- **471-479行**: オフセットテーブル

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

```
EXRExporter.parse(arg1, arg2, arg3)
    │
    ├─ 入力判定
    │      ├─ isWebGLRenderer || isWebGPURenderer
    │      │      └─ renderer, renderTarget, options
    │      └─ isDataTexture
    │             └─ texture, options
    │
    ├─ 検証
    │      ├─ supportedRTT(renderTarget) [RenderTarget時]
    │      └─ supportedDT(texture) [DataTexture時]
    │
    ├─ 情報構築
    │      ├─ buildInfoRTT(renderTarget, options) [RenderTarget時]
    │      └─ buildInfoDT(texture, options) [DataTexture時]
    │
    ├─ ピクセルデータ取得
    │      └─ getPixelData(renderer, rtt, info) [RenderTarget時]
    │             └─ readRenderTargetPixelsAsync()
    │
    ├─ データ再構成
    │      └─ reorganizeDataBuffer(dataBuffer, info)
    │             ├─ Y座標反転
    │             ├─ チャンネル分離 (A, B, G, R)
    │             └─ Half/Float変換
    │
    ├─ 圧縮
    │      └─ compressData(rawContentBuffer, info)
    │             ├─ compressNONE [NO_COMPRESSION]
    │             └─ compressZIP [ZIP/ZIPS]
    │                    ├─ リオーダー
    │                    ├─ プレディクター
    │                    └─ fflate.zlibSync()
    │
    └─ 出力生成
           └─ fillData(chunks, info)
                  ├─ fillHeader() [ヘッダー]
                  └─ スキャンラインデータ
```

### データフロー図

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

DataTexture ────────▶ supportedDT() ───────────▶ 検証OK/Error
      │                     │
      ▼                     ▼
texture.image.data ─▶ buildInfoDT() ───────────▶ info構造体
      │                     │                    (width, height, type等)
      │                     │
      └─────────────────────┼─────────────────────────────────────
                            │
RenderTarget ───────▶ supportedRTT() ──────────▶ 検証OK/Error
      │                     │
      ▼                     ▼
renderer ───────────▶ getPixelData() ──────────▶ Float32/Uint16Array
      │                     │
      └─────────────────────┼─────────────────────────────────────
                            ▼
dataBuffer ─────────▶ reorganizeDataBuffer() ──▶ チャンネル分離済み
      │                     │                    (A, B, G, R、Y反転)
      │                     │
      ▼                     ▼
rawContentBuffer ───▶ compressData() ──────────▶ 圧縮済みチャンク
      │                     │
      │                     ├─ compressNONE
      │                     └─ compressZIP
      │                            ├─ リオーダー
      │                            ├─ プレディクター
      │                            └─ zlibSync()
      │                     │
      ▼                     ▼
chunks ─────────────▶ fillData() ──────────────▶ Uint8Array
                            │
                            ├─ fillHeader()
                            └─ スキャンラインデータ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EXRExporter.js | `examples/jsm/exporters/EXRExporter.js` | ソース | メインエクスポーター実装 |
| fflate.module.js | `examples/jsm/libs/fflate.module.js` | ライブラリ | zlib圧縮（zlibSync） |
| three.module.js | `build/three.module.js` | ソース | Three.jsコアライブラリ（DataUtils等） |
