Selaa lähdekoodia

LibGfx/TIFF+CCITT: Start to decode CCITT Group 3 images

We currently only support 1D Group 3, but that's a start.

The test case was generated with GIMP (it happens to be 1D by chance).
Lucas CHOLLET 1 vuosi sitten
vanhempi
commit
75d87ccf5f

+ 12 - 0
Tests/LibGfx/TestImageDecoder.cpp

@@ -555,6 +555,18 @@ TEST_CASE(test_tiff_ccitt_rle)
     EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Black);
 }
 
+TEST_CASE(test_tiff_ccitt3)
+{
+    auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/ccitt3.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, { 400, 300 }));
+
+    EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color::NamedColor::White);
+    EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Black);
+}
+
 TEST_CASE(test_tiff_lzw)
 {
     auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/lzw.tiff"sv)));

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


+ 38 - 0
Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.cpp

@@ -316,6 +316,16 @@ ErrorOr<void> decode_single_ccitt3_1d_line(BigEndianInputBitStream& input_bit_st
     return {};
 }
 
+static ErrorOr<void> read_eol(BigEndianInputBitStream& bit_stream)
+{
+    constexpr u16 EOL = 0b0000'0000'0001;
+    auto const read = TRY(bit_stream.read_bits<u16>(12));
+    if (read != EOL)
+        return Error::from_string_literal("CCITTDecoder: Invalid EndOfLine code");
+
+    return {};
+}
+
 }
 
 ErrorOr<ByteBuffer> decode_ccitt_rle(ReadonlyBytes bytes, u32 image_width, u32 image_height)
@@ -337,4 +347,32 @@ ErrorOr<ByteBuffer> decode_ccitt_rle(ReadonlyBytes bytes, u32 image_width, u32 i
     return decoded_bytes;
 }
 
+ErrorOr<ByteBuffer> decode_ccitt_group3(ReadonlyBytes bytes, u32 image_width, u32 image_height, Group3Options const& options)
+{
+    auto strip_stream = make<FixedMemoryStream>(bytes);
+    auto bit_stream = make<BigEndianInputBitStream>(MaybeOwned<Stream>(*strip_stream));
+
+    ByteBuffer decoded_bytes = TRY(ByteBuffer::create_zeroed(ceil_div(image_width * image_height, 8)));
+    auto output_stream = make<FixedMemoryStream>(decoded_bytes.bytes());
+    auto decoded_bits = make<BigEndianOutputBitStream>(MaybeOwned<Stream>(*output_stream));
+
+    if (options.dimensions == Group3Options::Mode::OneDimension) {
+        // 4.1.2 End-of-line (EOL)
+        // This code word follows each line of data. It is a unique code word that can never be found within a
+        // valid line of data; therefore, resynchronization after an error burst is possible.
+        // In addition, this signal will occur prior to the first data line of a page.
+        // ---
+        // 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(decode_single_ccitt3_1d_line(*bit_stream, *decoded_bits, image_width));
+        }
+
+        return decoded_bytes;
+    }
+
+    return Error::from_string_literal("CCITT3 2D is not implemented yet :^(");
+}
+
 }

+ 2 - 0
Userland/Libraries/LibGfx/ImageFormats/CCITTDecoder.h

@@ -45,4 +45,6 @@ struct Group3Options {
     UseFillBits use_fill_bits = UseFillBits::No;
 };
 
+ErrorOr<ByteBuffer> decode_ccitt_group3(ReadonlyBytes bytes, u32 image_width, u32 image_height, Group3Options const& options);
+
 }

+ 14 - 0
Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp

@@ -314,6 +314,20 @@ private:
             TRY(loop_over_pixels(move(decode_ccitt_rle_strip)));
             break;
         }
+        case Compression::Group3Fax: {
+            TRY(ensure_tags_are_correct_for_ccitt());
+
+            auto const parameters = parse_t4_options(*m_metadata.t4_options());
+            ByteBuffer decoded_bytes {};
+            auto decode_group3_strip = [&](u32 num_bytes, u32 strip_height) -> ErrorOr<ReadonlyBytes> {
+                auto const encoded_bytes = TRY(m_stream->read_in_place<u8 const>(num_bytes));
+                decoded_bytes = TRY(CCITT::decode_ccitt_group3(encoded_bytes, *m_metadata.image_width(), strip_height, parameters));
+                return decoded_bytes;
+            };
+
+            TRY(loop_over_pixels(move(decode_group3_strip)));
+            break;
+        }
         case Compression::LZW: {
             ByteBuffer decoded_bytes {};
             auto decode_lzw_strip = [&](u32 num_bytes, u32) -> ErrorOr<ReadonlyBytes> {