Skip to content

Commit 7acb792

Browse files
2.1.0: print QC analysis — extrema colorimetry, shadow ink paths, tone response
Three additions to the Analysis tab, derived from the doQCpfA.m reference QC harness and checked numerically against its published GRACoL2013_CRPC6 report. Extrema Colorimetry (new section) White/black point in relative AND absolute colorimetry, the inking the profile chooses for black, and TAC; plus per-hue full-tone vs maximum-chroma colorimetry. Black is derived per-intent (PCS black -> B2A -> inking -> A2B1), because each B2A table picks its own black inking. Two things established while building it, both worth keeping: - A bare CIccXform DOES apply the absolute media-white adjustment; it is set up in CIccXform::Begin() and applied by CheckDstAbs(), gated on m_bAdjustPCS && m_bInput. No CIccCmm needed (IccCmm.cpp:1568-1596). - Absolute == relative is legitimate, not a bug: the adjustment only engages when media white differs from the illuminant. The CMYRGB corner map is CMYK/CMY only, deliberately. An nCLR space names its channels through colorantTable in any order, so "channel 0 is cyan" would be a guess that mislabels every row while looking plausible. Those profiles get an explicit N/A. Ink Usage in Shadows (new section) Four constant-L* paths (0/45/90/135 deg) through the selected B2A table, with an intent selector. BPC follows the reference exactly, including sourcing the black point from B2A0 for all three intents so they stay comparable. Neutral Axis Inking Adds the tone response (L*-out vs L*-in). Its plateau at the darkest reproducible lightness IS the media black point — and it agrees to the digit with WhiteBlackPoints, which reaches the same number by an independent path. A Delta-a*b* neutrality trace is computed but WITHDRAWN from the plot pending review of its definition (SHOW_NEUTRALITY=false). It began as a full dE*ab; probing GRACoL showed the reference curve peaks at ~0.28 just above the black point and never blows up, matching chroma-only (0.283) rather than full dE (10.8 at L*=0, purely because below the media black point the profile can only clamp). That artefact would also have dominated the secondary axis. Model / engine New iccviz APIs: WhiteBlackPoints, HueExtrema, ShadowInkPaths. New Series::useY2 + Graph::hasY2/y2Axis — a general "this series carries a different physical quantity" concept, not a one-off. Shared inkColorHints() replaces the inline hint block so the separation plots colour identically. New WASM exports: whiteBlackPoints, hueExtrema, shadowInkPaths. Fixed a latent bug found on the way: axisRange() accepted min == max and would have collapsed any axis using the new "no hint / autorange" convention. UI Every Analysis section now starts collapsed — each costs a full transform pass, so opening is an explicit choice rather than six analyses firing on tab switch. The two new tables give every colorimetric axis its own column under a spanning group header, and scroll in their own box (7-8 columns would otherwise push the page sideways; this panel has no media queries). Docs Rewrote the guide's Analysis section, which had drifted independently of this work: it described Profile Statistics and Round-Trip as separate sections after they were merged, and never documented CLUT Image or Gamut Image. Adds qc-analysis-gaps.md recording the full Q1-Q17 audit against the reference harness and what remains (Q6-Q17). i18n 34 new strings across all 12 locales; spreadsheets re-synced. Verified White/black points, TAC, black inking and all six hue corners match the published GRACoL2013_CRPC6 figures to <=0.007. The shadow plane is an independent check: the reference rule gives 38.335 from the published corner values; the engine computed 38.33 from its own transforms. All WASM modules rebuilt from a CLEAN iccDEV origin/master worktree (0550c344). Note for the runbook: third_party/ is untracked vendored deps, so a clean worktree needs it symlinked in before build-wasm.sh will configure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkxVtREhRmqbY71R5KnV7e
1 parent dabb00a commit 7acb792

32 files changed

Lines changed: 2325 additions & 91 deletions

MANUAL.md

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -194,30 +194,71 @@ The summary row at the top tallies each verdict as a coloured pill. **Click a pi
194194

195195
### 3.4 Analysis
196196

197-
Whole-profile quality analyses derived from the profile's colour transforms — computed by a dedicated WebAssembly module (the iccDEV visualization engine) that's fetched only when you first open this tab, and plotted in the app's own style. Each analysis is a collapsible section; one that doesn't apply to the loaded profile shows a short *not applicable* note instead (for example, a matrix/TRC display profile has no device↔PCS CLUTs to analyse).
197+
Whole-profile quality analyses derived from the profile's colour transforms — computed by a dedicated WebAssembly module (the iccDEV visualization engine) that's fetched only when you first open this tab, and plotted in the app's own style.
198+
199+
Each analysis is a collapsible section and **all of them start closed**. That's deliberate: every section costs a full transform pass over the profile, so opening one is your explicit choice rather than six analyses firing the moment you switch tabs. Results are cached, so re-opening a section is instant. A section that doesn't apply to the loaded profile shows a short *not applicable* note instead (for example, a matrix/TRC display profile has no device↔PCS CLUTs to analyse).
198200

