feat(gamut-cmm): matrix/TRC shaper pipelines and the chad convention - #369
Open
justin13888 wants to merge 3 commits into
Open
feat(gamut-cmm): matrix/TRC shaper pipelines and the chad convention#369justin13888 wants to merge 3 commits into
justin13888 wants to merge 3 commits into
Conversation
Builds runnable pipelines from v2 and v4 matrix/TRC profiles - the shape carried by sRGB, Display P3, Adobe RGB, and every camera-tagged JPEG - in both directions: TRC curves into the colorant matrix (column per primary, as read) for device->PCS, and the linalg-inverted matrix into analytically inverted curves for PCS->device (singular or non-finite colorant matrices reject with SingularMatrix). Gray kTRC profiles map through the new general-affine Stage::MatrixN (1<->3 shapes, decoded D50 scaling for XYZ PCS, L*/100 for Lab PCS). The flagged v2/v4 risk is settled and documented: colorants are taken as-is (spec-mandated D50-adapted), chad is never read on the relative path - matching lcms2, whose only chad consumer is the absolute-intent branch, inert at default adaptation state - and wtpt is reserved for absolute colorimetric (#329). LUT tags take precedence over the shaper; until #328 lands those pipelines they refuse with UnsupportedProfile rather than guessing. Part of #327.
Differentials reopen each synthesized profile from its serialized bytes on both sides, so the comparison measures evaluation semantics rather than tag quantization: device->PCS raw XYZ within 1e-6 (f32 oracle floor, measured 8.8e-8) for sRGB, Display P3, and a gamma-2.2 wide-gamut shaper; the chad three-way tested separately as the issue demands - v2-with-chad and v2-without-chad produce bitwise-identical pipelines, v2 and v4 of the same colorimetry agree exactly, and each matches lcms2. Gray, round-trip, and PCS->device sweeps carry measured, justified bounds (near-black looseness is gamma-slope amplification, documented at the assert). Error paths are pinned by exact display string: singular colorants, missing/mistyped tags, and the LUT-tag refusal boundary. Part of #327.
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 6/9 of the gamut-cmm stack (epic #323), stacked on #368 — the epic's flagged highest-risk phase: matrix/TRC ("shaper") pipelines and the v2/v4 chromatic-adaptation convention.
Curvesinto the colorantMatrix(column per primary); PCS→device:linalg-inverted matrix into analytically inverted curves (SingularMatrixon singular/non-finite colorants — exact-zero/non-finite determinant is the documented conditioning threshold). GraykTRCvia the new general-affineStage::MatrixN(1↔3, decoded D50 scaling for XYZ PCS, L*/100 for Lab PCS).chadis informative),chadis never read on the relative path (lcms2's sole chad consumer is the absolute-intent branch, inert at default adaptation state — exhaustive consumer audit cited),wtptis reserved for absolute colorimetric (gamut-cmm: rendering intents (absolute colorimetric) and black point compensation #329). Deliberate match of lcms2 over a strict v2 reading, per the issue's requirement to document the divergence explicitly.UnsupportedProfilerather than guessing.Validation
mise run mutants-crate gamut-cmm(full crate): 561 mutants — 546 caught, 15 unviable, 0 missed. Coverage 99.0% lines; clippy-D warnings, fmt-check, cargo doc clean.Stack
#321 (#351) → #322 (#358) → #324 (#362) → #325 (#364) → #326 (#368) → #327 (this) → #328 → #329 → #330.
Closes #327. Part of #323.