diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index b05fc354777..fdc76d228cc 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -384,6 +384,18 @@ TEST_CASE(test_tiff_uncompressed) EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Red); } +TEST_CASE(test_tiff_lzw) +{ + auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/lzw.tiff"sv))); + EXPECT(Gfx::TIFFImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = MUST(Gfx::TIFFImageDecoderPlugin::create(file->bytes())); + + auto frame = 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::Red); +} + TEST_CASE(test_tiff_packed_bits) { auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/packed_bits.tiff"sv))); diff --git a/Tests/LibGfx/test-inputs/tiff/lzw.tiff b/Tests/LibGfx/test-inputs/tiff/lzw.tiff new file mode 100644 index 00000000000..9040f769a3e Binary files /dev/null and b/Tests/LibGfx/test-inputs/tiff/lzw.tiff differ diff --git a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp index bc962a5c275..1e7c16210be 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Gfx { @@ -108,10 +109,12 @@ private: }; template - ErrorOr loop_over_pixels(ByteReader&& byte_reader) + ErrorOr loop_over_pixels(ByteReader&& byte_reader, Function(u32)> initializer = {}) { for (u32 strip_index = 0; strip_index < m_strip_offsets.size(); ++strip_index) { TRY(m_stream->seek(m_strip_offsets[strip_index])); + if (initializer) + TRY(initializer(m_strip_bytes_count[strip_index])); for (u32 row = 0; row < m_rows_per_strip; row++) { auto const scanline = row + m_rows_per_strip * strip_index; if (scanline >= static_cast(m_size.height())) @@ -145,6 +148,46 @@ private: case Compression::NoCompression: TRY(loop_over_pixels([this]() { return read_value(); })); break; + case Compression::LZW: { + Vector result_buffer {}; + Optional> decoder {}; + + u16 clear_code {}; + u16 end_of_information_code {}; + + auto initializer = [&](u32 bytes) -> ErrorOr { + auto strip_stream = make(TRY(m_stream->read_in_place(bytes))); + auto lzw_stream = make(MaybeOwned(move(strip_stream))); + decoder = Compress::LZWDecoder { MaybeOwned { move(lzw_stream) }, 8, -1 }; + + clear_code = decoder->add_control_code(); + end_of_information_code = decoder->add_control_code(); + + return {}; + }; + + auto read_lzw_byte = [&]() -> ErrorOr { + while (true) { + if (!result_buffer.is_empty()) + return result_buffer.take_first(); + + auto const code = TRY(decoder->next_code()); + + if (code == clear_code) { + decoder->reset(); + continue; + } + + if (code == end_of_information_code) + return Error::from_string_literal("TIFFImageDecoderPlugin: Reached end of LZW stream"); + + result_buffer = decoder->get_output(); + } + }; + + TRY(loop_over_pixels([read_lzw_byte = move(read_lzw_byte)]() { return read_lzw_byte(); }, move(initializer))); + break; + } case Compression::PackBits: { // Section 9: PackBits Compression Optional n;