From a9a7215e6fe4eefd509730c44a3be38a175867fa Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Wed, 11 Feb 2026 06:15:49 +0530 Subject: [PATCH 1/2] dependencies updated --- Cargo.toml | 15 +++---- examples/decode_animation.rs | 20 +++++----- src/decoder.rs | 26 +++++++++--- src/encoder.rs | 59 +++++++++++++++------------ src/encoder_config.rs | 22 +++++++---- src/lib.rs | 77 +++++++++++++++++++++++++++++------- 6 files changed, 149 insertions(+), 70 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a861c43..9c903d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,21 +10,22 @@ A high-level Rust wrapper for decoding and encoding WebP animations """ keywords = ["webp", "webp-animation", "decoder", "encoder"] categories = ["multimedia::images", "multimedia", "api-bindings"] -edition = "2018" -rust-version = "1.47" +edition = "2024" +rust-version = "1.93" [dependencies] -image = { version = "0.24.1", default_features = false, optional = true } +image = { version = "0.25.9", default-features = false, optional = true } log = "0.4.14" [dependencies.libwebp-sys2] -version = "0.1.9" +version = "0.2.0" features = ["0_5", "0_6", "1_2", "demux", "mux"] [dev-dependencies] -image = { version = "0.24.1", default_features = false, features = ["png"] } -imageproc = "0.23.0" -env_logger = "0.10.0" +image = { version = "0.25.9", default-features = false, features = ["png"] } +imageproc = "0.26.0" +env_logger = "0.11.8" [features] static = ["libwebp-sys2/static"] +image = ["dep:image"] \ No newline at end of file diff --git a/examples/decode_animation.rs b/examples/decode_animation.rs index 675cdc7..0706929 100644 --- a/examples/decode_animation.rs +++ b/examples/decode_animation.rs @@ -8,16 +8,18 @@ fn main() { let decoder = Decoder::new(&buffer).unwrap(); for frame in decoder.into_iter() { - assert_eq!(frame.dimensions(), (400, 400)); - assert_eq!(frame.data().len(), 400 * 400 * 4); // w * h * rgba + #[cfg(feature = "image")] + let (dimensions, data_len) = { + let dims = frame.dimensions(); + let len = frame.data().len(); + let image = frame.into_image().unwrap(); + assert_eq!(image.dimensions(), (400, 400)); + (dims, len) + }; - #[cfg(features = "image")] - assert_eq!(frame.into_image().unwrap().dimensions(), (400, 400)); + #[cfg(not(feature = "image"))] + let (dimensions, data_len) = { (frame.dimensions(), frame.data().len()) }; - info!( - "Frame, dimensions={:?}, data_len={}", - frame.dimensions(), - frame.data().len() - ); + info!("Frame, dimensions={:?}, data_len={}", dimensions, data_len); } } diff --git a/src/decoder.rs b/src/decoder.rs index a6800b4..7dbe074 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -172,7 +172,16 @@ impl<'a> Debug for Decoder<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let info = &self.info; - write!(f, "Decoder {{ buffer: {}b, info: {{ w: {}, h: {}, loop_cnt: {}, bgcolor: 0x{:x}, frame_count: {} }} }}", self.buffer.len(), info.canvas_width, info.canvas_height, info.loop_count, info.bgcolor, info.frame_count) + write!( + f, + "Decoder {{ buffer: {}b, info: {{ w: {}, h: {}, loop_cnt: {}, bgcolor: 0x{:x}, frame_count: {} }} }}", + self.buffer.len(), + info.canvas_width, + info.canvas_height, + info.loop_count, + info.bgcolor, + info.frame_count + ) } } @@ -250,12 +259,16 @@ impl<'a> Iterator for DecoderIterator<'a> { } != 1 { // "False if any of the arguments are NULL, or if there is a parsing or decoding error, or if there are no more frames. Otherwise, returns true." - log::warn!("webp::WebPAnimDecoderGetNext did not return success - frame parsing failed, parsing/decoding error?"); + log::warn!( + "webp::WebPAnimDecoderGetNext did not return success - frame parsing failed, parsing/decoding error?" + ); return None; } if output_buffer.is_null() { - log::error!("webp::WebPAnimDecoderGetNext returned null output ptr, can not decode a frame. This should not happen"); + log::error!( + "webp::WebPAnimDecoderGetNext returned null output ptr, can not decode a frame. This should not happen" + ); return None; } @@ -321,7 +334,7 @@ mod tests { fn test_decode_to_image() { use std::io::Cursor; - use image::{codecs::png::PngDecoder, DynamicImage, ImageDecoder as _, ImageOutputFormat}; + use image::{DynamicImage, ImageDecoder as _, ImageFormat, codecs::png::PngDecoder}; let buffer = get_animated_buffer(); let decoder = Decoder::new(&buffer).unwrap(); @@ -332,12 +345,13 @@ mod tests { let mut buf = Cursor::new(Vec::new()); DynamicImage::ImageRgba8(image) - .write_to(&mut buf, ImageOutputFormat::Png) + .write_to(&mut buf, ImageFormat::Png) .unwrap(); let buf = buf.into_inner(); + let cursor = Cursor::new(&buf); - let png_decoder = PngDecoder::new(&buf[..]).unwrap(); + let png_decoder = PngDecoder::new(cursor).unwrap(); assert_eq!(png_decoder.dimensions(), (400, 400)); } diff --git a/src/encoder.rs b/src/encoder.rs index 369ee23..e7fd744 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -164,21 +164,25 @@ impl Encoder { self.frame.set_data(data, self.options.color_mode)?; + // keep config alive + let tmp_config; + let config_ptr = match config { + Some(cfg) => { + tmp_config = cfg.to_config_container()?; + &*tmp_config // Deref or explicit borrow + } + None => match &self.encoding_config { + Some(cfg) => &**cfg, + None => ptr::null(), + }, + }; + if unsafe { webp::WebPAnimEncoderAdd( self.encoder_wr.encoder, self.frame.as_webp_picture_ref(), timestamp, - match config { - Some(config) => { - let config = config.to_config_container()?; - config.as_ptr() - } - None => match &self.encoding_config { - Some(config) => config.as_ptr(), - None => std::ptr::null(), - }, - }, + config_ptr, ) } == 0 { @@ -186,7 +190,6 @@ impl Encoder { } self.previous_timestamp = timestamp; - log::trace!( "Add a frame at timestamp {}ms, {} bytes", timestamp, @@ -474,16 +477,18 @@ mod tests { #[test] fn test_wrong_encoding_config() { let mut encoder = Encoder::new((4, 4)).unwrap(); - assert!(encoder - .add_frame_with_config( - &[0u8; 4 * 4 * 4], - 0, - &EncodingConfig { - quality: 100., - ..Default::default() - }, - ) - .is_ok()); + assert!( + encoder + .add_frame_with_config( + &[0u8; 4 * 4 * 4], + 0, + &EncodingConfig { + quality: 100., + ..Default::default() + }, + ) + .is_ok() + ); assert_eq!( encoder @@ -529,11 +534,13 @@ mod tests { Error::InvalidEncodingConfig ); - assert!(add_lossy_frame(LossyEncodingConfig { - filter_sharpness: 7, - ..Default::default() - }) - .is_ok()); + assert!( + add_lossy_frame(LossyEncodingConfig { + filter_sharpness: 7, + ..Default::default() + }) + .is_ok() + ); } fn add_lossy_frame(lossy_config: LossyEncodingConfig) -> Result<(), Error> { diff --git a/src/encoder_config.rs b/src/encoder_config.rs index f0af45b..436f02c 100644 --- a/src/encoder_config.rs +++ b/src/encoder_config.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{mem, ops::Deref}; use crate::{ColorMode, Error}; @@ -314,9 +314,12 @@ pub(crate) struct ConfigContainer { impl ConfigContainer { pub fn new(config: &EncodingConfig) -> Result { let mut webp_config = unsafe { - let mut config = mem::zeroed(); - webp::WebPConfigInit(&mut config); - config + let mut cfg = mem::zeroed(); + let ok = webp::WebPConfigInit(&mut cfg); + if ok == 0 { + return Err(Error::InvalidEncodingConfig); + } + cfg }; config.apply_to(&mut webp_config); @@ -329,8 +332,12 @@ impl ConfigContainer { config: webp_config, }) } +} + +impl Deref for ConfigContainer { + type Target = webp::WebPConfig; - pub fn as_ptr(&self) -> &webp::WebPConfig { + fn deref(&self) -> &Self::Target { &self.config } } @@ -338,18 +345,17 @@ impl ConfigContainer { #[cfg(test)] mod tests { use super::*; - #[test] fn test_config_defaults() { let default_webp_config = unsafe { let mut config = mem::zeroed(); - webp::WebPConfigInit(&mut config); + assert_ne!(webp::WebPConfigInit(&mut config), 0); config }; let config = ConfigContainer::new(&EncodingConfig::default()).unwrap(); - let left = config.as_ptr(); + let left = &*config; let def = &default_webp_config; // custom-set diff --git a/src/lib.rs b/src/lib.rs index 5dc9356..8fef8ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,22 +114,71 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::OptionsInitFailed => write!(f, "OptionsInitFailed: Initializing webp options failed, internal (memory allocation?) failure"), - Error::DecodeFailed => write!(f, "DecodeFailed: Could not decode input bytes, possibly malformed data"), - Error::DecoderGetInfoFailed => write!(f, "DecoderGetInfoFailed: Decoder could not get metadata of webp stream. Corrupt data?"), - Error::TooLargeCanvas(width, height, max_size) => write!(f, "TooLargeCanvas: Decodable canvas is too large ({} x {} = {} pixels). For now, size is limited to 3840 * 2160 = {} pixels", width, height, width * height, max_size), - Error::EncoderCreateFailed => write!(f, "EncoderCreateFailed: Encoder create failed. Wrong options combination?"), - Error::BufferSizeFailed(expected, received) => write!(f, "BufferSizeFailed: Expected (width * height * 4 = {}) bytes as input buffer, got {} bytes", expected, received), - Error::PictureImportFailed => write!(f, "PictureImportFailed: Raw data could not be converted into webp frame by underlying libwebp library"), - Error::EncoderAddFailed => write!(f, "EncoderAddFailed: Frame could not be added to webp stream by underlying libwebp library"), - Error::WrongColorMode(requested, expected) => write!(f, "WrongColorMode: Requested image in {:?} format but underlying is stored as {:?}", expected, requested), - Error::TimestampMustBeHigherThanPrevious(requested, previous) => write!(f, "TimestampMustBeHigherThanPrevious: Supplied timestamp (got {}) must be higher than {}", requested, previous), - Error::TimestampMustBeEqualOrHigherThanPrevious(requested, previous) => write!(f, "TimestampMustBeEqualOrHigherThanPrevious: Supplied timestamp (got {}) must be higher or equal to {}", requested, previous), - Error::EncoderAssmebleFailed => write!(f, "EncoderAssmebleFailed: Encoder webp assembly failed"), - Error::DimensionsMustbePositive => write!(f, "DimensionsMustbePositive: Supplied dimensions must be positive"), + Error::OptionsInitFailed => write!( + f, + "OptionsInitFailed: Initializing webp options failed, internal (memory allocation?) failure" + ), + Error::DecodeFailed => write!( + f, + "DecodeFailed: Could not decode input bytes, possibly malformed data" + ), + Error::DecoderGetInfoFailed => write!( + f, + "DecoderGetInfoFailed: Decoder could not get metadata of webp stream. Corrupt data?" + ), + Error::TooLargeCanvas(width, height, max_size) => write!( + f, + "TooLargeCanvas: Decodable canvas is too large ({} x {} = {} pixels). For now, size is limited to 3840 * 2160 = {} pixels", + width, + height, + width * height, + max_size + ), + Error::EncoderCreateFailed => write!( + f, + "EncoderCreateFailed: Encoder create failed. Wrong options combination?" + ), + Error::BufferSizeFailed(expected, received) => write!( + f, + "BufferSizeFailed: Expected (width * height * 4 = {}) bytes as input buffer, got {} bytes", + expected, received + ), + Error::PictureImportFailed => write!( + f, + "PictureImportFailed: Raw data could not be converted into webp frame by underlying libwebp library" + ), + Error::EncoderAddFailed => write!( + f, + "EncoderAddFailed: Frame could not be added to webp stream by underlying libwebp library" + ), + Error::WrongColorMode(requested, expected) => write!( + f, + "WrongColorMode: Requested image in {:?} format but underlying is stored as {:?}", + expected, requested + ), + Error::TimestampMustBeHigherThanPrevious(requested, previous) => write!( + f, + "TimestampMustBeHigherThanPrevious: Supplied timestamp (got {}) must be higher than {}", + requested, previous + ), + Error::TimestampMustBeEqualOrHigherThanPrevious(requested, previous) => write!( + f, + "TimestampMustBeEqualOrHigherThanPrevious: Supplied timestamp (got {}) must be higher or equal to {}", + requested, previous + ), + Error::EncoderAssmebleFailed => { + write!(f, "EncoderAssmebleFailed: Encoder webp assembly failed") + } + Error::DimensionsMustbePositive => write!( + f, + "DimensionsMustbePositive: Supplied dimensions must be positive" + ), Error::NoFramesAdded => write!(f, "NoFramesAdded: No frames have been added yet"), Error::ZeroSizeBuffer => write!(f, "ZeroSizeBuffer: Buffer contains no data"), - Error::InvalidEncodingConfig => write!(f, "InvalidEncodingConfig: encoding configuration validation failed") + Error::InvalidEncodingConfig => write!( + f, + "InvalidEncodingConfig: encoding configuration validation failed" + ), } } } From 7992cd6405bd91e612d5cc2dbea66f21bc28a34a Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Wed, 11 Feb 2026 06:31:34 +0530 Subject: [PATCH 2/2] fuzz dependencies updated --- fuzz/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index d12f94b..357fc05 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -2,13 +2,13 @@ name = "fuzz" version = "0.1.0" authors = ["Mika Vatanen "] -edition = "2018" +edition = "2024" [package.metadata] cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.3.0" +libfuzzer-sys = "0.4.12" webp-animation = { path = ".." } [[bin]]