Color spaces in AgX

AgX's render pipeline does math on pixel values in two related color spaces: linear Rec.2020 (the working space for physical operations) and gamma-encoded Rec.2020 (the working space for perceptual operations). Each stage runs in the space where its math is physically or perceptually correct. This page is the lookup reference for definitions, conversions, gamut matrices, and the per-stage assignment.

Linear vs gamma-encoded

There are two common ways to represent color values:

Linear light (also called "scene-referred"): values are proportional to physical light intensity. Double the value = double the photons. This is how light works in the real world. The specific linear space AgX uses internally is linear Rec.2020.

Gamma-encoded (also called "display-referred"): values are perceptually spaced for human vision. Our eyes are much more sensitive to changes in dark tones than bright ones. A gamma encoding allocates more of the 0–1 range to dark values, which is why JPEGs and PNGs use a gamma encoding by default.

AgX's gamma working space reuses the sRGB transfer-curve shape applied to Rec.2020 linear values. A sign-preserving variant of the curve handles small negative components that can arise from wide-gamut matrix conversions.

The conversion

The approximate relationship is a power curve:

  • Linear to gamma: gamma = linear ^ (1/2.2)
  • Gamma to linear: linear = gamma ^ 2.2

The exact sRGB specification uses a piecewise function with a linear segment near zero, and AgX uses that exact shape. The sign-preserving form is:

gamma  = sign(x) * srgb_curve(|x|)
linear = sign(x) * srgb_curve_inverse(|x|)

What 0.5 means in each space

  • Linear 0.5 = 50% of maximum light intensity (physically half as bright as 1.0).
  • Gamma 0.5 = a perceptual midtone (the gray that looks halfway between black and white on screen).

Doing math in the wrong space produces wrong results. Multiplying linear values by 2 doubles the light (correct exposure adjustment). Multiplying gamma values by 2 produces a non-physical result that doesn't look right.

Gamut and matrices

AgX accepts three primary input gamuts and converts each into linear Rec.2020 at decode:

Input primariesConversion to linear Rec.2020
sRGB / BT.7093×3 matrix (see below)
Display P33×3 matrix (see below)
BT.2020identity (primaries match Rec.2020)

PQ and HLG transfer-encoded inputs are not parsed as HDR; the decoder treats them as sRGB with a stderr warning.

Linear Rec.2020 → linear sRGB:

RGB
R1.660491-0.587641-0.072850
G-0.1245501.132899-0.008349
B-0.018151-0.1005791.118730

Linear sRGB → linear Rec.2020:

RGB
R0.6274040.3292830.043313
G0.0690970.9195410.011362
B0.0163910.0880130.895595

Linear Display P3 → linear Rec.2020:

RGB
R0.7538330.1985970.047570
G0.0457440.9417760.012480
B-0.0012100.0176010.983610

The Rec.2020 gamut contains the P3 gamut, but P3 primaries expressed in Rec.2020 coordinates can produce small negative components (e.g., the blue channel of P3 red is ≈ −0.0012). Downstream stages tolerate these; the final encode clamps to the 0–1 range.

Working space

AgX's working space is linear Rec.2020 for physical operations and gamma-encoded Rec.2020 for perceptual operations. The gamma encoding uses the sRGB transfer-curve shape applied to Rec.2020 linear values, in the sign-preserving variant.

Per-stage table

Each stage runs in the space where its math is correct.

StageColor space
White balanceLinear Rec.2020
ExposureLinear Rec.2020
DehazeLinear Rec.2020
Noise reductionLinear Rec.2020
Contrast, highlights, shadows, whites, blacksGamma Rec.2020
Tone curvesGamma Rec.2020
HSL adjustmentsGamma Rec.2020
Color gradingGamma Rec.2020
LUTGamma Rec.2020 (sampled in sRGB gamma via the engine's conversion bracket)
Detail pass (sharpen, clarity, texture)Gamma Rec.2020
GrainGamma Rec.2020
VignetteGamma Rec.2020

The LUT stage lives in the gamma-Rec.2020 portion of the pipeline, but the LUT itself is sampled in sRGB gamma — the engine wraps the LUT call with a conversion bracket so third-party .cube LUTs authored against sRGB continue to work unchanged.

See also