diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index a6e2b5e9e0..aa68add43d 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -616,6 +616,20 @@ TEST_CASE(test_tiff_ccitt3) EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Black); } +TEST_CASE(test_tiff_ccitt3_fill) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/ccitt3_1d_fill.tiff"sv))); + EXPECT(Gfx::TIFFImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::TIFFImageDecoderPlugin::create(file->bytes())); + + auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 6, 4 })); + + EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color::NamedColor::White); + EXPECT_EQ(frame.image->get_pixel(3, 0), Gfx::Color::NamedColor::Black); + EXPECT_EQ(frame.image->get_pixel(2, 2), Gfx::Color::NamedColor::White); + EXPECT_EQ(frame.image->get_pixel(5, 3), Gfx::Color::NamedColor::White); +} + TEST_CASE(test_tiff_lzw) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/lzw.tiff"sv))); diff --git a/Tests/LibGfx/test-inputs/tiff/ccitt3_1d_fill.tiff b/Tests/LibGfx/test-inputs/tiff/ccitt3_1d_fill.tiff new file mode 100644 index 0000000000..e1ce7e062c Binary files /dev/null and b/Tests/LibGfx/test-inputs/tiff/ccitt3_1d_fill.tiff differ diff --git a/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp index 9c52911fc1..b137f18f7f 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp @@ -318,9 +318,19 @@ ErrorOr decode_single_ccitt3_1d_line(BigEndianInputBitStream& input_bit_st return {}; } -static ErrorOr read_eol(BigEndianInputBitStream& bit_stream) +static ErrorOr read_eol(BigEndianInputBitStream& bit_stream, Group3Options::UseFillBits use_fill_bits) { constexpr u16 EOL = 0b0000'0000'0001; + + if (use_fill_bits == Group3Options::UseFillBits::Yes) { + // TIFF specification, description of the T4Options tag: + // "Fill bits have been added as necessary before EOL codes such that + // EOL always ends on a byte boundary, thus ensuring an EOL-sequence of 1 byte + // preceded by a zero nibble: xxxx-0000 0000-0001." + auto const to_skip = (12 + bit_stream.bits_until_next_byte_boundary()) % 8; + TRY(bit_stream.read_bits(to_skip)); + } + auto const read = TRY(bit_stream.read_bits(12)); if (read != EOL) return Error::from_string_literal("CCITTDecoder: Invalid EndOfLine code"); @@ -368,7 +378,7 @@ ErrorOr decode_ccitt_group3(ReadonlyBytes bytes, u32 image_width, u3 // NOTE: For whatever reason, the last EOL doesn't seem to be included for (u32 i = 0; i < image_height; ++i) { - TRY(read_eol(*bit_stream)); + TRY(read_eol(*bit_stream, options.use_fill_bits)); TRY(decode_single_ccitt3_1d_line(*bit_stream, *decoded_bits, image_width)); }