LibGfx/JPEG2000: Support jpx extended 'colr' boxes
The T.800 spec says there should only be one 'colr' box, but the extended jpx file format spec in T.801 annex M allows having multiple. Method 2 is a basic ICC profile, while method 3 (jpx-only) allows full ICC profiles. Support that. For the test, I opened buggie.png in Photoshop, converted it to grayscale, and saved it as a JPEG2000, with "JP2 Compatible" checked and "Include Transparency" unchecked. I also unchecked "Include Metadata", and "Lossless". I left "Fast Mode" checked and the quality at the default 50.
This commit is contained in:
parent
3f740fc727
commit
ab7da32d25
Notes:
sideshowbarker
2024-07-17 20:33:50 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/ab7da32d25 Pull-request: https://github.com/SerenityOS/serenity/pull/23770
4 changed files with 41 additions and 5 deletions
|
@ -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)));
|
||||
|
|
BIN
Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf
Normal file
BIN
Tests/LibGfx/test-inputs/jpeg2000/buggie-gray.jpf
Normal file
Binary file not shown.
|
@ -76,6 +76,19 @@ ErrorOr<void> 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());
|
||||
}
|
||||
|
||||
|
|
|
@ -108,9 +108,18 @@ static ErrorOr<void> 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<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*header_box.child_boxes()[i]);
|
||||
auto const& current_color_box = static_cast<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*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<void> decode_jpeg2000_header(JPEG2000LoadingContext& context, Rea
|
|||
context.size = { image_header_box.width, image_header_box.height };
|
||||
|
||||
auto const& color_header_box = static_cast<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*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 {};
|
||||
|
|
Loading…
Add table
Reference in a new issue