diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 596964dd27..069daa4d27 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -591,6 +591,18 @@ TEST_CASE(test_tiff_deflate) EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Red); } +TEST_CASE(test_tiff_krita) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/krita.tif"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::Red); +} + TEST_CASE(test_tiff_orientation) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/orientation.tiff"sv))); diff --git a/Tests/LibGfx/test-inputs/tiff/krita.tif b/Tests/LibGfx/test-inputs/tiff/krita.tif new file mode 100644 index 0000000000..182657a0b7 Binary files /dev/null and b/Tests/LibGfx/test-inputs/tiff/krita.tif differ diff --git a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp index f6b60ddd51..ff5bedee69 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp @@ -75,6 +75,9 @@ public: if (m_metadata.strip_offsets()->size() != m_metadata.strip_byte_counts()->size()) return Error::from_string_literal("TIFFImageDecoderPlugin: StripsOffset and StripByteCount have different sizes"); + if (!m_metadata.rows_per_strip().has_value() && m_metadata.strip_byte_counts()->size() != 1) + return Error::from_string_literal("TIFFImageDecoderPlugin: RowsPerStrip is not provided and impossible to deduce"); + if (any_of(*m_metadata.bits_per_sample(), [](auto bit_depth) { return bit_depth == 0 || bit_depth > 32; })) return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid value in BitsPerSample"); @@ -238,19 +241,20 @@ private: { auto const strips_offset = *m_metadata.strip_offsets(); auto const strip_byte_counts = *m_metadata.strip_byte_counts(); + auto const rows_per_strip = m_metadata.rows_per_strip().value_or(*m_metadata.image_height()); 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])); - auto const rows_in_strip = strip_index < strips_offset.size() - 1 ? *m_metadata.rows_per_strip() : *m_metadata.image_height() - *m_metadata.rows_per_strip() * strip_index; + auto const rows_in_strip = strip_index < strips_offset.size() - 1 ? rows_per_strip : *m_metadata.image_height() - rows_per_strip * strip_index; auto const decoded_bytes = TRY(strip_decoder(strip_byte_counts[strip_index], rows_in_strip)); auto decoded_strip = make(decoded_bytes); auto decoded_stream = make(move(decoded_strip)); - for (u32 row = 0; row < *m_metadata.rows_per_strip(); row++) { - auto const scanline = row + *m_metadata.rows_per_strip() * strip_index; + for (u32 row = 0; row < rows_per_strip; row++) { + auto const scanline = row + rows_per_strip * strip_index; if (scanline >= *m_metadata.image_height()) break; diff --git a/Userland/Libraries/LibGfx/TIFFGenerator.py b/Userland/Libraries/LibGfx/TIFFGenerator.py index b95d409697..ceead495e9 100755 --- a/Userland/Libraries/LibGfx/TIFFGenerator.py +++ b/Userland/Libraries/LibGfx/TIFFGenerator.py @@ -128,7 +128,7 @@ known_tags: List[Tag] = [ Tag('273', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripOffsets", is_required=True), Tag('274', [TIFFType.UnsignedShort], [1], Orientation.Default, "Orientation", Orientation), Tag('277', [TIFFType.UnsignedShort], [1], None, "SamplesPerPixel", is_required=True), - Tag('278', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "RowsPerStrip", is_required=True), + Tag('278', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "RowsPerStrip", is_required=False), Tag('279', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripByteCounts", is_required=True), Tag('282', [TIFFType.UnsignedRational], [1], None, "XResolution"), Tag('283', [TIFFType.UnsignedRational], [1], None, "YResolution"),