1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:17:44 +00:00

LibGfx/CCITT: Add support for images with an unknown number of lines

This commit is contained in:
Lucas CHOLLET 2024-03-06 00:32:28 -05:00 committed by Tim Flynn
parent cefb2d65d8
commit be5e7a360f
2 changed files with 42 additions and 19 deletions

View file

@ -392,10 +392,12 @@ constexpr Array node_codes = to_array<ModeCode>({
{ 7, Mode::Vertical_L3, 0b0000010 }, { 7, Mode::Vertical_L3, 0b0000010 },
}); });
ErrorOr<ModeCode> read_mode(BigEndianInputBitStream& input_bit_stream) using InvalidResult = u8;
ErrorOr<Variant<ModeCode, InvalidResult>> read_mode(BigEndianInputBitStream& input_bit_stream)
{ {
u8 size {}; u8 size {};
u16 potential_code {}; u8 potential_code {};
while (size < 7) { while (size < 7) {
potential_code <<= 1; potential_code <<= 1;
potential_code |= TRY(input_bit_stream.read_bit()); potential_code |= TRY(input_bit_stream.read_bit());
@ -405,7 +407,7 @@ ErrorOr<ModeCode> read_mode(BigEndianInputBitStream& input_bit_stream)
return *maybe_mode; return *maybe_mode;
} }
return Error::from_string_literal("CCITTDecoder: Unable to find the correct mode"); return Variant<ModeCode, InvalidResult>(InvalidResult { potential_code });
} }
enum class Search : u8 { enum class Search : u8 {
@ -413,9 +415,14 @@ enum class Search : u8 {
B2, B2,
}; };
ErrorOr<ReferenceLine> decode_single_ccitt_2d_line(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, ReferenceLine&& reference_line, u32 image_width) struct CCITTStatus {
{
ReferenceLine current_line {}; ReferenceLine current_line {};
bool has_reached_eol { false };
};
ErrorOr<CCITTStatus> 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 }; Color current_color { ccitt_white };
u32 column {}; u32 column {};
u32 remainder_from_pass_mode {}; u32 remainder_from_pass_mode {};
@ -453,15 +460,30 @@ ErrorOr<ReferenceLine> decode_single_ccitt_2d_line(BigEndianInputBitStream& inpu
current_color = invert(current_color); current_color = invert(current_color);
remainder_from_pass_mode = 0; remainder_from_pass_mode = 0;
TRY(current_line.try_empend(current_color, column)); TRY(status.current_line.try_empend(current_color, column));
return {}; return {};
}; };
while (column < image_width) { 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<InvalidResult>()) {
auto const partially_read_eol = maybe_mode.get<InvalidResult>();
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. // Behavior are described here 4.2.1.3.2 Coding modes.
switch (mode.mode) { switch (maybe_mode.get<ModeCode>().mode) {
case Mode::Pass: { case Mode::Pass: {
auto const column_before = column; auto const column_before = column;
// We search for b1. // We search for b1.
@ -515,7 +537,7 @@ ErrorOr<ReferenceLine> decode_single_ccitt_2d_line(BigEndianInputBitStream& inpu
TRY(decoded_bits.align_to_byte_boundary()); TRY(decoded_bits.align_to_byte_boundary());
return current_line; return status;
} }
ErrorOr<void> decode_single_ccitt3_2d_block(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, u32 image_width, u32 image_height, Group3Options::UseFillBits use_fill_bits) ErrorOr<void> 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<void> decode_single_ccitt3_2d_block(BigEndianInputBitStream& input_bit_s
if (next_is_1D) if (next_is_1D)
reference_line = TRY(decode_single_ccitt3_1d_line(input_bit_stream, decoded_bits, image_width)); reference_line = TRY(decode_single_ccitt3_1d_line(input_bit_stream, decoded_bits, image_width));
else 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 {}; return {};
@ -590,20 +612,21 @@ ErrorOr<ByteBuffer> decode_ccitt_group4(ReadonlyBytes bytes, u32 image_width, u3
auto strip_stream = make<FixedMemoryStream>(bytes); auto strip_stream = make<FixedMemoryStream>(bytes);
auto bit_stream = make<BigEndianInputBitStream>(MaybeOwned<Stream>(*strip_stream)); auto bit_stream = make<BigEndianInputBitStream>(MaybeOwned<Stream>(*strip_stream));
// Note: We put image_height extra-space to handle at most one alignment to byte boundary per line. auto output_stream = make<AllocatingMemoryStream>();
ByteBuffer decoded_bytes = TRY(ByteBuffer::create_zeroed(ceil_div(image_width * image_height, 8) + image_height));
auto output_stream = make<FixedMemoryStream>(decoded_bytes.bytes());
auto decoded_bits = make<BigEndianOutputBitStream>(MaybeOwned<Stream>(*output_stream)); auto decoded_bits = make<BigEndianOutputBitStream>(MaybeOwned<Stream>(*output_stream));
// T.6 2.2.1 Principle of the coding scheme // 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. // The reference line for the first coding line in a page is an imaginary white line.
ReferenceLine reference_line; CCITTStatus status;
TRY(reference_line.try_empend(ccitt_black, image_width)); TRY(status.current_line.try_empend(ccitt_black, image_width));
for (u32 i = 0; i < image_height; ++i) u32 i {};
reference_line = TRY(decode_single_ccitt_2d_line(*bit_stream, *decoded_bits, move(reference_line), image_width)); 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();
} }
} }

View file

@ -316,7 +316,7 @@ PDFErrorOr<ByteBuffer> Filter::decode_ccitt(ReadonlyBytes bytes, RefPtr<DictObje
// achieve to decode images that have it. Figure out what to do with it. // achieve to decode images that have it. Figure out what to do with it.
(void)end_of_block; (void)end_of_block;
if (require_end_of_line || encoded_byte_align || damaged_rows_before_error > 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"); return Error::rendering_unsupported_error("Unimplemented option for the CCITTFaxDecode Filter");
ByteBuffer decoded {}; ByteBuffer decoded {};