Skip to content

feat(jpeg)!: migrate JPEG to gamut-jpeg; delete jpegli stack and img-parts - #52

Merged
justin13888 merged 1 commit into
masterfrom
31-migrate-jpeg-gamut
Jul 19, 2026
Merged

feat(jpeg)!: migrate JPEG to gamut-jpeg; delete jpegli stack and img-parts#52
justin13888 merged 1 commit into
masterfrom
31-migrate-jpeg-gamut

Conversation

@justin13888

Copy link
Copy Markdown
Collaborator

Closes #31.

Stacked on #51; merge order #42 → … → #51 → this. This was the last v1 hard-blocker migration (gamut#28 landed upstream 2026-07-17).

What is Delivered

  • Decode on gamut-jpeg (baseline + progressive). 4-component streams route through gamut's Cmyk8 surface into a CMYK→RGB conversion verified bit-exact against the old zune backend's formula; grayscale/YCbCr via Rgb8; probe via gamut's header-only info().
  • Encode collapses jpeg-encoder + the entire vendored jpegli stack onto gamut-jpeg: one JpegEncodeConfig (quality/subsampling/progressive/restart/density — exactly gamut's knobs); EncodeOptions::Jpeg; codec id jpeg/gamut. Deleted: codecs/jpegli.rs + C++ shim, the build.rs jpegli slice, the third_party/jpegli submodule (and with it .gitmodules), zune-jpeg/jpeg-encoder deps, all four crate build-dependencies (bindgen/pkg-config/cc/cmake), and the CI submodule-checkout step.
  • Metadata both ways through gamut — extraction via gamut_jpeg::metadata() (now surfacing ICC + XMP where the old path read EXIF only), embedding via encoder setters. That retired the last three img-parts consumers: img-parts and the container-embed feature are fully deleted (audited: WebP never used it; AVIF splices in-crate; PNG/JXL ride gamut encoders).
  • New test_jpeg_metadata_roundtrip pins EXIF+ICC+XMP encode→extract.

Quality baseline (issue requirement; q90 re-encode of the three root fixtures)

At the apples-to-apples 4:4:4 setting gamut matches the old backend within ~1 % output size at comparable PSNR; gamut's 4:2:0 default trades ~35 % smaller files. Full table in the work log. jpegli perceptual parity remains upstream (gamut#19/#29/#30).

Honest regressions, documented on the config

quality: 0 no longer remaps to 90; default subsampling is now 4:2:0; the old decoder's max-dimension DoS-guard knobs have no gamut equivalent; gamut decode is currently slower than zune. The latter two are filed upstream (see follow-up issues).

Verification Done

Run independently after implementation: cargo test --workspace 384/0; --features full 541/0 (fixture suite 22/22 on the gamut path); serde config passes; clippy workspace + full -D warnings 0+0; --no-default-features --features jpeg standalone clean; greps confirm zune-jpeg (direct), jpeg-encoder, img-parts, jpegli, and .gitmodules are gone from tree, manifests, and lockfile. lefthook green.

…parts

JPEG decode moves from zune-jpeg to gamut_jpeg::JpegDecoder (baseline +
progressive). Four-component streams route through gamut's Cmyk8 surface into
a CMYK->RGB conversion verified bit-exact against the old zune backend;
grayscale/YCbCr go through Rgb8; 16-bit scaling unchanged. probe_jpeg uses
gamut's header-only info(), keeping the hand-rolled SOF scan solely for
featureless builds where probing is documented to work.

JPEG encode collapses jpeg-encoder AND the entire vendored jpegli stack onto
gamut-jpeg: one JpegEncodeConfig with exactly gamut's knobs (quality,
subsampling, progressive, restart interval, density);
EncodeOptions::JpegJpegEnc/JpegJpegli collapse to Jpeg; codec id jpeg/gamut.
Deleted: codecs/jpegli.rs + C++ shim, the build.rs jpegli slice (build.rs
retains only cfg-alias machinery), the third_party/jpegli submodule and with
it .gitmodules, deps zune-jpeg/jpeg-encoder, all four build-dependencies
(bindgen/pkg-config/cc/cmake at the crate level), the jpeg backend features,
and EncodeError::Jpeg/Jpegli.

APP-segment metadata rides gamut both ways: extraction via
gamut_jpeg::metadata() — which now also surfaces ICC and XMP into
ImageMetadata where the old path read EXIF only — and embedding via the
encoder's with_exif/with_icc_profile/with_xmp. That retired the last three
img-parts consumers, so img-parts and the container-embed feature are fully
deleted (audit: WebP muxes via its own libwebp RIFF path and never used it;
AVIF splices in-crate; PNG/JXL embed via gamut encoders).

Quality baseline vs the old backends (q90 re-encode of the three root
fixtures): at the apples-to-apples 4:4:4 setting gamut matches the old
output within ~1% size at comparable PSNR; gamut's 4:2:0 default trades
~35% smaller files. jpegli perceptual parity remains upstream
(gamut#19/#29/#30). Known regressions documented on the config: quality 0 no
longer remaps to 90, default subsampling is 4:2:0, and the old decoder's
max-dimension DoS-guard knobs have no gamut equivalent yet (upstream ask
filed; decode is also currently slower than zune — perf issue filed).

BREAKING CHANGE: JPEG config/variant/codec-id renames; ExifContainer::Jpeg
removed; container-embed feature and img-parts gone; jpegli vendored
toolchain gone.
Base automatically changed from 29-hwdec-vaapi-backend to master July 19, 2026 01:44
@justin13888
justin13888 merged commit 08ab25f into master Jul 19, 2026
@justin13888
justin13888 deleted the 31-migrate-jpeg-gamut branch July 19, 2026 01:48
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.

Migrate JPEG to gamut

1 participant