From 70e4d3a9b6a8b51c6c1bd4cabbc6048969a20b48 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Thu, 10 Aug 2023 15:48:49 -0400 Subject: [PATCH] LibGfx/JPEGXL: Support images with the `have_crop` option in FrameHeader This commit only allows us to read these values. The decoder will still output the image as if no cropping was requested. --- .../LibGfx/ImageFormats/JPEGXLLoader.cpp | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEGXLLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEGXLLoader.cpp index 28b116d11cc..af588b52de2 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEGXLLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEGXLLoader.cpp @@ -681,6 +681,10 @@ struct FrameHeader { u8 lf_level {}; bool have_crop { false }; + i32 x0 {}; + i32 y0 {}; + u32 width {}; + u32 height {}; BlendingInfo blending_info {}; FixedArray ec_blending_info {}; @@ -701,7 +705,9 @@ static int operator&(FrameHeader::Flags first, FrameHeader::Flags second) return static_cast(first) & static_cast(second); } -static ErrorOr read_frame_header(LittleEndianInputBitStream& stream, ImageMetadata const& metadata) +static ErrorOr read_frame_header(LittleEndianInputBitStream& stream, + SizeHeader size_header, + ImageMetadata const& metadata) { FrameHeader frame_header; bool const all_default = TRY(stream.read_bit()); @@ -744,15 +750,29 @@ static ErrorOr read_frame_header(LittleEndianInputBitStream& stream if (frame_header.frame_type != FrameHeader::FrameType::kLFFrame) frame_header.have_crop = TRY(stream.read_bit()); - if (frame_header.have_crop) - TODO(); + if (frame_header.have_crop) { + auto const read_crop_dimension = [&]() -> ErrorOr { + return U32(TRY(stream.read_bits(8)), 256 + TRY(stream.read_bits(11)), 2304 + TRY(stream.read_bits(14)), 18688 + TRY(stream.read_bits(30))); + }; + + if (frame_header.frame_type != FrameHeader::FrameType::kReferenceOnly) { + frame_header.x0 = unpack_signed(TRY(read_crop_dimension())); + frame_header.y0 = unpack_signed(TRY(read_crop_dimension())); + } + + frame_header.width = TRY(read_crop_dimension()); + frame_header.height = TRY(read_crop_dimension()); + } bool const normal_frame = frame_header.frame_type == FrameHeader::FrameType::kRegularFrame || frame_header.frame_type == FrameHeader::FrameType::kSkipProgressive; - // FIXME: also consider "cropped" image of the dimension of the frame - VERIFY(!frame_header.have_crop); - bool const full_frame = !frame_header.have_crop; + // Let full_frame be true if and only if have_crop is false or if the frame area given + // by width and height and offsets x0 and y0 completely covers the image area. + bool const cover_image_area = frame_header.x0 <= 0 && frame_header.y0 <= 0 + && (frame_header.width + frame_header.x0 >= size_header.width) + && (frame_header.height + frame_header.y0 == size_header.height); + bool const full_frame = !frame_header.have_crop || cover_image_area; if (normal_frame) { frame_header.blending_info = TRY(read_blending_info(stream, metadata, full_frame)); @@ -2304,13 +2324,14 @@ static ErrorOr read_frame(LittleEndianInputBitStream& stream, Frame frame; - frame.frame_header = TRY(read_frame_header(stream, metadata)); + frame.frame_header = TRY(read_frame_header(stream, size_header, metadata)); if (!frame.frame_header.have_crop) { frame.width = size_header.width; frame.height = size_header.height; } else { - TODO(); + frame.width = frame.frame_header.width; + frame.height = frame.frame_header.height; } if (frame.frame_header.upsampling > 1) {