From 6971ba35d53b7f55ad00f1129c0f7f4678a46b9e Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 29 Jan 2024 10:56:22 -0500 Subject: [PATCH] LibGfx+Tests: Support grayscale jpegs with 2x2 sampling and MCU reset Non-interleaved files always have an MCU of one data unit. (A "data unit" is an 8x8 tile of pixels, and an "MCU" is a "minium coded unit", e.g. 2x2 data units for luminance and 1 data unit each for Cr and Cb for a YCrCb image with 4:2:0 subsampling.) For the test case, I converted an existing image to a ppm: Build/lagom/bin/image -o out.ppm \ Tests/LibGfx/test-inputs/jpg/12-bit.jpg Then I converted it to grayscale and saved it as a pgm in Photoshop. Then I turned it into a weird jpeg like so: path/to/cjpeg \ -outfile Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg \ -sample 2x2 -restart 3 out.pgm Makes 3 of the 5 jpegs failing to decode at #22780 go. --- Tests/LibGfx/TestImageDecoder.cpp | 9 +++++++++ Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg | Bin 0 -> 3493 bytes .../Libraries/LibGfx/ImageFormats/JPEGLoader.cpp | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index d06992edf1..f0ccfe6170 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -417,6 +417,15 @@ TEST_CASE(test_jpeg_grayscale_with_app14) TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 80, 80 })); } +TEST_CASE(test_jpeg_grayscale_with_weird_mcu_and_reset_marker) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jpg/grayscale_mcu.jpg"sv))); + EXPECT(Gfx::JPEGImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::JPEGImageDecoderPlugin::create(file->bytes())); + + TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 320, 240 })); +} + TEST_CASE(test_jpeg_malformed_header) { Array test_inputs = { diff --git a/Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg b/Tests/LibGfx/test-inputs/jpg/grayscale_mcu.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c75b0f4f392f463258b01b592340e7461ce0cd68 GIT binary patch literal 3493 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 literal 0 HcmV?d00001 diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp index d3477f879f..a9b2aef925 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp @@ -1301,6 +1301,11 @@ static ErrorOr read_start_of_frame(JPEGStream& stream, JPEGLoadingContext& component.sampling_factors.horizontal = subsample_factors >> 4; component.sampling_factors.vertical = subsample_factors & 0x0F; + if (component_count == 1) { + // 4.8.2 Minimum coded unit: "If the compressed image data is non-interleaved, the MCU is defined to be one data unit." + component.sampling_factors = { 1, 1 }; + } + dbgln_if(JPEG_DEBUG, "Component subsampling: {}, {}", component.sampling_factors.horizontal, component.sampling_factors.vertical); if (i == 0) {