Skip to content

gamut-cmm: LUT-based pipelines and per-intent tag selection #328

Description

@justin13888

Part of #323. Blocked by #324 (scaffold), #325 (curves), #326 (CLUT), #321 (Lab), #322 (oracle).

Goal

Build runnable pipelines from LUT-based profileslut8, lut16, lutAToB (mAB ), and
lutBToA (mBA ) — and implement per-rendering-intent tag selection. These are the profiles that
carry CMYK printers, camera input profiles, and device links.

gamut-icc parses all four structurally and completely; none of them has an eval. This issue
turns the parsed structure into a Pipeline.

Scope

Stage assembly

Per ICC.1:2022 §10.10–10.13:

  • lut8 / lut16: 3×3 matrix → input tables → CLUT → output tables.
  • lutAToB (device→PCS): A-curves → CLUT → M-curves → matrix(3×3 + 3 offset) → B-curves.
  • lutBToA (PCS→device): B-curves → matrix → M-curves → CLUT → A-curves.

Every stage but the B-curves is optional. Note that gamut-icc deliberately accepts any stage
combination
a profile signals, not only the four combinations §10.12.1 permits — so this issue
must decide whether to evaluate a non-conforming combination or reject it, and document the choice.

PCS encoding per tag type

Gotcha to encode as an explicit test: lut16Type uses the v2 16-bit Lab encoding for
PCSLAB even inside a v4 profile. Using the v4 encoding there is a small, plausible-looking,
entirely wrong result. Uses the encodings from #321.

Per-intent tag selection

A2B0/A2B1/A2B2 and B2A0/B2A1/B2A2 map to perceptual / media-relative colorimetric /
saturation. Implement selection plus the fallback rule when the requested intent's tag is absent.

Research areas

  • Fallback order. ICC §8's required-tag models say which tags must be present per device
    class; they do not fully specify what a CMM does when an optional one is missing. Determine
    lcms2's actual order in third_party/lcms2/src/cmsio1.c (its input/output LUT readers) — in
    particular whether a missing intent falls back to intent 0 unconditionally.
  • Matrix-shaper vs LUT precedence. When a profile carries both (common in v4 display profiles),
    which does the CMM use, and does it depend on the requested intent? Interacts with gamut-cmm: matrix/TRC pipelines and chad / white-point conventions #327.
  • Non-conforming stage combinations — evaluate leniently or reject? gamut-icc chose lenient
    parsing; the engine can legitimately choose strict evaluation. Pick one, document it.
  • The lut8/lut16 3×3 matrix is only meaningful for XYZ PCS input; confirm the behaviour when it
    is present on a Lab-PCS profile.

Reference

ICC.1:2022 §10.10 (lut8Type), §10.11 (lut16Type), §10.12 (lutAToBType), §10.13 (lutBToAType),
§8 (required tags per class), Annex A (PCS encodings) — all in the vendored
references/icc/icc.1-2022-05.pdf. v2 Lab encoding: references/icc/icc.1-2001-04.pdf §6.3.4.2.

The stage orderings are also transcribed in references/icc/README.md.

Behavioral oracle: third_party/lcms2/src/cmsio1.c.

Oracle

lcms2 via #322 — the new v4 CMYK prtr with distinct A2B0/1/2 and B2A0/1/2 synthesizer exists
precisely so intent selection is observable, plus the scnr input-class camera profile and the
existing device-link synthesizers.

Acceptance criteria

  • All four LUT tag types assemble into a Pipeline with correct stage order, verified
    stage-by-stage against lcms2 with cmsFLAGS_NOOPTIMIZE.
  • A v4 profile carrying a lut16Type tag decodes its PCSLAB with the v2 encoding — asserted
    by a dedicated test, not incidentally.
  • Selecting each of the four intents on the CMYK prtr profile yields different output,
    matching lcms2 per intent.
  • Requesting an intent whose tag is absent follows the documented fallback and matches lcms2.
  • The stage-combination policy (lenient vs strict) is documented in STATUS.md with its
    rationale.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions