diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 97ff6a4aac..ced7b2a351 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -286,9 +286,7 @@ TEST_CASE(test_webp_extended_lossless_animated) EXPECT_EQ(plugin_decoder->frame_count(), 8u); EXPECT(plugin_decoder->is_animated()); - - // FIXME: This is wrong. - EXPECT(!plugin_decoder->loop_count()); + EXPECT_EQ(plugin_decoder->loop_count(), 42u); EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(990, 1050)); } diff --git a/Tests/LibGfx/test-inputs/extended-lossless-animated.webp b/Tests/LibGfx/test-inputs/extended-lossless-animated.webp index 5b44046e2c..f3d087d1cd 100644 Binary files a/Tests/LibGfx/test-inputs/extended-lossless-animated.webp and b/Tests/LibGfx/test-inputs/extended-lossless-animated.webp differ diff --git a/Userland/Libraries/LibGfx/WebPLoader.cpp b/Userland/Libraries/LibGfx/WebPLoader.cpp index 93a40361ae..a37066869c 100644 --- a/Userland/Libraries/LibGfx/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/WebPLoader.cpp @@ -79,6 +79,11 @@ struct VP8XHeader { u32 height; }; +struct ANIMChunk { + u32 background_color; + u16 loop_count; +}; + } struct WebPLoadingContext { @@ -333,6 +338,20 @@ static ErrorOr decode_webp_chunk_VP8X(WebPLoadingContext& context, C return VP8XHeader { has_icc, has_alpha, has_exif, has_xmp, has_animation, width, height }; } +// https://developers.google.com/speed/webp/docs/riff_container#animation +static ErrorOr decode_webp_chunk_ANIM(WebPLoadingContext& context, Chunk const& anim_chunk) +{ + VERIFY(anim_chunk.type == FourCC("ANIM")); + if (anim_chunk.data.size() < 6) + return context.error("WebPImageDecoderPlugin: ANIM chunk too small"); + + u8 const* data = anim_chunk.data.data(); + u32 background_color = (u32)data[0] | ((u32)data[1] << 8) | ((u32)data[2] << 16) | ((u32)data[3] << 24); + u16 loop_count = data[4] | (data[5] << 8); + + return ANIMChunk { background_color, loop_count }; +} + // https://developers.google.com/speed/webp/docs/riff_container#extended_file_format static ErrorOr decode_webp_extended(WebPLoadingContext& context, ReadonlyBytes chunks) { @@ -532,8 +551,19 @@ bool WebPImageDecoderPlugin::is_animated() size_t WebPImageDecoderPlugin::loop_count() { - // FIXME - return 0; + if (!is_animated()) + return 0; + + if (m_context->state < WebPLoadingContext::State::ChunksDecoded) { + if (decode_webp_chunks(*m_context).is_error()) + return 0; + } + + auto anim_or_error = decode_webp_chunk_ANIM(*m_context, m_context->animation_header_chunk.value()); + if (decode_webp_chunks(*m_context).is_error()) + return 0; + + return anim_or_error.value().loop_count; } size_t WebPImageDecoderPlugin::frame_count()