LibGfx/WebPLoader: Use transparent black as animation background color

This matches libwebp (see ZeroFillCanvas() call in
libwebp/src/demux/anim_decode.c:355 and ZeroFillFrameRect() call
in line 435, but in WebPAnimDecoderGetNext()) and makes files
written e.g. by asesprite look correct -- even though the old
behavior is also spec-compliant and arguably makes more sense.
Now nothing looks at the background color stored in the file.

See PR for an example image where it makes a visible difference.
This commit is contained in:
Nico Weber 2024-07-12 16:40:23 -04:00
parent a5bc15355d
commit 1a663078ed
2 changed files with 13 additions and 4 deletions

View file

@ -1395,7 +1395,7 @@ TEST_CASE(test_webp_extended_lossless_animated)
EXPECT_EQ(frame.image->get_pixel(500, 700), Gfx::Color::Yellow); EXPECT_EQ(frame.image->get_pixel(500, 700), Gfx::Color::Yellow);
// This one isn't the same in all frames. // This one isn't the same in all frames.
EXPECT_EQ(frame.image->get_pixel(500, 0), (frame_index == 2 || frame_index == 6) ? Gfx::Color::Black : Gfx::Color(255, 255, 255, 0)); EXPECT_EQ(frame.image->get_pixel(500, 0), (frame_index == 2 || frame_index == 6) ? Gfx::Color::Black : Gfx::Color(0, 0, 0, 0));
} }
} }

View file

@ -538,7 +538,7 @@ static ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_image_data(WebPLoadingContext&
return bitmap; return bitmap;
} }
// https://developers.google.com/speed/webp/docs/riff_container#assembling_the_canvas_from_frames // https://developers.google.com/speed/webp/docs/riff_container#canvas_assembly_from_frames
static ErrorOr<ImageFrameDescriptor> decode_webp_animation_frame(WebPLoadingContext& context, size_t frame_index) static ErrorOr<ImageFrameDescriptor> decode_webp_animation_frame(WebPLoadingContext& context, size_t frame_index)
{ {
if (frame_index >= context.animation_frame_chunks_data->size()) if (frame_index >= context.animation_frame_chunks_data->size())
@ -547,7 +547,15 @@ static ErrorOr<ImageFrameDescriptor> decode_webp_animation_frame(WebPLoadingCont
VERIFY(context.first_chunk->id() == "VP8X"sv); VERIFY(context.first_chunk->id() == "VP8X"sv);
VERIFY(context.vp8x_header.has_animation); VERIFY(context.vp8x_header.has_animation);
Color clear_color = Color::from_argb(context.animation_header_chunk_data->background_color); // The spec says
// "canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
// background color ANIM.background_color."
// But:
// * libwebp always fills with transparent black (#00000000)
// * some images (e.g. images written by Aseprite) set the background color to fully opaque white
// These images then end up with a nice transparent background in libwebp-based decoders (i.e. basically everywhere)
// but show a silly opaque border in ours. So don't use context.animation_header_chunk_data->background_color here.
Color clear_color(Color::Transparent);
size_t start_frame = context.current_frame + 1; size_t start_frame = context.current_frame + 1;
dbgln_if(WEBP_DEBUG, "start_frame {} context.current_frame {}", start_frame, context.current_frame); dbgln_if(WEBP_DEBUG, "start_frame {} context.current_frame {}", start_frame, context.current_frame);
@ -555,7 +563,8 @@ static ErrorOr<ImageFrameDescriptor> decode_webp_animation_frame(WebPLoadingCont
start_frame = 0; start_frame = 0;
auto format = context.vp8x_header.has_alpha ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888; auto format = context.vp8x_header.has_alpha ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888;
context.bitmap = TRY(Bitmap::create(format, { context.vp8x_header.width, context.vp8x_header.height })); context.bitmap = TRY(Bitmap::create(format, { context.vp8x_header.width, context.vp8x_header.height }));
context.bitmap->fill(clear_color); if (clear_color != Color(Color::Transparent)) // Bitmaps start out transparent, so only fill if not transparent.
context.bitmap->fill(clear_color);
} else if (frame_index < context.current_frame) { } else if (frame_index < context.current_frame) {
start_frame = 0; start_frame = 0;
} }