From 234376468536b2170c5ba59de4b8344c2dfeeeb4 Mon Sep 17 00:00:00 2001 From: meowtec Date: Sat, 4 Nov 2023 16:21:38 +0800 Subject: [PATCH 1/2] feat: consume frame after added, for reducing memory usage --- examples/animation.rs | 57 ++++++++++-------- src/animation_encoder.rs | 126 ++++++++++++++++++++------------------- 2 files changed, 97 insertions(+), 86 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index 099bb5a..f4a68a2 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -2,41 +2,48 @@ use webp::AnimEncoder; use webp::AnimFrame; use webp::WebPConfig; fn main() { - let width = 32u32; - let height = 32u32; - fn dumy_image(width: u32, height: u32, color: [u8; 4]) -> Vec { + let width = 512u32; + let height = 512u32; + + fn dumy_image(width: u32, height: u32, frame: u32, total_frames: u32) -> Vec { let mut pixels = Vec::with_capacity(width as usize * height as usize * 4); - for _ in 0..(width * height) { - pixels.push(color[0]); //red - pixels.push(color[1]); //green - pixels.push(color[2]); //blue - pixels.push(color[3]); //alpha + for x in 0..width { + for y in 0..height { + let normalized_frame = frame as f32 / total_frames as f32; + let normalized_x = x as f32 / width as f32; + let normalized_y = y as f32 / height as f32; + + let r = ((normalized_frame + normalized_x + normalized_y) % 1.0 * 255.0) as u8; + let g = + ((normalized_frame + normalized_x + normalized_y + 0.33) % 1.0 * 255.0) as u8; + let b = + ((normalized_frame + normalized_x + normalized_y + 0.67) % 1.0 * 255.0) as u8; + + pixels.push(r); + pixels.push(g); + pixels.push(b); + pixels.push(255); // alpha channel, fully opaque + } } pixels } + let mut config = WebPConfig::new().unwrap(); config.lossless = 1; config.alpha_compression = 0; - config.quality = 75f32; + config.quality = 100f32; let mut encoder = AnimEncoder::new(width as u32, height as u32, &config); encoder.set_bgcolor([255, 0, 0, 255]); - encoder.set_loop_count(3); - let mut time_ms = 1000; + encoder.set_loop_count(0); + let mut time_ms = 0; - let v = dumy_image(width, height, [255, 0, 0, 255]); - encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms)); - time_ms += 750; - - let v = dumy_image(width, height, [0, 255, 0, 255]); - encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms)); - time_ms += 500; - - let v = dumy_image(width, height, [0, 0, 255, 255]); - encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms)); - time_ms += 250; - - let v = dumy_image(width, height, [0, 0, 0, 0]); - encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms)); + for i in 0..120 { + let image = dumy_image(width, height, i, 120); + encoder + .add_frame(AnimFrame::from_rgba(&image, width, height, time_ms)) + .unwrap(); + time_ms += 17; + } let webp = encoder.encode(); let output_path = std::path::Path::new("assets") diff --git a/src/animation_encoder.rs b/src/animation_encoder.rs index 6547c8c..3c5b14a 100644 --- a/src/animation_encoder.rs +++ b/src/animation_encoder.rs @@ -101,19 +101,34 @@ impl Into for &AnimFrame<'_> { } } pub struct AnimEncoder<'a> { - frames: Vec>, width: u32, height: u32, config: &'a WebPConfig, + mux_abi_version: i32, muxparams: WebPMuxAnimParams, + encoder: *mut WebPAnimEncoder, } impl<'a> AnimEncoder<'a> { pub fn new(width: u32, height: u32, config: &'a WebPConfig) -> Self { + let mut uninit = std::mem::MaybeUninit::::uninit(); + + let mux_abi_version = WebPGetMuxABIVersion(); + let encoder = unsafe { + WebPAnimEncoderOptionsInitInternal(uninit.as_mut_ptr(), mux_abi_version); + WebPAnimEncoderNewInternal( + width as i32, + height as i32, + uninit.as_ptr(), + mux_abi_version, + ) + }; + Self { - frames: vec![], width, height, config, + encoder, + mux_abi_version, muxparams: WebPMuxAnimParams { bgcolor: 0, loop_count: 0, @@ -130,14 +145,59 @@ impl<'a> AnimEncoder<'a> { pub fn set_loop_count(&mut self, loop_count: i32) { self.muxparams.loop_count = loop_count; } - pub fn add_frame(&mut self, frame: AnimFrame<'a>) { - self.frames.push(frame); + pub fn add_frame(&mut self, frame: AnimFrame<'_>) -> Result<(), AnimEncodeError> { + unsafe { + let mut pic = crate::new_picture(frame.image, frame.layout, self.width, self.height); + let config = frame.config.unwrap_or(self.config); + let ok = WebPAnimEncoderAdd( + self.encoder, + &mut *pic as *mut _, + frame.timestamp as std::os::raw::c_int, + config, + ); + if ok == 0 { + return Err(AnimEncodeError::WebPEncodingError(pic.error_code)); + } + } + + Ok(()) } pub fn encode(&self) -> WebPMemory { self.try_encode().unwrap() } pub fn try_encode(&self) -> Result { - unsafe { anim_encode(&self) } + let encoder = self.encoder; + unsafe { + WebPAnimEncoderAdd(encoder, std::ptr::null_mut(), 0, std::ptr::null()); + + let mut webp_data = std::mem::MaybeUninit::::uninit(); + let ok = WebPAnimEncoderAssemble(encoder, webp_data.as_mut_ptr()); + if ok == 0 { + //ok == false + let cstring = WebPAnimEncoderGetError(encoder); + let cstring = CString::from_raw(cstring as *mut _); + let string = cstring.to_string_lossy().to_string(); + return Err(AnimEncodeError::WebPAnimEncoderGetError(string)); + } + let mux = WebPMuxCreateInternal(webp_data.as_ptr(), 1, self.mux_abi_version); + let mux_error = WebPMuxSetAnimationParams(mux, &self.muxparams); + if mux_error != WebPMuxError::WEBP_MUX_OK { + return Err(AnimEncodeError::WebPMuxError(mux_error)); + } + let mut raw_data: WebPData = webp_data.assume_init(); + WebPDataClear(&mut raw_data); + let mut webp_data = std::mem::MaybeUninit::::uninit(); + WebPMuxAssemble(mux, webp_data.as_mut_ptr()); + WebPMuxDelete(mux); + let raw_data: WebPData = webp_data.assume_init(); + Ok(WebPMemory(raw_data.bytes as *mut u8, raw_data.size)) + } + } +} + +impl Drop for AnimEncoder<'_> { + fn drop(&mut self) { + unsafe { WebPAnimEncoderDelete(self.encoder) }; } } @@ -147,59 +207,3 @@ pub enum AnimEncodeError { WebPMuxError(WebPMuxError), WebPAnimEncoderGetError(String), } -unsafe fn anim_encode(all_frame: &AnimEncoder) -> Result { - let width = all_frame.width; - let height = all_frame.height; - let mut uninit = std::mem::MaybeUninit::::uninit(); - - let mux_abi_version = WebPGetMuxABIVersion(); - WebPAnimEncoderOptionsInitInternal(uninit.as_mut_ptr(), mux_abi_version); - let encoder = WebPAnimEncoderNewInternal( - width as i32, - height as i32, - uninit.as_ptr(), - mux_abi_version, - ); - let mut frame_pictures = vec![]; - for frame in all_frame.frames.iter() { - let mut pic = crate::new_picture(frame.image, frame.layout, width, height); - let config = frame.config.unwrap_or(all_frame.config); - let ok = WebPAnimEncoderAdd( - encoder, - &mut *pic as *mut _, - frame.timestamp as std::os::raw::c_int, - config, - ); - if ok == 0 { - //ok == false - WebPAnimEncoderDelete(encoder); - return Err(AnimEncodeError::WebPEncodingError(pic.error_code)); - } - frame_pictures.push(pic); - } - WebPAnimEncoderAdd(encoder, std::ptr::null_mut(), 0, std::ptr::null()); - - let mut webp_data = std::mem::MaybeUninit::::uninit(); - let ok = WebPAnimEncoderAssemble(encoder, webp_data.as_mut_ptr()); - if ok == 0 { - //ok == false - let cstring = WebPAnimEncoderGetError(encoder); - let cstring = CString::from_raw(cstring as *mut _); - let string = cstring.to_string_lossy().to_string(); - WebPAnimEncoderDelete(encoder); - return Err(AnimEncodeError::WebPAnimEncoderGetError(string)); - } - WebPAnimEncoderDelete(encoder); - let mux = WebPMuxCreateInternal(webp_data.as_ptr(), 1, mux_abi_version); - let mux_error = WebPMuxSetAnimationParams(mux, &all_frame.muxparams); - if mux_error != WebPMuxError::WEBP_MUX_OK { - return Err(AnimEncodeError::WebPMuxError(mux_error)); - } - let mut raw_data: WebPData = webp_data.assume_init(); - WebPDataClear(&mut raw_data); - let mut webp_data = std::mem::MaybeUninit::::uninit(); - WebPMuxAssemble(mux, webp_data.as_mut_ptr()); - WebPMuxDelete(mux); - let raw_data: WebPData = webp_data.assume_init(); - Ok(WebPMemory(raw_data.bytes as *mut u8, raw_data.size)) -} From 8ad7c9d458891a614afe8cb3eab408fc18aa0231 Mon Sep 17 00:00:00 2001 From: meowtec Date: Thu, 28 Mar 2024 23:58:52 +0800 Subject: [PATCH 2/2] Update image crate --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56cce62..f48ed22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,12 @@ keywords = ["image", "webp", "conversion"] categories = ["external-ffi-bindings"] [dependencies] -libwebp-sys = "0.9.3" -image = { version = "^0.24.0", default-features = false, optional = true } +libwebp-sys = "0.9.5" +image = { version = "0.25", default-features = false, optional = true } [features] default = ["img"] img = [ "image" ] [dev-dependencies] -image = "0.24" +image = "0.25"