diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 7321ea11b32..75592ee97d2 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -584,6 +584,20 @@ TEST_CASE(test_jpeg2000_simple) EXPECT_EQ(icc_bytes->size(), 3144u); } +TEST_CASE(test_jpeg2000_gray) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jpeg2000/buggie-gray.jpf"sv))); + EXPECT(Gfx::JPEG2000ImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::JPEG2000ImageDecoderPlugin::create(file->bytes())); + + EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(64, 138)); + + // The file contains both a simple and a real profile. Make sure we get the bigger one. + auto icc_bytes = MUST(plugin_decoder->icc_data()); + EXPECT(icc_bytes.has_value()); + EXPECT_EQ(icc_bytes->size(), 912u); +} + TEST_CASE(test_pam_rgb) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("pnm/2x1.pam"sv))); diff --git a/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf b/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf new file mode 100644 index 00000000000..9203eaf6a04 Binary files /dev/null and b/Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf differ diff --git a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp index 2fa8760b7b5..f44731e5167 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.cpp @@ -76,6 +76,19 @@ ErrorOr JPEG2000ColorSpecificationBox::read_from_stream(BoxStream& stream) TRY(stream.read_until_filled(local_icc_data)); icc_data = move(local_icc_data); } + + // T.801 JPX extended file format syntax, + // Table M.22 – Legal METH values + if (method == 3) { + ByteBuffer local_icc_data = TRY(ByteBuffer::create_uninitialized(stream.remaining())); + TRY(stream.read_until_filled(local_icc_data)); + icc_data = move(local_icc_data); + } + if (method == 4) + return Error::from_string_literal("Method 4 is not yet implemented"); + if (method == 5) + return Error::from_string_literal("Method 5 is not yet implemented"); + return {}; } @@ -87,7 +100,7 @@ void JPEG2000ColorSpecificationBox::dump(String const& prepend) const outln("{}- approximation = {}", prepend, approximation); if (method == 1) outln("{}- enumerated_color_space = {}", prepend, enumerated_color_space); - if (method == 2) + if (method == 2 || method == 3) outln("{}- icc_data = {} bytes", prepend, icc_data.size()); } diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp index fc1236f5f87..9860094663b 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp @@ -108,9 +108,18 @@ static ErrorOr decode_jpeg2000_header(JPEG2000LoadingContext& context, Rea image_header_box_index = i; } if (subbox->box_type() == ISOBMFF::BoxType::JPEG2000ColorSpecificationBox) { - if (color_header_box_index.has_value()) - return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple Color Specification boxes"); - color_header_box_index = i; + // T.800 says there should be just one 'colr' box, but T.801 allows several and says to pick the one with highest precedence. + bool use_this_color_box; + if (!color_header_box_index.has_value()) { + use_this_color_box = true; + } else { + auto const& new_header_box = static_cast(*header_box.child_boxes()[i]); + auto const& current_color_box = static_cast(*header_box.child_boxes()[color_header_box_index.value()]); + use_this_color_box = new_header_box.precedence > current_color_box.precedence; + } + + if (use_this_color_box) + color_header_box_index = i; } } @@ -123,7 +132,7 @@ static ErrorOr decode_jpeg2000_header(JPEG2000LoadingContext& context, Rea context.size = { image_header_box.width, image_header_box.height }; auto const& color_header_box = static_cast(*header_box.child_boxes()[color_header_box_index.value()]); - if (color_header_box.method == 2) + if (color_header_box.method == 2 || color_header_box.method == 3) context.icc_data = color_header_box.icc_data.bytes(); return {};