diff --git a/Cargo.lock b/Cargo.lock index cb6709bb..44d47c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -869,6 +869,17 @@ dependencies = [ "weezl", ] +[[package]] +name = "gif-dispose" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1aa07391f3d9c279f388cea6faf291555dd891df59bed01d4378583df946ac" +dependencies = [ + "gif", + "imgref", + "rgb", +] + [[package]] name = "gimli" version = "0.29.0" @@ -1163,6 +1174,7 @@ dependencies = [ "fnv", "getopts", "gif", + "gif-dispose", "hex", "hyper", "imageflow_c_components", @@ -1187,7 +1199,7 @@ dependencies = [ "multiversion", "num 0.4.3", "petgraph", - "rand 0.4.6", + "rand 0.8.5", "rgb", "rustface", "serde", diff --git a/imageflow_core/Cargo.toml b/imageflow_core/Cargo.toml index 03cef015..299dff92 100644 --- a/imageflow_core/Cargo.toml +++ b/imageflow_core/Cargo.toml @@ -35,6 +35,7 @@ slotmap = "1" base64 = "0.22" hex = "0.4" gif = "0.13.1" +gif-dispose = "5.0.1" rgb = { version = "0.8", features = ["argb"] } imagequant = "4" lodepng = "3" diff --git a/imageflow_core/src/codecs/gif/mod.rs b/imageflow_core/src/codecs/gif/mod.rs index 068275bc..418e4b49 100644 --- a/imageflow_core/src/codecs/gif/mod.rs +++ b/imageflow_core/src/codecs/gif/mod.rs @@ -6,15 +6,12 @@ use crate::ffi::BitmapBgra; use imageflow_types::collections::AddRemoveSet; use crate::io::IoProxy; use uuid::Uuid; -use imageflow_types::{IoDirection, PixelLayout}; +use imageflow_types::{FrameSizeLimit, IoDirection, PixelLayout}; use super::*; use std::any::Any; -mod disposal; -mod subimage; -mod screen; -mod bgra; -use self::bgra::BGRA8; -use self::screen::Screen; + +use rgb::alt::BGRA8; +use gif_dispose::Screen; use crate::gif::Frame; use std::rc::Rc; use lcms2_sys::cmsAllocProfileSequenceDescription; @@ -22,32 +19,36 @@ use crate::io::IoProxyProxy; use crate::io::IoProxyRef; use crate::graphics::bitmaps::{BitmapKey, ColorSpace, BitmapCompositing}; + pub struct GifDecoder{ reader: ::gif::Decoder, screen: Screen, buffer: Option>, last_frame: Option>, - next_frame: Option> + next_frame: Option>, + state: Option> } impl GifDecoder { pub fn create(c: &Context, io: IoProxy, io_id: i32) -> Result { + let max_pixels = c.security.max_frame_size.clone().or(Some(FrameSizeLimit{ w: 8000, h: 8000, megapixels: 16f32 })).unwrap().megapixels * 1000f32 * 1000f32; let mut options = ::gif::Decoder::::build(); options.allow_unknown_blocks(true); - options.set_memory_limit(::gif::MemoryLimit::Bytes(std::num::NonZeroU64::new(8000*8000).unwrap())); + options.set_memory_limit(::gif::MemoryLimit::Bytes(std::num::NonZeroU64::new(max_pixels as u64).unwrap())); options.set_color_output(::gif::ColorOutput::Indexed); // Important let reader = options.read_info(io) .map_err(|e| FlowError::from(e).at(here!()))?; - let screen = Screen::new(&reader); + let screen = Screen::new_decoder(&reader); Ok(GifDecoder{ reader, screen, + state: None, buffer: None, last_frame: None, next_frame: None @@ -65,14 +66,13 @@ impl GifDecoder { fn create_bitmap_from_screen(&self, c: &Context) -> Result{ // Create output bitmap and copy to it unsafe { - let w = self.screen.width; - let h = self.screen.height; + let w = self.screen.width(); + let h = self.screen.height(); let mut bitmaps = c.borrow_bitmaps_mut() .map_err(|e| e.at(here!()))?; - let bitmap_key = bitmaps.create_bitmap_u8(w as u32, - h as u32, + let bitmap_key = bitmaps.create_bitmap_u8(w as u32, h as u32, PixelLayout::BGRA, false, true, @@ -89,8 +89,14 @@ impl GifDecoder { let copy_mut = &mut copy; for row in 0..h{ + let row_bytes = bitmap.get_window_u8().unwrap().row_mut(row as u32).unwrap(); + + let row_pixels = bytemuck::cast_mut::<[u8],BGRA8>(row_bytes); + self.screen. + rgb let to_row: &mut [BGRA8] = std::slice::from_raw_parts_mut(copy_mut.pixels.offset(copy_mut.stride as isize * row as isize) as *mut BGRA8, w as usize); - to_row.copy_from_slice(&self.screen.pixels[row * w..(row + 1) * w]); + self.screen. + to_row.copy_from_slice(&self.screen.pixels_rgba()[row * w..(row + 1) * w]); } Ok(bitmap_key) } diff --git a/imageflow_core/tests/visuals.rs b/imageflow_core/tests/visuals.rs index 2ac2d740..4c39cb3e 100644 --- a/imageflow_core/tests/visuals.rs +++ b/imageflow_core/tests/visuals.rs @@ -826,6 +826,16 @@ fn test_read_gif_eof() { } +#[test] +fn test_read_animated_transparent_gif() { + let matched = compare(Some(IoTestEnum::Url("https://s3-us-west-2.amazonaws.com/imageflow-resources/test_inputs/transparent-animated.gif".to_owned())), 500, + "transparent-animated.gif", POPULATE_CHECKSUMS, DEBUG_GRAPH, vec![ + Node::Decode {io_id: 0, commands: None} + ] + ); + assert!(matched); +} + diff --git a/imageflow_core/tests/visuals/checksums.json b/imageflow_core/tests/visuals/checksums.json index 0913e289..b85f9fa6 100644 --- a/imageflow_core/tests/visuals/checksums.json +++ b/imageflow_core/tests/visuals/checksums.json @@ -84,6 +84,7 @@ "test_rot_90_and_red_dot_command_string": "0C790C6600AFBBADA_0AE4839D1D9B04C57", "test_round_corners_command_string": "05218274046A5989B_0BF80F0AE71CD9A63", "test_webp_to_webp_quality": "0AFEF1F4539220FD1.webp", + "transparent-animated.gif": "0B4DF2CB7EDFEC674_0BF7B7FA413ED3B60", "transparent_png_to_jpeg": "0DC709F50C5148224.jpg", "transparent_png_to_jpeg_constrained": "0D6B7D34193494A6C.jpg", "transparent_png_to_png": "0A839287BD1939BE8.png",