From fab6a3915e4c81aa5fd1dc2a85444dfd0f8e8804 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sat, 6 May 2023 21:10:26 -0400 Subject: [PATCH] LibGfx/WebP: Implement uncompressed ALPH chunk reading ALPH chunks are only used to give lossy webp frames an alpha channel, and lossy decompression isn't implemented yet. So this can currently never be hit in practice -- but for debugging and testing, I put in some code behind `#if 0` for now that fake-decompresses a lossy webp frame by returning an empty bitmap. But this also doesn't implement compressed ALPH chunks yet, and I couldn't find any lossy-webp-with-alpha files that use uncompressed alpha channels. So the code here isn't really tested. --- .../LibGfx/ImageFormats/WebPLoader.cpp | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp index 610ff8df55..c3e35692e4 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp @@ -303,8 +303,16 @@ static ErrorOr> decode_webp_chunk_VP8(WebPLoadingContext& { VERIFY(vp8_chunk.type == FourCC("VP8 ")); + // Uncomment this to test ALPH decoding for WebP-lossy-with-alpha images while lossy decoding isn't implemented yet. +#if 0 + auto vp8_header = TRY(decode_webp_chunk_VP8_header(context, vp8_chunk)); + + // FIXME: probably want to pass in the bitmap format based on if there's an ALPH chunk. + return Bitmap::create(BitmapFormat::BGRA8888, { vp8_header.width, vp8_header.height }); +#else // FIXME: Implement webp lossy decoding. return context.error("WebPImageDecoderPlugin: decoding lossy webps not yet implemented"); +#endif } // https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossless @@ -1300,6 +1308,41 @@ static ErrorOr> decode_webp_chunk_VP8L(WebPLoadingContext& return bitmap; } +// https://developers.google.com/speed/webp/docs/riff_container#alpha +static ErrorOr decode_webp_chunk_ALPH(WebPLoadingContext& context, Chunk const& alph_chunk, Bitmap& bitmap) +{ + VERIFY(alph_chunk.type == FourCC("ALPH")); + + if (alph_chunk.data.size() < 1) + return context.error("WebPImageDecoderPlugin: ALPH chunk too small"); + + u8 flags = alph_chunk.data.data()[0]; + u8 preprocessing = (flags >> 4) & 3; + u8 filtering_method = (flags >> 2) & 3; + u8 compression_method = flags & 3; + + dbgln_if(WEBP_DEBUG, "preprocessing {} filtering_method {} compression_method {}", preprocessing, filtering_method, compression_method); + + ReadonlyBytes alpha_data = alph_chunk.data.slice(1); + + 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) + bitmap.begin()[i] |= alpha_data[i] << 24; + return {}; + } + + // "Lossless format compression: the byte sequence is a compressed image-stream (as described in the WebP Lossless Bitstream Format) + // 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"); +} + static ErrorOr decode_webp_chunk_VP8X(WebPLoadingContext& context, Chunk const& vp8x_chunk) { VERIFY(vp8x_chunk.type == FourCC("VP8X")); @@ -1594,9 +1637,8 @@ static ErrorOr> decode_webp_image_data(WebPLoadingContext& VERIFY(image_data.image_data_chunk->type == FourCC("VP8 ")); auto bitmap = TRY(decode_webp_chunk_VP8(context, image_data.image_data_chunk.value())); - if (image_data.alpha_chunk.has_value()) { - // FIXME: Decode alpha chunk and store decoded alpha in `bitmap`. - } + if (image_data.alpha_chunk.has_value()) + TRY(decode_webp_chunk_ALPH(context, image_data.alpha_chunk.value(), *bitmap)); return bitmap; }