diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp index 4b7e85243c..2d0b23f238 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoader.cpp @@ -311,10 +311,8 @@ static ErrorOr decode_webp_chunk_ANMF(WebPLoadingContext& context, RI u32 frame_duration = (u32)anmf_chunk[12] | ((u32)anmf_chunk[13] << 8) | ((u32)anmf_chunk[14] << 16); u8 flags = anmf_chunk[15]; - auto blending_method = static_cast((flags >> 1) & 1); - auto disposal_method = static_cast(flags & 1); - - ReadonlyBytes frame_data = anmf_chunk.data().slice(16); + auto blending_method = static_cast((flags >> 1) & 1); + auto disposal_method = static_cast(flags & 1); dbgln_if(WEBP_DEBUG, "frame_x {} frame_y {} frame_width {} frame_height {} frame_duration {} blending_method {} disposal_method {}", frame_x, frame_y, frame_width, frame_height, frame_duration, (int)blending_method, (int)disposal_method); @@ -326,7 +324,9 @@ static ErrorOr decode_webp_chunk_ANMF(WebPLoadingContext& context, RI if (frame_x + frame_width > context.vp8x_header.width || frame_y + frame_height > context.vp8x_header.height) return Error::from_string_literal("WebPImageDecoderPlugin: ANMF dimensions out of bounds"); - return ANMFChunk { frame_x, frame_y, frame_width, frame_height, frame_duration, blending_method, disposal_method, frame_data }; + auto header = ANMFChunkHeader { frame_x, frame_y, frame_width, frame_height, frame_duration, blending_method, disposal_method }; + ReadonlyBytes frame_data = anmf_chunk.data().slice(16); + return ANMFChunk { header, frame_data }; } static ErrorOr decode_webp_set_image_data(Optional alpha, Optional image_data) @@ -561,28 +561,29 @@ static ErrorOr decode_webp_animation_frame(WebPLoadingCont for (size_t i = start_frame; i <= frame_index; ++i) { dbgln_if(WEBP_DEBUG, "drawing frame {} to produce frame {}", i, frame_index); - auto const& frame_description = context.animation_frame_chunks_data.value()[i]; + auto const& frame = context.animation_frame_chunks_data.value()[i]; + auto const& frame_description = frame.header; if (i > 0) { - auto const& previous_frame = context.animation_frame_chunks_data.value()[i - 1]; - if (previous_frame.disposal_method == ANMFChunk::DisposalMethod::DisposeToBackgroundColor) + auto const& previous_frame = context.animation_frame_chunks_data.value()[i - 1].header; + if (previous_frame.disposal_method == ANMFChunkHeader::DisposalMethod::DisposeToBackgroundColor) painter.clear_rect({ previous_frame.frame_x, previous_frame.frame_y, previous_frame.frame_width, previous_frame.frame_height }, clear_color); } - auto frame_image_data = TRY(decode_webp_animation_frame_image_data(frame_description)); + auto frame_image_data = TRY(decode_webp_animation_frame_image_data(frame)); auto frame_bitmap = TRY(decode_webp_image_data(context, frame_image_data)); if (static_cast(frame_bitmap->width()) != frame_description.frame_width || static_cast(frame_bitmap->height()) != frame_description.frame_height) return Error::from_string_literal("WebPImageDecoderPlugin: decoded frame bitmap size doesn't match frame description size"); // FIXME: "Alpha-blending SHOULD be done in linear color space..." - bool apply_alpha = frame_description.blending_method == ANMFChunk::BlendingMethod::UseAlphaBlending; + bool apply_alpha = frame_description.blending_method == ANMFChunkHeader::BlendingMethod::UseAlphaBlending; painter.blit({ frame_description.frame_x, frame_description.frame_y }, *frame_bitmap, { {}, frame_bitmap->size() }, /*opacity=*/1.0, apply_alpha); context.current_frame = i; context.state = WebPLoadingContext::State::BitmapDecoded; } - return ImageFrameDescriptor { context.bitmap, static_cast(context.animation_frame_chunks_data.value()[frame_index].frame_duration_in_milliseconds) }; + return ImageFrameDescriptor { context.bitmap, static_cast(context.animation_frame_chunks_data.value()[frame_index].header.frame_duration_in_milliseconds) }; } WebPImageDecoderPlugin::WebPImageDecoderPlugin(ReadonlyBytes data, OwnPtr context) diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPShared.h b/Userland/Libraries/LibGfx/ImageFormats/WebPShared.h index a56d6b36ab..603b561e66 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPShared.h +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPShared.h @@ -26,7 +26,7 @@ struct ANIMChunk { u16 loop_count { 0 }; }; -struct ANMFChunk { +struct ANMFChunkHeader { u32 frame_x { 0 }; u32 frame_y { 0 }; u32 frame_width { 0 }; @@ -44,7 +44,10 @@ struct ANMFChunk { DisposeToBackgroundColor = 1, }; DisposalMethod disposal_method { DisposalMethod::DoNotDispose }; +}; +struct ANMFChunk { + ANMFChunkHeader header; ReadonlyBytes frame_data; }; diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPWriter.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPWriter.cpp index 3c7ade9140..c88d498b97 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPWriter.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPWriter.cpp @@ -370,7 +370,7 @@ static ErrorOr align_to_two(SeekableStream& stream) return align_to_two(stream, TRY(stream.tell())); } -static ErrorOr write_ANMF_chunk(Stream& stream, ANMFChunk const& chunk) +static ErrorOr write_ANMF_chunk(Stream& stream, ANMFChunkHeader const& chunk, ReadonlyBytes frame_data) { if (chunk.frame_width > (1 << 24) || chunk.frame_height > (1 << 24)) return Error::from_string_literal("WebP dimensions too large for ANMF chunk"); @@ -384,7 +384,7 @@ static ErrorOr write_ANMF_chunk(Stream& stream, ANMFChunk const& chunk) dbgln_if(WEBP_DEBUG, "writing ANMF frame_x {} frame_y {} frame_width {} frame_height {} frame_duration {} blending_method {} disposal_method {}", chunk.frame_x, chunk.frame_y, chunk.frame_width, chunk.frame_height, chunk.frame_duration_in_milliseconds, (int)chunk.blending_method, (int)chunk.disposal_method); - TRY(write_chunk_header(stream, "ANMF"sv, 16 + chunk.frame_data.size())); + TRY(write_chunk_header(stream, "ANMF"sv, 16 + frame_data.size())); LittleEndianOutputBitStream bit_stream { MaybeOwned(stream) }; @@ -414,11 +414,11 @@ static ErrorOr write_ANMF_chunk(Stream& stream, ANMFChunk const& chunk) // MUST be 0. Readers MUST ignore this field." // "Blending method (B): 1 bit" - if (chunk.blending_method == ANMFChunk::BlendingMethod::DoNotBlend) + if (chunk.blending_method == ANMFChunkHeader::BlendingMethod::DoNotBlend) flags |= 0x2; // "Disposal method (D): 1 bit" - if (chunk.disposal_method == ANMFChunk::DisposalMethod::DisposeToBackgroundColor) + if (chunk.disposal_method == ANMFChunkHeader::DisposalMethod::DisposeToBackgroundColor) flags |= 0x1; TRY(bit_stream.write_bits(flags, 8u)); @@ -426,9 +426,9 @@ static ErrorOr write_ANMF_chunk(Stream& stream, ANMFChunk const& chunk) // FIXME: Make ~LittleEndianOutputBitStream do this, or make it VERIFY() that it has happened at least. TRY(bit_stream.flush_buffer_to_stream()); - TRY(stream.write_until_depleted(chunk.frame_data)); + TRY(stream.write_until_depleted(frame_data)); - if (chunk.frame_data.size() % 2 != 0) + if (frame_data.size() % 2 != 0) TRY(stream.write_value(0)); return {}; @@ -447,17 +447,16 @@ ErrorOr WebPAnimationWriter::add_frame(Bitmap& bitmap, int duration_ms, In TRY(write_VP8L_chunk(vp8l_chunk_stream, bitmap.width(), bitmap.height(), true, vp8l_data_bytes)); auto vp8l_chunk_bytes = TRY(vp8l_chunk_stream.read_until_eof()); - ANMFChunk chunk; + ANMFChunkHeader chunk; chunk.frame_x = static_cast(at.x()); chunk.frame_y = static_cast(at.y()); chunk.frame_width = static_cast(bitmap.width()); chunk.frame_height = static_cast(bitmap.height()); chunk.frame_duration_in_milliseconds = static_cast(duration_ms); - chunk.blending_method = ANMFChunk::BlendingMethod::DoNotBlend; - chunk.disposal_method = ANMFChunk::DisposalMethod::DoNotDispose; - chunk.frame_data = vp8l_chunk_bytes; + chunk.blending_method = ANMFChunkHeader::BlendingMethod::DoNotBlend; + chunk.disposal_method = ANMFChunkHeader::DisposalMethod::DoNotDispose; - TRY(write_ANMF_chunk(m_stream, chunk)); + TRY(write_ANMF_chunk(m_stream, chunk, vp8l_chunk_bytes)); TRY(update_size_in_header());