Skip to content

1.7.1 — Image gamut

Choose a tag to compare

@colourbill-ctrl colourbill-ctrl released this 21 May 01:09
· 67 commits to main since this release

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