mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibGfx: Correctly decode webp lossless with small palette and odd width
WebP lossless files that use a color indexing transform with <= 16 colors use pixel bundling to pack 2, 4, or 8 pixels into a single pixel. If the image's width doesn't happen to be an exact multiple of the bundling factor, we need to: 1. Use ceil_div() instead of just dividing the width by the bundling factor 2. Remember the original width and use it instead of computing reduced width times bundling factor This does these changes, and adds a simple test for it -- it at least checks that the decoded images have the right size. (I created these images myself in Photoshop, and used the same technique as for Tests/LibGfx/test-inputs/catdog-alert-*.webp to create images with a certain number of colors.)
This commit is contained in:
parent
625b29f507
commit
95e35b7f5e
Notes:
sideshowbarker
2024-07-17 18:38:54 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/95e35b7f5e Pull-request: https://github.com/SerenityOS/serenity/pull/18243
5 changed files with 31 additions and 7 deletions
|
@ -420,6 +420,27 @@ TEST_CASE(test_webp_simple_lossless_color_index_transform_pixel_bundling)
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE(test_webp_simple_lossless_color_index_transform_pixel_bundling_odd_width)
|
||||
{
|
||||
StringView file_names[] = {
|
||||
"width11-height11-colors2.webp"sv,
|
||||
"width11-height11-colors3.webp"sv,
|
||||
"width11-height11-colors15.webp"sv,
|
||||
};
|
||||
|
||||
for (auto file_name : file_names) {
|
||||
auto file = MUST(Core::MappedFile::map(MUST(String::formatted("{}{}", TEST_INPUT(""), file_name))));
|
||||
auto plugin_decoder = MUST(Gfx::WebPImageDecoderPlugin::create(file->bytes()));
|
||||
EXPECT(plugin_decoder->initialize());
|
||||
|
||||
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||
EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(11, 11));
|
||||
|
||||
auto frame = MUST(plugin_decoder->frame(0));
|
||||
EXPECT_EQ(frame.image->size(), Gfx::IntSize(11, 11));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(test_webp_extended_lossless_animated)
|
||||
{
|
||||
auto file = MUST(Core::MappedFile::map(TEST_INPUT("extended-lossless-animated.webp"sv)));
|
||||
|
|
BIN
Tests/LibGfx/test-inputs/width11-height11-colors15.webp
Normal file
BIN
Tests/LibGfx/test-inputs/width11-height11-colors15.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
Tests/LibGfx/test-inputs/width11-height11-colors2.webp
Normal file
BIN
Tests/LibGfx/test-inputs/width11-height11-colors2.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 172 B |
BIN
Tests/LibGfx/test-inputs/width11-height11-colors3.webp
Normal file
BIN
Tests/LibGfx/test-inputs/width11-height11-colors3.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 B |
|
@ -1083,7 +1083,7 @@ ErrorOr<NonnullRefPtr<Bitmap>> SubtractGreenTransform::transform(NonnullRefPtr<B
|
|||
// https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#44_color_indexing_transform
|
||||
class ColorIndexingTransform : public Transform {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> read(WebPLoadingContext&, LittleEndianInputBitStream&);
|
||||
static ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> read(WebPLoadingContext&, LittleEndianInputBitStream&, int original_width);
|
||||
virtual ErrorOr<NonnullRefPtr<Bitmap>> transform(NonnullRefPtr<Bitmap>) override;
|
||||
|
||||
// For a color indexing transform, the green channel of the source image is used as the index into a palette to produce an output color.
|
||||
|
@ -1102,17 +1102,19 @@ public:
|
|||
int pixels_per_pixel() const { return m_pixels_per_pixel; }
|
||||
|
||||
private:
|
||||
ColorIndexingTransform(unsigned pixels_per_pixel, NonnullRefPtr<Bitmap> palette_bitmap)
|
||||
ColorIndexingTransform(int pixels_per_pixel, int original_width, NonnullRefPtr<Bitmap> palette_bitmap)
|
||||
: m_pixels_per_pixel(pixels_per_pixel)
|
||||
, m_original_width(original_width)
|
||||
, m_palette_bitmap(palette_bitmap)
|
||||
{
|
||||
}
|
||||
|
||||
int m_pixels_per_pixel;
|
||||
int m_original_width;
|
||||
NonnullRefPtr<Bitmap> m_palette_bitmap;
|
||||
};
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> ColorIndexingTransform::read(WebPLoadingContext& context, LittleEndianInputBitStream& bit_stream)
|
||||
ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> ColorIndexingTransform::read(WebPLoadingContext& context, LittleEndianInputBitStream& bit_stream, int original_width)
|
||||
{
|
||||
// color-indexing-image = 8BIT ; color count
|
||||
// entropy-coded-image
|
||||
|
@ -1137,7 +1139,7 @@ ErrorOr<NonnullOwnPtr<ColorIndexingTransform>> ColorIndexingTransform::read(WebP
|
|||
for (ARGB32* pixel = palette_bitmap->begin() + 1; pixel != palette_bitmap->end(); ++pixel)
|
||||
*pixel = add_argb32(*pixel, pixel[-1]);
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) ColorIndexingTransform(pixels_per_pixel, move(palette_bitmap)));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) ColorIndexingTransform(pixels_per_pixel, original_width, move(palette_bitmap)));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> ColorIndexingTransform::transform(NonnullRefPtr<Bitmap> bitmap)
|
||||
|
@ -1156,7 +1158,8 @@ ErrorOr<NonnullRefPtr<Bitmap>> ColorIndexingTransform::transform(NonnullRefPtr<B
|
|||
}
|
||||
|
||||
// Pixel bundling case.
|
||||
IntSize unbundled_size = { bitmap->size().width() * pixels_per_pixel(), bitmap->size().height() };
|
||||
VERIFY(ceil_div(m_original_width, pixels_per_pixel()) == bitmap->size().width());
|
||||
IntSize unbundled_size = { m_original_width, bitmap->size().height() };
|
||||
auto new_bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, unbundled_size));
|
||||
|
||||
unsigned bits_per_pixel = 8 / pixels_per_pixel();
|
||||
|
@ -1252,8 +1255,8 @@ static ErrorOr<void> decode_webp_chunk_VP8L(WebPLoadingContext& context, Chunk c
|
|||
TRY(transforms.try_append(TRY(try_make<SubtractGreenTransform>())));
|
||||
break;
|
||||
case COLOR_INDEXING_TRANSFORM: {
|
||||
auto color_indexing_transform = TRY(ColorIndexingTransform::read(context, bit_stream));
|
||||
stored_size.set_width(stored_size.width() / color_indexing_transform->pixels_per_pixel());
|
||||
auto color_indexing_transform = TRY(ColorIndexingTransform::read(context, bit_stream, stored_size.width()));
|
||||
stored_size.set_width(ceil_div(stored_size.width(), color_indexing_transform->pixels_per_pixel()));
|
||||
TRY(transforms.try_append(move(color_indexing_transform)));
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue