From 0d2e6162dbecd4e253d5fa91d3fbd6bcaa949c9f Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sat, 6 May 2023 21:32:45 -0400 Subject: [PATCH] LibGfx/WebP: Implement compressed ALPH chunk reading A compressed ALPH chunk is a lossless webp bitstream, but without the 5 byte "header" that stores width, height, is-alpha-channel-used (it never is for an ALPH chunk since the ALPH chunk gets the alpha data out of the lossless webp's green channel), and version fields. For that reason, this cuts decode_webp_chunk_VP8L() into the header-reading part and the remaining part, so that the remaining part can be called by the ALPH reading routine. Lossy webp files with a (losslessly) compressed alpha channel can be found in the wild. Since we can't decode lossy webp data yet, change the `#if 0` in decode_webp_chunk_VP8() to `#if 1` to test this. --- .../LibGfx/ImageFormats/WebPLoader.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp index c3e35692e4..6eb06b71cd 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp @@ -1230,14 +1230,22 @@ ErrorOr> ColorIndexingTransform::transform(NonnullRefPtr> decode_webp_chunk_VP8L_contents(WebPLoadingContext& context, VP8LHeader const& vp8l_header, ReadonlyBytes lossless_data); + static ErrorOr> decode_webp_chunk_VP8L(WebPLoadingContext& context, Chunk const& vp8l_chunk) { VERIFY(context.first_chunk->type == FourCC("VP8L") || context.first_chunk->type == FourCC("VP8X")); VERIFY(vp8l_chunk.type == FourCC("VP8L")); auto vp8l_header = TRY(decode_webp_chunk_VP8L_header(context, vp8l_chunk)); + ReadonlyBytes lossless_data = vp8l_chunk.data.slice(5); + return decode_webp_chunk_VP8L_contents(context, vp8l_header, lossless_data); +} - FixedMemoryStream memory_stream { vp8l_chunk.data.slice(5) }; +// Decodes the lossless webp bitstream following the 5-byte header information. +static ErrorOr> decode_webp_chunk_VP8L_contents(WebPLoadingContext& context, VP8LHeader const& vp8l_header, ReadonlyBytes lossless_data) +{ + FixedMemoryStream memory_stream { lossless_data }; LittleEndianInputBitStream bit_stream { MaybeOwned(memory_stream) }; // image-stream = optional-transform spatially-coded-image @@ -1325,9 +1333,10 @@ static ErrorOr decode_webp_chunk_ALPH(WebPLoadingContext& context, Chunk c ReadonlyBytes alpha_data = alph_chunk.data.slice(1); + size_t pixel_count = bitmap.width() * bitmap.height(); + if (compression_method == 0) { // "Raw data: consists of a byte sequence of length width * height, containing all the 8-bit transparency values in scan order." - size_t pixel_count = bitmap.width() * bitmap.height(); if (alpha_data.size() < pixel_count) return context.error("WebPImageDecoderPlugin: uncompressed ALPH data too small"); for (size_t i = 0; i < pixel_count; ++i) @@ -1339,8 +1348,16 @@ static ErrorOr decode_webp_chunk_ALPH(WebPLoadingContext& context, Chunk c // of implicit dimension width x height. That is, this image-stream does NOT contain any headers describing the image dimension. // Once the image-stream is decoded into ARGB color values, following the process described in the lossless format specification, // the transparency information must be extracted from the green channel of the ARGB quadruplet." - // FIXME - return context.error("WebPImageDecoderPlugin: compressed ALPH chunk processing not yet implemented"); + VP8LHeader vp8l_header = { static_cast(bitmap.width()), static_cast(bitmap.height()), /*is_alpha_used=*/false }; + auto lossless_bitmap = TRY(decode_webp_chunk_VP8L_contents(context, vp8l_header, alpha_data)); + + if (pixel_count != static_cast(lossless_bitmap->width() * lossless_bitmap->height())) + return context.error("WebPImageDecoderPlugin: decompressed ALPH dimensions don't match VP8 dimensions"); + + for (size_t i = 0; i < pixel_count; ++i) + bitmap.begin()[i] |= (lossless_bitmap->begin()[i] & 0xff00) << 16; + + return {}; } static ErrorOr decode_webp_chunk_VP8X(WebPLoadingContext& context, Chunk const& vp8x_chunk)