Browse Source

LibGfx/CCITT: Consider the UseFillBits option

Lucas CHOLLET 1 year ago
parent
commit
b9afac0a06

+ 14 - 0
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);
     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)
 TEST_CASE(test_tiff_lzw)
 {
 {
     auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/lzw.tiff"sv)));
     auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/lzw.tiff"sv)));

BIN
Tests/LibGfx/test-inputs/tiff/ccitt3_1d_fill.tiff


+ 12 - 2
Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp

@@ -318,9 +318,19 @@ ErrorOr<void> decode_single_ccitt3_1d_line(BigEndianInputBitStream& input_bit_st
     return {};
     return {};
 }
 }
 
 
-static ErrorOr<void> read_eol(BigEndianInputBitStream& bit_stream)
+static ErrorOr<void> read_eol(BigEndianInputBitStream& bit_stream, Group3Options::UseFillBits use_fill_bits)
 {
 {
     constexpr u16 EOL = 0b0000'0000'0001;
     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<u16>(12));
     auto const read = TRY(bit_stream.read_bits<u16>(12));
     if (read != EOL)
     if (read != EOL)
         return Error::from_string_literal("CCITTDecoder: Invalid EndOfLine code");
         return Error::from_string_literal("CCITTDecoder: Invalid EndOfLine code");
@@ -368,7 +378,7 @@ ErrorOr<ByteBuffer> decode_ccitt_group3(ReadonlyBytes bytes, u32 image_width, u3
         // NOTE: For whatever reason, the last EOL doesn't seem to be included
         // NOTE: For whatever reason, the last EOL doesn't seem to be included
 
 
         for (u32 i = 0; i < image_height; ++i) {
         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));
             TRY(decode_single_ccitt3_1d_line(*bit_stream, *decoded_bits, image_width));
         }
         }