199201
#### Profile Statistics
200202

201-
Per rendering intent, two whole-profile metrics computed from the device↔PCS lookup tables:
203+
Whole-profile metrics for **one** selected rendering intent, driven by two listboxes and a checkbox:
204+
205+
- **Rendering intent** — Perceptual / Relative Colorimetric / Saturation / Absolute Colorimetric.
206+
- **Round-trip type** — which accuracy metric to report (see below).
207+
- **Use MPE (color) tags** — off = the colorimetric lookup tables; on = the multi-processing-element / colour tags. It has no effect on the in-gamut overview, and is disabled there.
208+
209+
The table shows the **gamut volume** (in ΔE\*ab³, enclosed by the device → PCS `A2B` transform, measured by voxelising the boundary and counting enclosed cells) alongside the selected round trip's **min / mean / std dev / P90 / max** ΔE\*ab. Below it sits a ΔE distribution histogram — relative frequency bars with a cumulative-frequency line — plus the worst-error `L, a, b` and, for PRMG, whether the profile implies the Perceptual Reference Medium Gamut.
210+
211+
The four round-trip types:
212+
213+
- **In-gamut overview (RT0)** — a device-value grid taken to PCS, back to device, and to PCS again; ΔE\*ab measured between the two PCS passes. A fast in-gamut stability check.
214+
- **Inversion + gamut (RT1)** — ΔE\*ab between each device colour's PCS and its PCS after one Lab → device → Lab round trip. Reflects inversion accuracy *and* gamut mapping.
215+
- **Reproducibility (RT2)** — ΔE\*ab between the first and second round trips: how stable a repeated round trip is, independent of the first trip's gamut clipping.
216+
- **PRMG interoperability** — PCS colours inside the Perceptual Reference Medium Gamut round-tripped once; the ΔE distribution indicates cross-profile interoperability.
202217

203-
- **Gamut volume** — the volume (in ΔE\*ab³) enclosed by the device → PCS (`A2B`) transform, measured by voxelising the gamut boundary and counting the enclosed cells. A robust estimate of how much colour the profile can reproduce.
204-
- **Round-trip ΔE** — how accurately the `B2A` (PCS → device) table inverts the `A2B`: in-gamut Lab values are pushed Lab → device → Lab and the error is reported as **mean**, **P90**, and **max** ΔE\*ab.
218+
A profile lacking the device↔PCS transforms a metric needs shows a *not applicable* note; a device space too wide to sample is reported as skipped rather than as an error.
205219

206-
One row per intent (Perceptual, Relative Colorimetric, Saturation, Absolute Colorimetric); intents whose tags are absent are omitted.
220+
#### Extrema Colorimetry
207221

208-
#### Round-Trip (PRMG)
222+
The ends of the profile's reproduction range — the numbers a print operator usually reads first.
209223

210-
The full round-trip report, matching the iccDEV `iccRoundTrip` reference tool exactly. Where the Profile Statistics row above gives a quick single-direction summary, this section runs the reference metric: it seeds from the **device colour cube** and reports **both** round-trip directions plus a Perceptual Reference Medium Gamut (PRMG) interoperability histogram.
224+
- **White point** — the colour of **zero colorant**, i.e. bare substrate.
225+
- **Black point** — found by pushing PCS black (L\*=0, a\*=b\*=0) through the selected `B2A` table to get the inking the profile *chooses* for black, then reading that inking back through `A2B1`.
226+
- Both are shown in **relative** and **absolute** colorimetry. Absolute shows the substrate's own colour (a blue-white paper reads as something like L\* 95, b\* −4); relative re-references it to a perfect white. If the two columns are identical, that's normal — the media white simply matches the illuminant. A profile with no media white point tag shows relative only.
227+
- **Inking at black point** and **TAC** — the per-channel ink at that black, and their sum: total area coverage, the ink-limit figure.
211228

