1.7.1 — Image gamut
Adds an Image tab in Compare mode that loads a real image file and plots its
colour gamut alongside the loaded Dataset A / Dataset B traces in the 3D plot
and 2D slice. Colorimetry is derived from the image's embedded ICC profile
when one is present, and falls back to channel-count inference otherwise.
Lab TIFFs plot in their native Lab* coordinates with no transformation.
Supported image formats
- PNG Embedded iCCP profile honoured (zlib-inflated via
DecompressionStream); Gray and RGB via Canvas. - JPEG Embedded APP2 ICC profile honoured. Gray/RGB via Canvas; CMYK
decoded via UTIF's bundled JpegDecoder (PDF.js lineage), which
handles Adobe APP14 YCCK and emits CMYK in normal orientation. - TIFF Embedded ICC profile (tag 34675); 8 / 16-bit Gray, RGB, CMYK,
and NCLR. PhotometricInterpretation 8 / 9 (CIELAB / ICCLab) is
auto-detected and plotted as Lab. - BMP Indexed and 16 / 24 / 32-bit decoded as RGB via Canvas. V5 ICC
tag not yet read. - GIF Palette → RGB via Canvas; first frame only for animated GIFs.
(GIF has no embedded ICC support in the format.) - PDF Photoshop-generated PDFs only — strict 'Adobe Photoshop' check
on /Info Producer or Creator. Single-page; first image XObject
extracted. Linearized PDFs are walked via the /Prev xref chain.
Filters: /DCTDecode (incl. CMYK via UTIF) and /FlateDecode with
TIFF Predictor 2 or PNG predictors (10-15). Embedded ICC via
/ICCBased on the image, OR via page-level /DefaultGray,
/DefaultRGB, /DefaultCMYK colour-space override on a Device*-
tagged image (PDF spec §8.6.5.6).
Embedded ICC profile dropdown
When an image carries an embedded ICC profile, the bind dropdown prepends an
'Embedded: ' entry and selects it by default. Conversion runs
the image's device pixels through the embedded profile via lcms2 (Absolute
Colorimetric) directly — no dataset binding required, and no family/channel
compatibility check applies (the profile is the image's colour space by
construction). The user can still pick Dataset A or B with the existing
compatibility check.
UI
The Image section sits above the 3D plot in Compare mode (collapsed by
default; hidden in Explore mode). Dark-mode styling matches the existing
plot / slice sections. Status messages now render outside the
display:none image-info wrapper so format-unsupported / PDF-rejected /
file-too-large errors are visible regardless of decode success.
Plumbing
- inflateAsync() thin wrapper over native DecompressionStream('deflate'),
used by PNG iCCP and PDF /FlateDecode (image streams +
/ICCBased ICC streams). Lifts the prior PNG iCCP stub
that returned null. - Minimal PDF parser (~300 lines) — classic xref tables, indirect-ref
resolver, dict/array/string lexer, /Prev chain following. Handles
Photoshop's linearized output (xref at offset 116, /Prev to the small
auxiliary table at end of file). - decodeCmykJpegViaUtif() shared CMYK decode for standalone .jpg files
and PDF /DCTDecode CMYK streams. CMYK thumb
preview via (1-C)(1-K), (1-M)(1-K), (1-Y)(1-K).
i18n
29 new image_* keys added to all 12 locale dictionaries and the nine
Eng-*.xlsx translation spreadsheets. image_format_unsupported lists every
supported format. image_err_cmyk_jpeg removed (CMYK JPEGs are now
supported). check-translations audit clean.
Server
CSP img-src extended to allow blob: so URL.createObjectURL() works for
image thumbnails. script-src unchanged. The icctools postMessage handoff
is unchanged.
Docs
MANUAL.md §5.4 documents the format table, Photoshop-PDF scope, and the
embedded-ICC parity across formats.
Post-tag translation polish
Three follow-up commits address language-switch gaps discovered after the
initial 1.7.1 tag was cut. Re-tagged in place so 1.7.1 is the single
shipping artifact for the entire image-gamut feature line.
- Image tab strings written imperatively (colour-space line, points
summary, status messages, "Embedded: …" dropdown entry) now re-render
on language change via renderImageInfo() / repopulateImageBindDropdown()
/ bindImageToCurrentSelection() inside onLangChange(). - 18 previously untranslated strings ported to t(): file-select handles,
Contact button, file-card delete tooltip, rendering-intent dropdown
(label + 4 options) and ICC summary panel labels, Settings language
"System default" entry, ICC load/parse error prefixes, popup-blocked
alert, file-load failure alerts. New data-i18n-aria attribute handler
added to applyTranslations() for aria-labels. - Estimate tab "ICC A2B Evaluation" / "ICC Output", Settings language
"System default (…)", and Compare table no-match error message now all
re-translate on language switch. - scripts/generate-help.js TOC updated to include 5.4 Image gamut so the
in-app Help page links to the section. - All translation spreadsheets (nine Eng-*.xlsx) carry the new rows;
scripts/check-translations.js reports zero drift.
Image detail (Coarse / Standard / Fine)
The Image tab now exposes an "Image detail" dropdown driving the
spatial-bin and dedupe-radius parameters used by the gamut pipeline.
Persists per browser via localStorage; Standard is the default.
- Coarse bins=900 radius=2.0 fastest, sparser trace
- Standard bins=2025 radius=1.0 default
- Fine bins=5000 radius=0.5 denser trace, slower
Changing the level re-runs binImage + dedupeRadial on the cached decoded
pixels and re-binds — no source-file re-decode. Four new image_detail_*
i18n keys added to all 12 locale dictionaries and the nine Eng-*.xlsx
spreadsheets. The opening of MANUAL.md now also calls out the icctools
companion and the in-app Launch editor hand-off.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com