1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 14:48:14 +00:00

LibGfx: Implement COLOR_TRANSFORM for webp lossless decoder

This commit is contained in:
Nico Weber 2023-04-04 10:54:21 -04:00 committed by Linus Groh
parent ebbe4dafa1
commit 55b2977d5d

View file

@ -923,6 +923,85 @@ ARGB32 PredictorTransform::inverse_transform(ARGB32 pixel, ARGB32 prediction)
.value();
}
// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#42_color_transform
class ColorTransform : public Transform {
public:
static ErrorOr<NonnullOwnPtr<ColorTransform>> read(WebPLoadingContext&, LittleEndianInputBitStream&, IntSize const& image_size);
virtual ErrorOr<void> transform(Bitmap&) override;
private:
ColorTransform(int size_bits, NonnullRefPtr<Bitmap> color_bitmap)
: m_size_bits(size_bits)
, m_color_bitmap(move(color_bitmap))
{
}
static i8 ColorTransformDelta(i8 transform, i8 color)
{
return (transform * color) >> 5;
}
static ARGB32 inverse_transform(ARGB32 pixel, ARGB32 transform);
int m_size_bits;
NonnullRefPtr<Bitmap> m_color_bitmap;
};
ErrorOr<NonnullOwnPtr<ColorTransform>> ColorTransform::read(WebPLoadingContext& context, LittleEndianInputBitStream& bit_stream, IntSize const& image_size)
{
// color-image = 3BIT ; sub-pixel code
// entropy-coded-image
int size_bits = TRY(bit_stream.read_bits(3)) + 2;
int block_size = 1 << size_bits;
IntSize color_image_size { ceil_div(image_size.width(), block_size), ceil_div(image_size.height(), block_size) };
auto color_bitmap = TRY(decode_webp_chunk_VP8L_image(context, ImageKind::EntropyCoded, BitmapFormat::BGRx8888, color_image_size, bit_stream));
return adopt_nonnull_own_or_enomem(new (nothrow) ColorTransform(size_bits, move(color_bitmap)));
}
ErrorOr<void> ColorTransform::transform(Bitmap& bitmap)
{
for (int y = 0; y < bitmap.height(); ++y) {
ARGB32* bitmap_scanline = bitmap.scanline(y);
int color_y = y >> m_size_bits;
ARGB32* color_scanline = m_color_bitmap->scanline(color_y);
for (int x = 0; x < bitmap.width(); ++x) {
int color_x = x >> m_size_bits;
bitmap_scanline[x] = inverse_transform(bitmap_scanline[x], color_scanline[color_x]);
}
}
return {};
}
ARGB32 ColorTransform::inverse_transform(ARGB32 pixel, ARGB32 transform)
{
// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#51_roles_of_image_data
// "Each ColorTransformElement 'cte' is treated as a pixel whose alpha component is 255,
// red component is cte.red_to_blue, green component is cte.green_to_blue
// and blue component is cte.green_to_red."
auto transform_color = Color::from_argb(transform);
i8 red_to_blue = static_cast<i8>(transform_color.red());
i8 green_to_blue = static_cast<i8>(transform_color.green());
i8 green_to_red = static_cast<i8>(transform_color.blue());
auto pixel_color = Color::from_argb(pixel);
// "Transformed values of red and blue components"
int tmp_red = pixel_color.red();
int green = pixel_color.green();
int tmp_blue = pixel_color.blue();
// "Applying the inverse transform is just adding the color transform deltas"
tmp_red += ColorTransformDelta(green_to_red, green);
tmp_blue += ColorTransformDelta(green_to_blue, green);
tmp_blue += ColorTransformDelta(red_to_blue, tmp_red & 0xff);
return Color(tmp_red & 0xff, green, tmp_blue & 0xff, pixel_color.alpha()).value();
}
// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#43_subtract_green_transform
class SubtractGreenTransform : public Transform {
public:
@ -1005,7 +1084,8 @@ static ErrorOr<void> decode_webp_chunk_VP8L(WebPLoadingContext& context, Chunk c
TRY(transforms.try_append(TRY(PredictorTransform::read(context, bit_stream, context.size.value()))));
break;
case COLOR_TRANSFORM:
return context.error("WebPImageDecoderPlugin: VP8L COLOR_TRANSFORM handling not yet implemented");
TRY(transforms.try_append(TRY(ColorTransform::read(context, bit_stream, context.size.value()))));
break;
case SUBTRACT_GREEN_TRANSFORM:
TRY(transforms.try_append(TRY(try_make<SubtractGreenTransform>())));
break;