From 335097e446f92c6341f44f718fb336c81c67b318 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sun, 7 Jan 2024 00:20:40 -0500 Subject: [PATCH] LibGfx/TIFF: Modify the image according to the Orientation tag Let's use the already existing logic (ExifOrientedBitmap) to modify the bitmap to honor the orientation tag. --- Tests/LibGfx/TestImageDecoder.cpp | 13 +++++++++++++ Tests/LibGfx/test-inputs/tiff/orientation.tiff | Bin 0 -> 9787 bytes .../Libraries/LibGfx/ImageFormats/TIFFLoader.cpp | 11 +++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 Tests/LibGfx/test-inputs/tiff/orientation.tiff diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 444f98f0e4..f9f7022d1a 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -546,6 +546,19 @@ TEST_CASE(test_tiff_deflate) EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Red); } +TEST_CASE(test_tiff_orientation) +{ + auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/orientation.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, { 300, 400 })); + + // Orientation is Rotate90Clockwise + EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color::NamedColor::White); + EXPECT_EQ(frame.image->get_pixel(300 - 75, 60), 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/orientation.tiff b/Tests/LibGfx/test-inputs/tiff/orientation.tiff new file mode 100644 index 0000000000000000000000000000000000000000..4ff6e25b022c4bbb1652a8a464657e09f6ac1063 GIT binary patch literal 9787 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 #include #include +#include #include namespace Gfx { @@ -69,7 +70,7 @@ public: IntSize size() const { - return { *m_metadata.image_width(), *m_metadata.image_height() }; + return ExifOrientedBitmap::oriented_size({ *m_metadata.image_width(), *m_metadata.image_height() }, *m_metadata.orientation()); } Metadata const& metadata() const @@ -207,6 +208,8 @@ private: auto const strips_offset = *m_metadata.strip_offsets(); auto const strip_byte_counts = *m_metadata.strip_byte_counts(); + auto oriented_bitmap = TRY(ExifOrientedBitmap::create(BitmapFormat::BGRA8888, { *metadata().image_width(), *metadata().image_height() }, *metadata().orientation())); + for (u32 strip_index = 0; strip_index < strips_offset.size(); ++strip_index) { TRY(m_stream->seek(strips_offset[strip_index])); @@ -231,20 +234,20 @@ private: } last_color = color; - m_bitmap->set_pixel(column, scanline, color); + oriented_bitmap.set_pixel(column, scanline, color); } decoded_stream->align_to_byte_boundary(); } } + m_bitmap = oriented_bitmap.bitmap(); + return {}; } ErrorOr decode_frame_impl() { - m_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, size())); - switch (*m_metadata.compression()) { case Compression::NoCompression: { auto identity = [&](u32 num_bytes) {