From be5e7a360f2e9119262719d7011b5761ddd8f518 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Wed, 6 Mar 2024 00:32:28 -0500 Subject: [PATCH] LibGfx/CCITT: Add support for images with an unknown number of lines --- .../LibGfx/ImageFormats/CCITTDecoder.cpp | 59 +++++++++++++------ Userland/Libraries/LibPDF/Filter.cpp | 2 +- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp index cffd9b3ecb..ef9d7a5272 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp @@ -392,10 +392,12 @@ constexpr Array node_codes = to_array({ { 7, Mode::Vertical_L3, 0b0000010 }, }); -ErrorOr read_mode(BigEndianInputBitStream& input_bit_stream) +using InvalidResult = u8; + +ErrorOr> read_mode(BigEndianInputBitStream& input_bit_stream) { u8 size {}; - u16 potential_code {}; + u8 potential_code {}; while (size < 7) { potential_code <<= 1; potential_code |= TRY(input_bit_stream.read_bit()); @@ -405,7 +407,7 @@ ErrorOr read_mode(BigEndianInputBitStream& input_bit_stream) return *maybe_mode; } - return Error::from_string_literal("CCITTDecoder: Unable to find the correct mode"); + return Variant(InvalidResult { potential_code }); } enum class Search : u8 { @@ -413,9 +415,14 @@ enum class Search : u8 { B2, }; -ErrorOr decode_single_ccitt_2d_line(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, ReferenceLine&& reference_line, u32 image_width) -{ +struct CCITTStatus { ReferenceLine current_line {}; + bool has_reached_eol { false }; +}; + +ErrorOr decode_single_ccitt_2d_line(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, ReferenceLine&& reference_line, u32 image_width) +{ + CCITTStatus status {}; Color current_color { ccitt_white }; u32 column {}; u32 remainder_from_pass_mode {}; @@ -453,15 +460,30 @@ ErrorOr decode_single_ccitt_2d_line(BigEndianInputBitStream& inpu current_color = invert(current_color); remainder_from_pass_mode = 0; - TRY(current_line.try_empend(current_color, column)); + TRY(status.current_line.try_empend(current_color, column)); return {}; }; while (column < image_width) { - auto const mode = TRY(read_mode(input_bit_stream)); + auto const maybe_mode = TRY(read_mode(input_bit_stream)); + + if (maybe_mode.has()) { + auto const partially_read_eol = maybe_mode.get(); + + if (partially_read_eol != 0) + return Error::from_string_literal("CCITTDecoder: Unable to find the correct mode"); + + auto const remaining_eol = TRY(input_bit_stream.read_bits(5)); + if (remaining_eol != 1) + return Error::from_string_literal("CCITTDecoder: Unable to find the correct mode"); + + // We reached EOL + status.has_reached_eol = true; + break; + } // Behavior are described here 4.2.1.3.2 Coding modes. - switch (mode.mode) { + switch (maybe_mode.get().mode) { case Mode::Pass: { auto const column_before = column; // We search for b1. @@ -515,7 +537,7 @@ ErrorOr decode_single_ccitt_2d_line(BigEndianInputBitStream& inpu TRY(decoded_bits.align_to_byte_boundary()); - return current_line; + return status; } ErrorOr decode_single_ccitt3_2d_block(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, u32 image_width, u32 image_height, Group3Options::UseFillBits use_fill_bits) @@ -528,7 +550,7 @@ ErrorOr decode_single_ccitt3_2d_block(BigEndianInputBitStream& input_bit_s if (next_is_1D) reference_line = TRY(decode_single_ccitt3_1d_line(input_bit_stream, decoded_bits, image_width)); else - reference_line = TRY(decode_single_ccitt_2d_line(input_bit_stream, decoded_bits, move(reference_line), image_width)); + reference_line = TRY(decode_single_ccitt_2d_line(input_bit_stream, decoded_bits, move(reference_line), image_width)).current_line; } return {}; @@ -590,20 +612,21 @@ ErrorOr decode_ccitt_group4(ReadonlyBytes bytes, u32 image_width, u3 auto strip_stream = make(bytes); auto bit_stream = make(MaybeOwned(*strip_stream)); - // Note: We put image_height extra-space to handle at most one alignment to byte boundary per line. - ByteBuffer decoded_bytes = TRY(ByteBuffer::create_zeroed(ceil_div(image_width * image_height, 8) + image_height)); - auto output_stream = make(decoded_bytes.bytes()); + auto output_stream = make(); auto decoded_bits = make(MaybeOwned(*output_stream)); // T.6 2.2.1 Principle of the coding scheme // The reference line for the first coding line in a page is an imaginary white line. - ReferenceLine reference_line; - TRY(reference_line.try_empend(ccitt_black, image_width)); + CCITTStatus status; + TRY(status.current_line.try_empend(ccitt_black, image_width)); - for (u32 i = 0; i < image_height; ++i) - reference_line = TRY(decode_single_ccitt_2d_line(*bit_stream, *decoded_bits, move(reference_line), image_width)); + u32 i {}; + while (!status.has_reached_eol && (image_height == 0 || i < image_height)) { + status = TRY(decode_single_ccitt_2d_line(*bit_stream, *decoded_bits, move(status.current_line), image_width)); + ++i; + } - return decoded_bytes; + return output_stream->read_until_eof(); } } diff --git a/Userland/Libraries/LibPDF/Filter.cpp b/Userland/Libraries/LibPDF/Filter.cpp index 4e5aa8d850..616e9e55ef 100644 --- a/Userland/Libraries/LibPDF/Filter.cpp +++ b/Userland/Libraries/LibPDF/Filter.cpp @@ -316,7 +316,7 @@ PDFErrorOr Filter::decode_ccitt(ReadonlyBytes bytes, RefPtr 0 || rows == 0) + if (require_end_of_line || encoded_byte_align || damaged_rows_before_error > 0) return Error::rendering_unsupported_error("Unimplemented option for the CCITTFaxDecode Filter"); ByteBuffer decoded {};