瀏覽代碼

LibGfx/TIFF: Add support for bit-depth up to 32 bits per sample

This makes us support every "minisblack" and "rgb-contig" images from
the depth folder of libtiff's test suite:
https://libtiff.gitlab.io/libtiff/images.html
Lucas CHOLLET 1 年之前
父節點
當前提交
234d084876

+ 12 - 0
Tests/LibGfx/TestImageDecoder.cpp

@@ -431,6 +431,18 @@ TEST_CASE(test_tiff_grayscale)
     EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color(130, 130, 130));
 }
 
+TEST_CASE(test_tiff_16_bits)
+{
+    auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/16_bits.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::Red);
+}
+
 TEST_CASE(test_webp_simple_lossy)
 {
     auto file = MUST(Core::MappedFile::map(TEST_INPUT("webp/simple-vp8.webp"sv)));

二進制
Tests/LibGfx/test-inputs/tiff/16_bits.tiff


+ 32 - 10
Userland/Libraries/LibGfx/ImageFormats/TIFFLoader.cpp

@@ -76,6 +76,34 @@ private:
         BigEndian,
     };
 
+    static ErrorOr<u8> read_component(BigEndianInputBitStream& stream, u8 bits)
+    {
+        // FIXME: This function truncates everything to 8-bits
+        auto const value = TRY(stream.read_bits<u32>(bits));
+
+        if (bits > 8)
+            return value >> (bits - 8);
+        return value << (8 - bits);
+    }
+
+    ErrorOr<Color> read_color(BigEndianInputBitStream& stream)
+    {
+        auto bits_per_sample = *m_metadata.bits_per_sample();
+        if (m_metadata.samples_per_pixel().value_or(3) == 3) {
+            auto const first_component = TRY(read_component(stream, bits_per_sample[0]));
+            auto const second_component = TRY(read_component(stream, bits_per_sample[1]));
+            auto const third_component = TRY(read_component(stream, bits_per_sample[2]));
+            return Color(first_component, second_component, third_component);
+        }
+
+        if (*m_metadata.samples_per_pixel() == 1) {
+            auto const luminosity = TRY(read_component(stream, bits_per_sample[0]));
+            return Color(luminosity, luminosity, luminosity);
+        }
+
+        return Error::from_string_literal("Unsupported number of sample per pixel");
+    }
+
     template<CallableAs<ErrorOr<ReadonlyBytes>, u32> StripDecoder>
     ErrorOr<void> loop_over_pixels(StripDecoder&& strip_decoder)
     {
@@ -87,6 +115,7 @@ private:
 
             auto const decoded_bytes = TRY(strip_decoder(strip_byte_counts[strip_index]));
             auto decoded_strip = make<FixedMemoryStream>(decoded_bytes);
+            auto decoded_stream = make<BigEndianInputBitStream>(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;
@@ -96,16 +125,7 @@ private:
                 Optional<Color> last_color {};
 
                 for (u32 column = 0; column < *m_metadata.image_width(); ++column) {
-                    Color color {};
-
-                    if (m_metadata.samples_per_pixel().value_or(3) == 3) {
-                        color = Color { TRY(decoded_strip->template read_value<u8>()), TRY(decoded_strip->template read_value<u8>()), TRY(decoded_strip->template read_value<u8>()) };
-                    } else if (*m_metadata.samples_per_pixel() == 1) {
-                        auto luminosity = TRY(decoded_strip->template read_value<u8>());
-                        color = Color { luminosity, luminosity, luminosity };
-                    } else {
-                        return Error::from_string_literal("Unsupported number of sample per pixel");
-                    }
+                    auto color = TRY(read_color(*decoded_stream));
 
                     if (m_metadata.predictor() == Predictor::HorizontalDifferencing && last_color.has_value()) {
                         color.set_red(last_color->red() + color.red());
@@ -116,6 +136,8 @@ private:
                     last_color = color;
                     m_bitmap->set_pixel(column, scanline, color);
                 }
+
+                decoded_stream->align_to_byte_boundary();
             }
         }