212-
- **Round Trip 1** — the device → PCS → device error: ΔE\*ab between each device colour's Lab and its Lab after one round trip. Reported as **min**, **mean**, **max**, plus the worst-case `L, a, b`.
213-
- **Round Trip 2** — the PCS round-trip stability: ΔE\*ab between the first and second round trip, again with min / mean / max and worst-case `L, a, b`.
214-
- **PRMG interoperability** — the count and share of samples whose round-trip ΔE falls within **1, 2, 3, 5, and 10**, and whether the profile implies the Perceptual Reference Medium Gamut.
229+
Because each `B2A` table picks its own inking for black, the black point genuinely differs between intents — hence the rendering-intent selector.
215230

216-
Two controls drive it: a **rendering intent** selector (Perceptual / Relative / Saturation / Absolute) and a **Use MPE (color) tags** checkbox (off = the colorimetric lookup tables; on = the multi-processing-element / color tags). Results are computed on demand when you open the section and cached per intent and MPE setting. A profile that can't be round-tripped — one lacking the device↔PCS transforms this metric needs — shows a *not applicable* note; a device space too wide to sample is reported as skipped rather than as an error.
231+
Below that, **Full tone vs maximum chroma**: for each ink corner (C, M, Y, R, G, B and K), where it lands in hue / chroma / lightness, and the most chromatic point found on the way there from bare substrate. These rows are measured through `A2B1` and so **do not change with the intent selector**. On a well-behaved profile the two rows match. When maximum chroma arrives *before* full tone the row is flagged: past that point the extra ink is no longer adding chroma, only darkening.
232+
233+
**Output (printer) profiles only** — the analysis assumes zero colorant means bare substrate. The per-hue table additionally needs a **CMYK or CMY** device space: an n-colour (nCLR) profile names its channels in any order, so which channel is "cyan" cannot be known, and the table reports that rather than guessing.
217234

218235
#### Neutral Axis Inking
219236

220-
Sweeps the neutral axis (a\*=b\*=0) from white (L\*=100) down to black (L\*=0) through the profile's `B2A` (PCS → device) table and plots how much of each device colorant the profile lays down along the way — the classic GCR / neutral-build curve. One curve per device channel, colour-coded per colorant. **Output (printer) profiles only**; other profile classes show a note.
237+
Sweeps the neutral axis (a\*=b\*=0) from white (L\*=100) down to black (L\*=0) through the profile's `B2A` (PCS → device) table and plots how much of each device colorant the profile lays down along the way — the classic GCR / neutral-build curve. One curve per device channel, colour-coded per colorant.
238+
239+
A further curve is drawn over the separation:
240+
241+
- **L\* out (tone)** — the tone response: where the neutral axis actually lands in lightness after a round trip through the profile. Read two things from its shape. Sag below the diagonal means greys render darker than requested. More usefully, the curve **flattens at the darkest lightness the profile can reach** — that plateau *is* the media black point, and it should agree with the Extrema Colorimetry section above.
242+
243+
**Output (printer) profiles only**; other profile classes show a note.
244+
245+
#### Ink Usage in Shadows
246+
247+
Four straight paths across the a\*b\* plane at one constant, deliberately dark lightness — 0°, 45°, 90° and 135° — run through the selected `B2A` table, with the resulting separation plotted for each.
248+
249+
Because every sample on a path shares the same L\*, any abrupt step or reversal in a colorant comes from hue and chroma handling alone. That's the signature of a shadow gamut-mapping artefact, and it's the kind of thing that shows up in print as banding or a sudden colour shift in dark areas. The paths sweep from far outside the gamut, through it, and out the other side, so you also see where the profile starts clamping.
250+
251+
The lightness plane is chosen automatically: halfway between the profile's Blue corner and the darkest of C, M, Y, R and G. For the perceptual and saturation tables the lightness is first stretched from the media black point up to PCS black — those tables expect black there — and the section reports both the compensated plane and the raw one.
252+
253+
**Output (printer) profiles only**, and the same CMYK/CMY restriction as the per-hue table above (the plane is derived from the ink corners).
254+
255+
#### CLUT Image
256+
257+
The colour lookup table of a device↔PCS transform, tiled into an image, with a selector for which rendering-intent table to view (`A2B0``A2B3`, `B2A0``B2A3`, and the preview tables). Useful for spotting structural damage in a table at a glance. Zoom, pan and reset with the controls on the canvas.
258+
259+
#### Gamut Image
260+
261+
The gamut tag's in/out-of-gamut map: neutral where a PCS colour is reproducible, red where it falls outside the device gamut. Only present when the profile carries a `gamt` tag.
221262

222263
### 3.5 XML
223264

frontend/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "profiletool-frontend",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"description": "ICC profile viewer and validation UI",
55
"type": "module",
66
"scripts": {

0 commit comments

Comments
 (0)