From 7309441b312c5dd8ba5d2cf7316aba83fc4e4f40 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 7 Apr 2023 15:56:53 -0400 Subject: [PATCH] LibGfx: Enable webp lossless Transform to return new bitmap ...in addition to modifying in-place. This is needed for bitpacking support for the color indexing transform (and it could also be used to make the color indexing transform return an indexed bitmap, which is something we could do if that's the last transform that's applied). No behavior change. --- .../LibGfx/ImageFormats/WebPLoader.cpp | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp index d9404601d0..443041746e 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp @@ -750,7 +750,9 @@ static ARGB32 add_argb32(ARGB32 a, ARGB32 b) class Transform { public: virtual ~Transform(); - virtual ErrorOr transform(Bitmap&) = 0; + + // Could modify the input bitmap and return it, or could return a new bitmap. + virtual ErrorOr> transform(NonnullRefPtr) = 0; }; Transform::~Transform() = default; @@ -759,7 +761,7 @@ Transform::~Transform() = default; class PredictorTransform : public Transform { public: static ErrorOr> read(WebPLoadingContext&, LittleEndianInputBitStream&, IntSize const& image_size); - virtual ErrorOr transform(Bitmap&) override; + virtual ErrorOr> transform(NonnullRefPtr) override; private: PredictorTransform(int size_bits, NonnullRefPtr predictor_bitmap) @@ -857,8 +859,10 @@ ErrorOr> PredictorTransform::read(WebPLoadingC return adopt_nonnull_own_or_enomem(new (nothrow) PredictorTransform(size_bits, move(predictor_bitmap))); } -ErrorOr PredictorTransform::transform(Bitmap& bitmap) +ErrorOr> PredictorTransform::transform(NonnullRefPtr bitmap_ref) { + Bitmap& bitmap = *bitmap_ref; + // "There are special handling rules for some border pixels. // If there is a prediction transform, regardless of the mode [0..13] for these pixels, // the predicted value for the left-topmost pixel of the image is 0xff000000, @@ -908,7 +912,7 @@ ErrorOr PredictorTransform::transform(Bitmap& bitmap) bitmap_previous_scanline = bitmap_scanline; } - return {}; + return bitmap_ref; } ErrorOr PredictorTransform::predict(u8 predictor, ARGB32 TL, ARGB32 T, ARGB32 TR, ARGB32 L) @@ -980,7 +984,7 @@ ErrorOr PredictorTransform::predict(u8 predictor, ARGB32 TL, ARGB32 T, A class ColorTransform : public Transform { public: static ErrorOr> read(WebPLoadingContext&, LittleEndianInputBitStream&, IntSize const& image_size); - virtual ErrorOr transform(Bitmap&) override; + virtual ErrorOr> transform(NonnullRefPtr) override; private: ColorTransform(int size_bits, NonnullRefPtr color_bitmap) @@ -1015,8 +1019,10 @@ ErrorOr> ColorTransform::read(WebPLoadingContext& return adopt_nonnull_own_or_enomem(new (nothrow) ColorTransform(size_bits, move(color_bitmap))); } -ErrorOr ColorTransform::transform(Bitmap& bitmap) +ErrorOr> ColorTransform::transform(NonnullRefPtr bitmap_ref) { + Bitmap& bitmap = *bitmap_ref; + for (int y = 0; y < bitmap.height(); ++y) { ARGB32* bitmap_scanline = bitmap.scanline(y); @@ -1028,7 +1034,7 @@ ErrorOr ColorTransform::transform(Bitmap& bitmap) bitmap_scanline[x] = inverse_transform(bitmap_scanline[x], color_scanline[color_x]); } } - return {}; + return bitmap_ref; } ARGB32 ColorTransform::inverse_transform(ARGB32 pixel, ARGB32 transform) @@ -1060,25 +1066,25 @@ ARGB32 ColorTransform::inverse_transform(ARGB32 pixel, ARGB32 transform) // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#43_subtract_green_transform class SubtractGreenTransform : public Transform { public: - virtual ErrorOr transform(Bitmap&) override; + virtual ErrorOr> transform(NonnullRefPtr) override; }; -ErrorOr SubtractGreenTransform::transform(Bitmap& bitmap) +ErrorOr> SubtractGreenTransform::transform(NonnullRefPtr bitmap) { - for (ARGB32& pixel : bitmap) { + for (ARGB32& pixel : *bitmap) { Color color = Color::from_argb(pixel); u8 red = (color.red() + color.green()) & 0xff; u8 blue = (color.blue() + color.green()) & 0xff; pixel = Color(red, color.green(), blue, color.alpha()).value(); } - return {}; + return bitmap; } // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#44_color_indexing_transform class ColorIndexingTransform : public Transform { public: static ErrorOr> read(WebPLoadingContext&, LittleEndianInputBitStream&); - virtual ErrorOr transform(Bitmap&) override; + virtual ErrorOr> transform(NonnullRefPtr) override; private: explicit ColorIndexingTransform(NonnullRefPtr palette_bitmap) @@ -1113,18 +1119,18 @@ ErrorOr> ColorIndexingTransform::read(WebP return adopt_nonnull_own_or_enomem(new (nothrow) ColorIndexingTransform(move(palette_bitmap))); } -ErrorOr ColorIndexingTransform::transform(Bitmap& bitmap) +ErrorOr> ColorIndexingTransform::transform(NonnullRefPtr bitmap) { // FIXME: If this is the last transform, consider returning an Indexed8 bitmap here? - for (ARGB32& pixel : bitmap) { + 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 {}; + return bitmap; } } @@ -1209,7 +1215,7 @@ static ErrorOr decode_webp_chunk_VP8L(WebPLoadingContext& context, Chunk c // Transforms have to be applied in the reverse order they appear in in the file. // (As far as I can tell, this isn't mentioned in the spec.) for (auto const& transform : transforms.in_reverse()) - TRY(transform->transform(*context.bitmap)); + context.bitmap = TRY(transform->transform(context.bitmap.release_nonnull())); return {}; }