mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 14:57:35 +00:00
LibGfx: Implement COLOR_TRANSFORM for webp lossless decoder
This commit is contained in:
parent
ebbe4dafa1
commit
55b2977d5d
1 changed files with 81 additions and 1 deletions
|
@ -923,6 +923,85 @@ ARGB32 PredictorTransform::inverse_transform(ARGB32 pixel, ARGB32 prediction)
|
||||||
.value();
|
.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
|
// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#43_subtract_green_transform
|
||||||
class SubtractGreenTransform : public Transform {
|
class SubtractGreenTransform : public Transform {
|
||||||
public:
|
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()))));
|
TRY(transforms.try_append(TRY(PredictorTransform::read(context, bit_stream, context.size.value()))));
|
||||||
break;
|
break;
|
||||||
case COLOR_TRANSFORM:
|
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:
|
case SUBTRACT_GREEN_TRANSFORM:
|
||||||
TRY(transforms.try_append(TRY(try_make<SubtractGreenTransform>())));
|
TRY(transforms.try_append(TRY(try_make<SubtractGreenTransform>())));
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue