From 81794df280bf7154ffbc1284859c8f799fff9833 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sun, 29 Oct 2023 17:02:45 -0400 Subject: [PATCH] LibGfx/TIFF: Add support for images with PackBits compression --- Tests/LibGfx/TestImageDecoder.cpp | 12 +++++ .../LibGfx/test-inputs/tiff/packed_bits.tiff | Bin 0 -> 26905 bytes .../LibGfx/ImageFormats/TIFFLoader.cpp | 44 +++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 Tests/LibGfx/test-inputs/tiff/packed_bits.tiff diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 7df82a5b50..07211bc0cd 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -382,6 +382,18 @@ TEST_CASE(test_tiff_uncompressed) 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))); + 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_webp_simple_lossy) { auto file = MUST(Core::MappedFile::map(TEST_INPUT("webp/simple-vp8.webp"sv))); diff --git a/Tests/LibGfx/test-inputs/tiff/packed_bits.tiff b/Tests/LibGfx/test-inputs/tiff/packed_bits.tiff new file mode 100644 index 0000000000000000000000000000000000000000..c66c9183740a5e2966b9e25c9f3d09edab20a689 GIT binary patch literal 26905 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 OYJ5p%TACh+U;qG;OuS|Q literal 0 HcmV?d00001 diff --git a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp index df2df3352d..327364fff1 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp @@ -130,8 +130,50 @@ private: case Compression::NoCompression: TRY(loop_over_pixels([this]() { return read_value(); })); break; + case Compression::PackBits: { + // Section 9: PackBits Compression + Optional n; + Optional saved_byte; + + auto read_packed_byte = [&]() -> ErrorOr { + while (true) { + if (!n.has_value()) + n = TRY(read_value()); + + if (n.value() >= 0 && !saved_byte.has_value()) { + n.value() = n.value() - 1; + if (n.value() == -1) + n.clear(); + + return read_value(); + } + + if (n.value() == -128) { + n.clear(); + continue; + } + + if (!saved_byte.has_value()) + saved_byte = TRY(read_value()); + + n.value() = n.value() + 1; + + auto const byte_backup = *saved_byte; + + if (n == 1) { + saved_byte.clear(); + n.clear(); + } + + return byte_backup; + } + }; + + TRY(loop_over_pixels(move(read_packed_byte))); + break; + } default: - return Error::from_string_literal("Compressed TIFF are not supported yet :^)"); + return Error::from_string_literal("This compression type is not supported yet :^)"); } return {};