diff --git a/crates/jpeg/README.md b/crates/jpeg/README.md index 63afb834..f403f454 100644 --- a/crates/jpeg/README.md +++ b/crates/jpeg/README.md @@ -112,6 +112,8 @@ SOF11 (lossless) falls in that range and returns `Unsupported`. - `tests/conformance.rs`, on libjpeg-turbo's test images in `tests/data` (provenance and how the derived files were made: `tests/data/SOURCE.md`): - `testimgari.jpg` (SOF9) decodes to exactly the samples of its jpegtran Huffman transcode. + - jpegtran's arithmetic and progressive arithmetic transcodes of `testorig.jpg` (one with + restarts) and `testorig12.jpg` decode to exactly the samples of their sources. - `whitetail-ari.jpg` (SOF9 from cjpeg, a white lower part that decodes from the zeros supplied at EOI) decodes undamaged and equal to its Huffman transcode. - `testorig.jpg` matches `zune-jpeg`, and `testorig12.jpg` matches libjpeg-turbo's 12-bit diff --git a/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive-restart.jpg b/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive-restart.jpg new file mode 100644 index 00000000..10c5081f Binary files /dev/null and b/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive-restart.jpg differ diff --git a/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive.jpg b/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive.jpg new file mode 100644 index 00000000..e814c929 Binary files /dev/null and b/crates/jpeg/fuzz/corpus/decode/testorig-ari-progressive.jpg differ diff --git a/crates/jpeg/fuzz/corpus/decode/testorig12-ari-progressive.jpg b/crates/jpeg/fuzz/corpus/decode/testorig12-ari-progressive.jpg new file mode 100644 index 00000000..3a716ea7 Binary files /dev/null and b/crates/jpeg/fuzz/corpus/decode/testorig12-ari-progressive.jpg differ diff --git a/crates/jpeg/fuzz/corpus/decode/testorig12-ari.jpg b/crates/jpeg/fuzz/corpus/decode/testorig12-ari.jpg new file mode 100644 index 00000000..66478ad5 Binary files /dev/null and b/crates/jpeg/fuzz/corpus/decode/testorig12-ari.jpg differ diff --git a/crates/jpeg/tests/conformance.rs b/crates/jpeg/tests/conformance.rs index e9fab449..86b9dfb0 100644 --- a/crates/jpeg/tests/conformance.rs +++ b/crates/jpeg/tests/conformance.rs @@ -41,6 +41,21 @@ fn arithmetic_coded_image_matches_its_huffman_transcode() { assert!(huffman.samples == arithmetic.samples, "testimgari.jpg decodes differently from testimgari-huffman.jpg"); } +/// The arithmetic and progressive arithmetic transcodes jpegtran made of testorig.jpg and +/// testorig12.jpg hold their coefficients, so they decode to exactly the same samples. +#[test] +fn arithmetic_transcodes_match_their_huffman_sources() { + for (source, transcodes) in [ + ("testorig.jpg", &["testorig-ari-progressive.jpg", "testorig-ari-progressive-restart.jpg"]), + ("testorig12.jpg", &["testorig12-ari.jpg", "testorig12-ari-progressive.jpg"]), + ] { + let reference = decode_fixture(source); + for name in transcodes { + assert!(decode_fixture(name).samples == reference.samples, "{name} decodes differently from {source}"); + } + } +} + /// libjpeg's arithmetic encoder drops the trailing zero bytes of a scan, so the white lower /// part of whitetail-ari.jpg decodes from zeros supplied at the EOI marker. It must decode /// whole and match its Huffman transcode. diff --git a/crates/jpeg/tests/data/SHA256SUMS b/crates/jpeg/tests/data/SHA256SUMS index 2309d1b5..19c71c54 100644 --- a/crates/jpeg/tests/data/SHA256SUMS +++ b/crates/jpeg/tests/data/SHA256SUMS @@ -5,3 +5,7 @@ acc6ec555d41d15b368320edaa3b20958ee6fa97cb6e4a18d1213d5ae8bec73b testorig.jpg 62d182ed042e243544d3d753c440ada1a6992464717311fe6efd546632f44dc4 testorig12.ppm 0031b82a06300b5b5d19ef5553cc39d3fafafa4fbc474199e6cfbc7818312a87 whitetail-ari.jpg 0f6e17a44be7a005d6610e66f91fa119388dc6b8438e95cb8816b6db58f8c365 whitetail-huffman.jpg +7e7edd9d9825c2eca1e6ff6dfafeaddec39e2464fc5d6d1ce4a378a59ec3f716 testorig-ari-progressive.jpg +cb0a2c60e6a89c68ef8cf48854dc09c77c26ffb27ac62e17d343d98f1424cc77 testorig-ari-progressive-restart.jpg +c4942c4b704d20e27c4cd3e613c6ef975dd7cfe53a756e2c209fa3ad033d8b51 testorig12-ari.jpg +18df0394115d9f8751939fb9f45a0d330df2ca849e9f8b07d0af76c09b90fcdb testorig12-ari-progressive.jpg diff --git a/crates/jpeg/tests/data/SOURCE.md b/crates/jpeg/tests/data/SOURCE.md index 8e916998..bc15d764 100644 --- a/crates/jpeg/tests/data/SOURCE.md +++ b/crates/jpeg/tests/data/SOURCE.md @@ -13,12 +13,17 @@ libjpeg-turbo's tools, as described below. | `testimgari-huffman.jpg` | `testimgari.jpg` losslessly transcoded to baseline Huffman (SOF0) | | `testorig12.jpg` | 227×149 YCbCr, 12-bit extended sequential Huffman (SOF1) | | `testorig12.ppm` | libjpeg-turbo's decode of `testorig12.jpg`: 12-bit RGB, binary PPM, maxval 4095 | +| `testorig-ari-progressive.jpg` | `testorig.jpg` transcoded to progressive arithmetic (SOF10) | +| `testorig-ari-progressive-restart.jpg` | the same with a restart interval of 3 MCU rows | +| `testorig12-ari.jpg` | `testorig12.jpg` transcoded to 12-bit arithmetic (SOF9) | +| `testorig12-ari-progressive.jpg` | `testorig12.jpg` transcoded to 12-bit progressive arithmetic (SOF10) | | `whitetail-ari.jpg` | 512×512 gray, arithmetic coded (SOF9), noise in the top 16 rows, white below | | `whitetail-huffman.jpg` | `whitetail-ari.jpg` losslessly transcoded to baseline Huffman (SOF0) | `testimgari.jpg` and `testorig.jpg` share their quantization tables but not their coefficients: libjpeg-turbo decodes them to different pixels. `testimgari-huffman.jpg` holds exactly the -coefficients of `testimgari.jpg`, so the two must decode to identical samples. +coefficients of `testimgari.jpg`, so the two must decode to identical samples. The same holds +for each jpegtran transcode below and its source. ## How the derived files were made @@ -29,6 +34,16 @@ coefficients of `testimgari.jpg`, so the two must decode to identical samples. jpegtran -copy none testimgari.jpg > testimgari-huffman.jpg ``` +The arithmetic transcodes of `testorig.jpg` and `testorig12.jpg`, with jpegtran from +libjpeg-turbo 3.1.3 (Alpine 3.24, `libjpeg-turbo-utils`): + +```sh +jpegtran -copy none -arithmetic -progressive testorig.jpg > testorig-ari-progressive.jpg +jpegtran -copy none -arithmetic -progressive -restart 3 testorig.jpg > testorig-ari-progressive-restart.jpg +jpegtran -copy none -arithmetic testorig12.jpg > testorig12-ari.jpg +jpegtran -copy none -arithmetic -progressive testorig12.jpg > testorig12-ari-progressive.jpg +``` + `testorig12.ppm`, with libjpeg-turbo 3.2.0 through imagecodecs 2026.10.10 (default settings: integer IDCT `JDCT_ISLOW`, fancy upsampling), run as `uv run --with imagecodecs --with numpy python make.py testorig12.jpg testorig12.ppm`: diff --git a/crates/jpeg/tests/data/testorig-ari-progressive-restart.jpg b/crates/jpeg/tests/data/testorig-ari-progressive-restart.jpg new file mode 100644 index 00000000..10c5081f Binary files /dev/null and b/crates/jpeg/tests/data/testorig-ari-progressive-restart.jpg differ diff --git a/crates/jpeg/tests/data/testorig-ari-progressive.jpg b/crates/jpeg/tests/data/testorig-ari-progressive.jpg new file mode 100644 index 00000000..e814c929 Binary files /dev/null and b/crates/jpeg/tests/data/testorig-ari-progressive.jpg differ diff --git a/crates/jpeg/tests/data/testorig12-ari-progressive.jpg b/crates/jpeg/tests/data/testorig12-ari-progressive.jpg new file mode 100644 index 00000000..3a716ea7 Binary files /dev/null and b/crates/jpeg/tests/data/testorig12-ari-progressive.jpg differ diff --git a/crates/jpeg/tests/data/testorig12-ari.jpg b/crates/jpeg/tests/data/testorig12-ari.jpg new file mode 100644 index 00000000..66478ad5 Binary files /dev/null and b/crates/jpeg/tests/data/testorig12-ari.jpg differ