Skip to content

feat(lcms2-oracle): transform, ΔE, and black-point bindings plus synthesizer corpus - #358

Open
justin13888 wants to merge 3 commits into
feat/321-color-cielab-de2000from
feat/322-lcms2-oracle-transforms
Open

feat(lcms2-oracle): transform, ΔE, and black-point bindings plus synthesizer corpus#358
justin13888 wants to merge 3 commits into
feat/321-color-cielab-de2000from
feat/322-lcms2-oracle-transforms

Conversation

@justin13888

Copy link
Copy Markdown
Collaborator

Summary

PR 2/9 of the gamut-cmm stack (epic #323), stacked on #351. Extends tooling/lcms2-oracle so colour transforms — not just profiles — can be differentially validated against Little-CMS:

  • Transform bindings: RAII Transform over cmsHTRANSFORM (profile pair, multiprofile, proofing) with f64/u16/u8 apply paths; black-point detection; quiet log handler; alarm codes; public ToneCurve (gamma/parametric/tabulated, eval, reverse); cmsXYZ2Lab/cmsLab2XYZ, ΔE76/CIEDE2000, and the v2/v4 Lab + XYZ encoders.
  • TYPE_*/cmsFLAGS_* constants rebuilt in Rust — bindgen cannot expand the function-like *_SH() macro forms, so a documented const fn replicates the packing (pinned against the verified numeric values) and the flags are transcribed with lcms2.h line citations. build.rs is byte-identical (issue acceptance criterion).
  • New synthesizer corpus for the CMM engine: scnr camera profiles (matrix-shaper + LUT), v4 and v2 CMYK prtr with six distinct per-intent A2B/B2A tables, Display P3 with the true piecewise sRGB TRC (parametric type 4), v2 matrix-shaper with/without chad, and a v4 shaper with a real non-D50 wtpt (absolute-intent test vehicle — cmsCreateRGBProfile always writes wtpt = D50 + chad, which would make those tests vacuous).
  • Differential tests: gamut-color ΔE₀₀ agrees with cmsCIE2000DeltaE to ≤1e-12 over 632 seeded pairs (hue-wrap/achromatic edges included); XYZ↔Lab ≤1e-12 under explicit D50; encoders match lcms2 exactly over dense sweeps + every clamp boundary (lcms2's fast-floor ambiguity window documented and skipped). gamut-icc parses and round-trips every new synthesizer profile and pins the v2→lut16 / v4→lutAToB tag serialization.

Validation

  • cargo test -p gamut-color -p gamut-icc --all-features + oracle's own tests: all green (15 oracle, 6 new gamut-color, 2 new gamut-icc tests).
  • Clippy -D warnings, mise run fmt-check clean; git diff --stat tooling/lcms2-oracle/build.rs empty.

Stack

#321 (#351) → #322 (this)#324#325#326#327#328#329#330.

Closes #322. Part of #323.

Extends the dev-only Little-CMS oracle beyond profile synthesis and
inspection so colour transforms can be differentially validated:

- RAII Transform over cmsHTRANSFORM (pair, multiprofile, proofing)
  with f64/u16/u8 apply paths; TYPE_* pixel-format codes rebuilt as
  documented const fns (bindgen cannot expand the *_SH macro forms)
  and cmsFLAGS_* transcribed with lcms2.h line citations, both pinned
  by tests; INTENT_* re-exported from the generated bindings.
- Public ToneCurve (gamma/parametric/tabulated, eval, reverse) and
  cmsXYZ2Lab/cmsLab2XYZ, dE76/CIEDE2000, and the v2/v4 Lab and XYZ
  encoders; black-point detection; quiet log handler; alarm codes.
- New synthesizers for the CMM engine test corpus: scnr camera
  profiles (matrix-shaper + LUT), v4 and v2 CMYK prtr with distinct
  per-intent A2B/B2A tables, Display P3 with the true piecewise sRGB
  TRC (parametric type 4), v2 matrix-shaper with and without chad,
  and a v4 shaper with a real (non-D50) wtpt for absolute-intent
  tests; existing synthesizers move to synth.rs with re-exports.

build.rs is byte-identical: the existing cms.* allowlists already
generate every binding used here.

Part of #322.
Differential suite over the new oracle bindings: CIEDE2000 agrees with
cmsCIE2000DeltaE to 1e-12 across 632 seeded pairs including hue-wrap,
one-chroma-zero, and achromatic edges (weighted variant included);
XYZ<->Lab matches cmsXYZ2Lab/cmsLab2XYZ to 1e-12 under an explicit D50
white, with the ~3e-6 rounded-literal white divergence measured and
bounded separately; the v2/v4 16-bit Lab and PCSXYZ encoders match
lcms2 exactly over dense sweeps and every clamp boundary, skipping
only inputs inside lcms2's documented fast-floor ambiguity window.

Part of #322.
Every profile the new lcms2-oracle synthesizers emit (scnr matrix and
LUT camera profiles, v4/v2 CMYK prtr, Display P3 with the piecewise
sRGB TRC, v2 shapers with and without chad, the non-D50-wtpt shaper)
parses without error and round-trips; the v2 prtr's A2B0 decodes as
lut16 and the v4 prtr's as lutAToB, pinning the version-dependent tag
serialization the CMM's per-intent selection relies on.

Part of #322.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant