Launch OpenGPEX

OpenGPEX 1.x documentation (legacy editor).

v2 Docs

Color Pipeline

How OpenGPEX manages color spaces across the entire image lifecycle — from import through compositing to export — using a centralized strategy matrix that ensures zero color drift and full ICC profile round-trip fidelity.


Architecture: Centralized Strategy Routing

All color-space decisions are concentrated in a single ColorPipeline.ts strategy matrix. Format handlers, compositors, and renderers query this matrix and execute instructions — they never make autonomous color decisions.

Source File → [FORMAT_COLOR_STRATEGY] → detectedCS → [IMPORT_PIPELINE] → Frame.colorSpace
                                                                              │
                                                                    ┌─────────┴─────────┐
                                                                    ▼                   ▼
                                                            [WORKING_PIPELINE]    sourceBlob?
                                                            composite/display         │
                                                            /export                   ▼
                                                                              compositeFrame + encode
                                                                              (unified 8-bit path)

Core Design Principles

  1. Single source of truth — Modify one strategy entry to change behavior globally
  2. Only two runtime values — Frame.colorSpace is always 'srgb' or 'display-p3'
  3. Handlers are pure executors — They switch/case on strategy instructions, zero if/else judgment
  4. Renderer-agnostic — Color management is pipeline logic, not tied to Canvas 2D / WebGL / WebGPU

Two-Layer Strategy System

Layer 1: Format Strategy (by file format)

Decides how to read color metadata and whether to retain the original source blob:

Format Reads ICC Supports ICC Embed Source Blob Retention
PNG ✅ ✅ On fidelity loss
JPEG ✅ ✅ On fidelity loss
WebP ✅ ✅ On fidelity loss
AVIF ✅ ✅ On fidelity loss
TIFF ✅ ✅ On fidelity loss
RAW ✅ ❌ Always
HEIC ✅ ❌ (decode only) Always (revert needs original for metadata re-extraction)
BMP/GIF/SVG/EPS ❌ ❌ Never (fixed sRGB)

Layer 2: Color Space Strategy (by detected color space)

Decides what happens to pixels at each pipeline stage:

Source ColorSpace Frame.colorSpace Conversion Notes
sRGB srgb None Zero-cost passthrough
Display P3 display-p3 None Native wide-gamut
Adobe RGB display-p3 3×3 Matrix P3 covers ~98% of AdobeRGB
ProPhoto RGB display-p3 3×3 Matrix ~30% super-gamut clamped
CMYK srgb ICC Engine (vips) Full Little CMS transform
Custom ICC srgb ICC Engine (vips) sourceBlob preserves original

Wide-Gamut (Display P3) Support

OpenGPEX natively supports Display P3 as a working color space:

  • Import: P3 source files set Frame.colorSpace = 'display-p3' directly; AdobeRGB/ProPhoto are matrix-converted to P3 at import time (one-shot, ~4ms/1080p)
  • Composite: Canvas 2D context created with { colorSpace: 'display-p3' } — zero per-layer conversion overhead
  • Display: Auto-detects P3 hardware via color-gamut: p3 media query; falls back to sRGB + matrix clamp on non-P3 displays
  • Export: P3 pixels encoded directly + ICC Profile embedded (PNG/JPEG/WebP/AVIF/TIFF)

Import-Time Conversion (Choice A)

AdobeRGB/ProPhoto pixels are converted to P3 once during decode(), not at composite time. This means:

  • Zero composite overhead — No per-layer, per-frame conversion during editing
  • Full export fidelity — sourceBlob preserves the original file for lossless round-trip
  • Simple architecture — Compositing only ever sees sRGB or P3 pixels

Export Color Management

All export follows the unified single-path architecture: compositeFrame (8-bit) → files.encode → download. Color decisions are made inline with no separate routing or strategy resolution.

Export Decisions

Two color-related decisions are made at export time:

Decision Function Purpose
ICC Embed shouldEmbedIcc(format, frameCS, userOverride?) 3-layer decision: format capability × strategy default × user choice
Format Downgrade Auto P3→sRGB conversion For formats that don't support wide-gamut (BMP/GIF)

ICC Embed Three-Layer Model

Layer 1: Format capability      (BMP/GIF → never; PNG/JPEG/WebP/AVIF/TIFF → allowed)
Layer 2: Strategy default        (sRGB: true; P3: true — matches Photoshop/Lightroom)
Layer 3: User override           (export UI checkbox, if provided)

Color Pipeline Infrastructure

Matrix Conversion Engine

Pure TypeScript 3×3 linear matrix transforms (zero external dependencies):

Transform Use Case Performance (1080p)
sRGB ↔ P3 Most common ~3ms
AdobeRGB → P3 Import ~3ms
ProPhoto → P3 Import (with Bradford D50→D65) ~4ms

TRC (Transfer Curve) Handling

  • sRGB ↔ linear conversion via pre-computed LUT (~2ms/4K)
  • Linear-light compositing for physically-correct blend modes
  • Filter TRC auto-insertion (blur executes in linear, auto-converts back)

ICC Profile Support

Capability Technology Location
ICC v2/v4 parsing Pure TypeScript core/files/icc.ts
Full ICC transform wasm-vips + Little CMS (Worker) file-io.ts
ICC injection (PNG) iCCP chunk builder handlers/png/writers.ts
ICC injection (JPEG) APP2 marker injection handlers/jpeg/jfif.ts
ICC injection (WebP) RIFF ICCP chunk (custom) handlers/webp/riff.ts
ICC injection (TIFF/AVIF) vips iccTransform Worker (vips)

Format × Color Space Matrix (Summary)

Source Import Action Frame.colorSpace Export Behavior
sRGB anything Passthrough srgb sRGB direct output
P3 anything Passthrough display-p3 P3 + ICC embed
AdobeRGB Matrix → P3 display-p3 P3 + ICC embed
ProPhoto Matrix → P3 display-p3 P3 + ICC embed
CMYK ICC → sRGB srgb sRGB + optional ICC restore
Custom ICC ICC → sRGB srgb sRGB + optional ICC restore

Comparison with Desktop Applications

Aspect Photoshop / GIMP OpenGPEX
Working space User-configured (any ICC) Auto-detected (sRGB or P3)
Color engine Little CMS (compiled in) Pluggable: BuiltinEngine (matrices) + VipsEngine (Little CMS)
Per-pixel tagging Yes (babl in GIMP) No — entire Frame shares one space
Conversion trigger Every op declares format → auto-insert Import-time one-shot + strategy matrix
Export ICC Optional, user-configured Three-layer auto-decision
Wide-gamut preview Requires calibrated workflow Auto-detects P3 hardware

Known Limitations & Future

Limitation Cause Resolution
ProPhoto ~30% super-gamut clamped Canvas 2D only supports P3/sRGB WebGPU (float16 textures)
8-bit canvas precision OffscreenCanvas limitation WebGPU (float16/float32)
No soft-proofing No display transform layer yet Phase D: DisplayStrategy extension
No user-initiated space conversion Frame.colorSpace currently immutable Planned as undoable command

Last updated: 2026-08-08