summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2023-04-06 21:19:45 -0400
committerLinus Groh <mail@linusgroh.de>2023-04-08 16:50:40 +0200
commita915d07293920c1ab9acc9e80556a89e94fbd19e (patch)
treee84d6f44650e296b58ad7f8d66a9dee61a17a281 /Userland
parent6f4fdd85b711832ba9913094401bc6e624ae1945 (diff)
downloadserenity-a915d07293920c1ab9acc9e80556a89e94fbd19e.zip
LibGfx: Implement most of COLOR_INDEXING_TRANSFORM for webp decoder
Doesn't yet implement pixel packing for when the palette has fewer than 16 colors.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp55
1 files changed, 54 insertions, 1 deletions
diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp
index 4a7ee549a9..997b2d4b88 100644
--- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp
+++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp
@@ -1062,6 +1062,58 @@ ErrorOr<void> SubtractGreenTransform::transform(Bitmap& bitmap)
return {};
}
+// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#44_color_indexing_transform
+class ColorIndexingTransform : public Transform {
+public:
+ static ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> read(WebPLoadingContext&, LittleEndianInputBitStream&);
+ virtual ErrorOr<void> transform(Bitmap&) override;
+
+private:
+ explicit ColorIndexingTransform(NonnullRefPtr<Bitmap> palette_bitmap)
+ : m_palette_bitmap(palette_bitmap)
+ {
+ }
+
+ NonnullRefPtr<Bitmap> m_palette_bitmap;
+};
+
+ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> ColorIndexingTransform::read(WebPLoadingContext& context, LittleEndianInputBitStream& bit_stream)
+{
+ // color-indexing-image = 8BIT ; color count
+ // entropy-coded-image
+ int color_table_size = TRY(bit_stream.read_bits(8)) + 1;
+ IntSize palette_image_size { color_table_size, 1 };
+
+ // "When the color table is small (equal to or less than 16 colors), several pixels are bundled into a single pixel...."
+ // FIXME: Implement support for this pixel packing.
+ if (color_table_size <= 16)
+ return Error::from_string_literal("WebPImageDecoderPlugin: COLOR_INDEXING_TRANSFORM pixel packing not yet implemented");
+
+ auto palette_bitmap = TRY(decode_webp_chunk_VP8L_image(context, ImageKind::EntropyCoded, BitmapFormat::BGRA8888, palette_image_size, bit_stream));
+
+ // "The color table is always subtraction-coded to reduce image entropy. [...] In decoding, every final color in the color table
+ // can be obtained by adding the previous color component values by each ARGB component separately,
+ // and storing the least significant 8 bits of the result."
+ for (ARGB32* pixel = palette_bitmap->begin() + 1; pixel != palette_bitmap->end(); ++pixel)
+ *pixel = add_argb32(*pixel, pixel[-1]);
+
+ return adopt_nonnull_own_or_enomem(new (nothrow) ColorIndexingTransform(move(palette_bitmap)));
+}
+
+ErrorOr<void> ColorIndexingTransform::transform(Bitmap& bitmap)
+{
+ // FIXME: If this is the last transform, consider returning an Indexed8 bitmap here?
+
+ for (ARGB32& pixel : bitmap) {
+ // "The inverse transform for the image is simply replacing the pixel values (which are indices to the color table)
+ // with the actual color table values. The indexing is done based on the green component of the ARGB color. [...]
+ // If the index is equal or larger than color_table_size, the argb color value should be set to 0x00000000 (transparent black)."
+ u8 index = Color::from_argb(pixel).green();
+ pixel = index < m_palette_bitmap->width() ? m_palette_bitmap->scanline(0)[index] : 0;
+ }
+ return {};
+}
+
}
// https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossless
@@ -1133,7 +1185,8 @@ static ErrorOr<void> decode_webp_chunk_VP8L(WebPLoadingContext& context, Chunk c
TRY(transforms.try_append(TRY(try_make<SubtractGreenTransform>())));
break;
case COLOR_INDEXING_TRANSFORM:
- return context.error("WebPImageDecoderPlugin: VP8L COLOR_INDEXING_TRANSFORM handling not yet implemented");
+ TRY(transforms.try_append(TRY(ColorIndexingTransform::read(context, bit_stream))));
+ break;
}
}