Skip to content

Commit

Permalink
Merge pull request #110 from Juliapixel/compositing_fix
Browse files Browse the repository at this point in the history
Fix wrong frame compositing logic
  • Loading branch information
kornelski authored Sep 18, 2024
2 parents ca1fc02 + 413a954 commit ce638c7
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 13 deletions.
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

0 comments on commit ce638c7

Please sign in to comment.