Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong frame compositing logic #110

Merged
merged 2 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ struct AnimationState {
next_frame: u32,
next_frame_start: u64,
dispose_next_frame: bool,
previous_frame_width: u32,
previous_frame_height: u32,
previous_frame_x_offset: u32,
previous_frame_y_offset: u32,
canvas: Option<Vec<u8>>,
}
impl Default for AnimationState {
Expand All @@ -242,6 +246,10 @@ impl Default for AnimationState {
next_frame: 0,
next_frame_start: 0,
dispose_next_frame: true,
previous_frame_width: 0,
previous_frame_height: 0,
previous_frame_x_offset: 0,
previous_frame_y_offset: 0,
canvas: None,
}
}
Expand Down Expand Up @@ -800,8 +808,15 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
_ => return Err(DecodingError::ChunkHeaderInvalid(chunk.to_fourcc())),
};

// fill starting canvas with clear color
if self.animation.canvas.is_none() {
self.animation.canvas = Some(vec![0; (self.width * self.height * 4) as usize]);
self.animation.canvas = {
let mut canvas = vec![0; (self.width * self.height * 4) as usize];
canvas
.chunks_exact_mut(4)
.for_each(|c| c.copy_from_slice(&info.background_color));
Some(canvas)
}
}
extended::composite_frame(
self.animation.canvas.as_mut().unwrap(),
Expand All @@ -815,8 +830,17 @@ impl<R: BufRead + Seek> WebPDecoder<R> {
frame_height,
frame_has_alpha,
use_alpha_blending,
self.animation.previous_frame_width,
self.animation.previous_frame_height,
self.animation.previous_frame_x_offset,
self.animation.previous_frame_y_offset,
);

self.animation.previous_frame_width = frame_width;
self.animation.previous_frame_height = frame_height;
self.animation.previous_frame_x_offset = frame_x;
self.animation.previous_frame_y_offset = frame_y;

self.animation.dispose_next_frame = dispose;
self.animation.next_frame_start += anmf_size + 8;
self.animation.next_frame += 1;
Expand Down
57 changes: 45 additions & 12 deletions src/extended.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub(crate) struct WebPExtendedInfo {

/// Composites a frame onto a canvas.
///
/// Starts by filling the canvas with the background color, if provided. Then copies or blends the
/// frame onto the canvas.
/// Starts by filling the rectangle occupied by the previous frame with the background
/// color, if provided. Then copies or blends the frame onto the canvas.
#[allow(clippy::too_many_arguments)]
pub(crate) fn composite_frame(
canvas: &mut [u8],
Expand All @@ -35,13 +35,17 @@ pub(crate) fn composite_frame(
frame_height: u32,
frame_has_alpha: bool,
frame_use_alpha_blending: bool,
previous_frame_width: u32,
previous_frame_height: u32,
previous_frame_offset_x: u32,
previous_frame_offset_y: u32,
) {
if frame_offset_x == 0
let frame_is_full_size = frame_offset_x == 0
&& frame_offset_y == 0
&& frame_width == canvas_width
&& frame_height == canvas_height
&& !frame_use_alpha_blending
{
&& frame_height == canvas_height;

if frame_is_full_size && !frame_use_alpha_blending {
if frame_has_alpha {
canvas.copy_from_slice(frame);
} else {
Expand All @@ -53,14 +57,43 @@ pub(crate) fn composite_frame(
return;
}

// clear rectangle occupied by previous frame
if let Some(clear_color) = clear_color {
if frame_has_alpha {
for pixel in canvas.chunks_exact_mut(4) {
pixel.copy_from_slice(&clear_color);
match (frame_is_full_size, frame_has_alpha) {
(true, true) => {
for pixel in canvas.chunks_exact_mut(4) {
pixel.copy_from_slice(&clear_color);
}
}
} else {
for pixel in canvas.chunks_exact_mut(3) {
pixel.copy_from_slice(&clear_color[..3]);
(true, false) => {
for pixel in canvas.chunks_exact_mut(3) {
pixel.copy_from_slice(&clear_color[..3]);
}
}
(false, true) => {
for y in 0..previous_frame_height as usize {
for x in 0..previous_frame_width as usize {
let canvas_index = ((x + previous_frame_offset_x as usize)
+ (y + previous_frame_offset_y as usize) * canvas_width as usize)
* 4;

let output = &mut canvas[canvas_index..][..4];
output.copy_from_slice(&clear_color);
}
}
}
(false, false) => {
for y in 0..previous_frame_height as usize {
for x in 0..previous_frame_width as usize {
// let frame_index = (x + y * frame_width as usize) * 4;
let canvas_index = ((x + previous_frame_offset_x as usize)
+ (y + previous_frame_offset_y as usize) * canvas_width as usize)
* 3;

let output = &mut canvas[canvas_index..][..3];
output.copy_from_slice(&clear_color[..3]);
}
}
}
}
}
Expand Down
Loading