From 4f8deed61bbfeedd6421f1d5cb1ca78b4c9d258a Mon Sep 17 00:00:00 2001 From: Lucas de Linhares Date: Mon, 28 Jul 2025 16:11:34 +0100 Subject: [PATCH 1/4] update to 2024 edition and fix clippy warnings --- Cargo.toml | 6 +- examples/animation.rs | 2 +- examples/animation_decode.rs | 8 +-- src/animation_decoder.rs | 135 +++++++++++++++++++---------------- src/animation_encoder.rs | 118 +++++++++++++++--------------- src/decoder.rs | 3 +- src/encoder.rs | 42 +++++------ src/lib.rs | 2 +- src/shared.rs | 4 +- 9 files changed, 167 insertions(+), 153 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0f87db6..510c0d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "webp" -version = "0.3.0" +version = "0.3.1" authors = ["Jared Forth "] -edition = "2018" +edition = "2024" description = "WebP conversion library." @@ -20,7 +20,7 @@ image = { version = "^0.25.0", default-features = false, optional = true } [features] default = ["img"] -img = [ "image" ] +img = ["image"] [dev-dependencies] image = "0.25" diff --git a/examples/animation.rs b/examples/animation.rs index 099bb5a..01e209a 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -18,7 +18,7 @@ fn main() { config.lossless = 1; config.alpha_compression = 0; config.quality = 75f32; - let mut encoder = AnimEncoder::new(width as u32, height as u32, &config); + let mut encoder = AnimEncoder::new(width, height, &config); encoder.set_bgcolor([255, 0, 0, 255]); encoder.set_loop_count(3); let mut time_ms = 1000; diff --git a/examples/animation_decode.rs b/examples/animation_decode.rs index 7697f3a..960790c 100644 --- a/examples/animation_decode.rs +++ b/examples/animation_decode.rs @@ -36,12 +36,11 @@ fn main() { let webp = std::fs::read(input).unwrap(); match AnimDecoder::new(&webp).decode() { Ok(frames) => { - let mut file_number = 0; println!("has_animation {}", frames.has_animation()); println!("loop_count {}", frames.loop_count); println!("bg_color {}", frames.bg_color); let mut last_ms = 0; - for f in frames.into_iter() { + for (file_number, f) in frames.into_iter().enumerate() { let delay_ms = f.get_time_ms() - last_ms; println!( "{}x{} {:?} time{}ms delay{}ms", @@ -54,14 +53,13 @@ fn main() { last_ms += delay_ms; let webp = Encoder::from(&f).encode_simple(true, 100f32); let output = std::path::Path::new("assets") - .join(format!("{}{}", src, file_number)) + .join(format!("{src}{file_number}")) .with_extension("webp"); - file_number += 1; std::fs::write(&output, &*webp.unwrap()).unwrap(); } } Err(mes) => { - println!("{}", mes); + println!("{mes}"); } } } diff --git a/src/animation_decoder.rs b/src/animation_decoder.rs index 6cc8601..8925d91 100644 --- a/src/animation_decoder.rs +++ b/src/animation_decoder.rs @@ -1,7 +1,8 @@ +#![allow(clippy::uninit_vec)] use libwebp_sys::*; -use crate::shared::PixelLayout; use crate::AnimFrame; +use crate::shared::PixelLayout; pub struct AnimDecoder<'a> { data: &'a [u8], @@ -14,69 +15,72 @@ impl<'a> AnimDecoder<'a> { unsafe { self.decode_internal(true) } } unsafe fn decode_internal(&self, mut has_alpha: bool) -> Result { - let mut dec_options: WebPAnimDecoderOptions = std::mem::zeroed(); - dec_options.color_mode = if has_alpha { - WEBP_CSP_MODE::MODE_RGBA - } else { - WEBP_CSP_MODE::MODE_RGB - }; - let ok = WebPAnimDecoderOptionsInitInternal(&mut dec_options, WebPGetDemuxABIVersion()); - if ok == 0 { - return Err(String::from("option init error")); - } - match dec_options.color_mode { - WEBP_CSP_MODE::MODE_RGBA | WEBP_CSP_MODE::MODE_RGB => {} - _ => return Err(String::from("unsupport color mode")), - } - has_alpha = dec_options.color_mode == WEBP_CSP_MODE::MODE_RGBA; - let webp_data = WebPData { - bytes: self.data.as_ptr(), - size: self.data.len(), - }; - let dec = WebPAnimDecoderNewInternal(&webp_data, &dec_options, WebPGetDemuxABIVersion()); - if dec.is_null() { - return Err(String::from("null_decoder")); - } - let mut anim_info: WebPAnimInfo = std::mem::zeroed(); - let ok = WebPAnimDecoderGetInfo(dec, &mut anim_info); - if ok == 0 { - return Err(String::from("null info")); - } - let width = anim_info.canvas_width; - let height = anim_info.canvas_height; - let mut list: Vec = vec![]; - while WebPAnimDecoderHasMoreFrames(dec) > 0 { - let mut buf: *mut u8 = std::ptr::null_mut(); - let mut timestamp: std::os::raw::c_int = 0; - let ok = WebPAnimDecoderGetNext(dec, &mut buf, &mut timestamp); - if ok != 0 { - let len = (if has_alpha { 4 } else { 3 } * width * height) as usize; - let mut img = Vec::with_capacity(len); - img.set_len(len); - buf.copy_to(img.as_mut_ptr(), len); - let layout = if has_alpha { - PixelLayout::Rgba - } else { - PixelLayout::Rgb - }; - let frame = DecodeAnimFrame { - img, - width, - height, - layout, - timestamp, - }; - list.push(frame); + unsafe { + let mut dec_options: WebPAnimDecoderOptions = std::mem::zeroed(); + dec_options.color_mode = if has_alpha { + WEBP_CSP_MODE::MODE_RGBA + } else { + WEBP_CSP_MODE::MODE_RGB + }; + let ok = WebPAnimDecoderOptionsInitInternal(&mut dec_options, WebPGetDemuxABIVersion()); + if ok == 0 { + return Err(String::from("option init error")); } + match dec_options.color_mode { + WEBP_CSP_MODE::MODE_RGBA | WEBP_CSP_MODE::MODE_RGB => {} + _ => return Err(String::from("unsupport color mode")), + } + has_alpha = dec_options.color_mode == WEBP_CSP_MODE::MODE_RGBA; + let webp_data = WebPData { + bytes: self.data.as_ptr(), + size: self.data.len(), + }; + let dec = + WebPAnimDecoderNewInternal(&webp_data, &dec_options, WebPGetDemuxABIVersion()); + if dec.is_null() { + return Err(String::from("null_decoder")); + } + let mut anim_info: WebPAnimInfo = std::mem::zeroed(); + let ok = WebPAnimDecoderGetInfo(dec, &mut anim_info); + if ok == 0 { + return Err(String::from("null info")); + } + let width = anim_info.canvas_width; + let height = anim_info.canvas_height; + let mut list: Vec = vec![]; + while WebPAnimDecoderHasMoreFrames(dec) > 0 { + let mut buf: *mut u8 = std::ptr::null_mut(); + let mut timestamp: std::os::raw::c_int = 0; + let ok = WebPAnimDecoderGetNext(dec, &mut buf, &mut timestamp); + if ok != 0 { + let len = (if has_alpha { 4 } else { 3 } * width * height) as usize; + let mut img = Vec::with_capacity(len); + img.set_len(len); + buf.copy_to(img.as_mut_ptr(), len); + let layout = if has_alpha { + PixelLayout::Rgba + } else { + PixelLayout::Rgb + }; + let frame = DecodeAnimFrame { + img, + width, + height, + layout, + timestamp, + }; + list.push(frame); + } + } + WebPAnimDecoderReset(dec); + //let demuxer:WebPDemuxer=WebPAnimDecoderGetDemuxer(dec); + // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data). + WebPAnimDecoderDelete(dec); + let mut anim = DecodeAnimImage::from(list); + anim.loop_count = anim_info.loop_count; + anim.bg_color = anim_info.bgcolor; + Ok(anim) } - WebPAnimDecoderReset(dec); - //let demuxer:WebPDemuxer=WebPAnimDecoderGetDemuxer(dec); - // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data). - WebPAnimDecoderDelete(dec); - let mut anim = DecodeAnimImage::from(list); - anim.loop_count = anim_info.loop_count; - anim.bg_color = anim_info.bgcolor; - Ok(anim) } } struct DecodeAnimFrame { @@ -132,6 +136,11 @@ impl DecodeAnimImage { pub fn len(&self) -> usize { self.frames.len() } + + pub fn is_empty(&self) -> bool { + self.frames.is_empty() + } + pub fn has_animation(&self) -> bool { self.len() > 1 } @@ -186,7 +195,7 @@ mod tests { let result = decoder.decode(); assert!(result.is_ok(), "Decoding should succeed for valid data"); let anim = result.unwrap(); - assert!(anim.len() > 0, "Animation should have at least one frame"); + assert!(!anim.is_empty(), "Animation should have at least one frame"); let _ = anim.loop_count; let _ = anim.bg_color; } diff --git a/src/animation_encoder.rs b/src/animation_encoder.rs index 049eccb..2f57017 100644 --- a/src/animation_encoder.rs +++ b/src/animation_encoder.rs @@ -5,7 +5,7 @@ use libwebp_sys::*; #[cfg(feature = "img")] use image::*; -use crate::{shared::*, Encoder}; +use crate::{Encoder, shared::*}; pub struct AnimFrame<'a> { image: &'a [u8], @@ -62,7 +62,7 @@ impl<'a> AnimFrame<'a> { Self::new(image, PixelLayout::Rgba, width, height, timestamp, None) } pub fn get_image(&self) -> &[u8] { - &self.image + self.image } pub fn get_layout(&self) -> PixelLayout { self.layout @@ -83,16 +83,16 @@ impl<'a> From<&'a AnimFrame<'a>> for Encoder<'a> { } } #[cfg(feature = "img")] -impl Into for &AnimFrame<'_> { - fn into(self) -> DynamicImage { - if self.layout.is_alpha() { +impl From<&AnimFrame<'_>> for DynamicImage { + fn from(val: &AnimFrame<'_>) -> Self { + if val.layout.is_alpha() { let image = - ImageBuffer::from_raw(self.width(), self.height(), self.get_image().to_owned()) + ImageBuffer::from_raw(val.width(), val.height(), val.get_image().to_owned()) .expect("ImageBuffer couldn't be created"); DynamicImage::ImageRgba8(image) } else { let image = - ImageBuffer::from_raw(self.width(), self.height(), self.get_image().to_owned()) + ImageBuffer::from_raw(val.width(), val.height(), val.get_image().to_owned()) .expect("ImageBuffer couldn't be created"); DynamicImage::ImageRgb8(image) } @@ -135,7 +135,7 @@ impl<'a> AnimEncoder<'a> { self.try_encode().unwrap() } pub fn try_encode(&self) -> Result { - unsafe { anim_encode(&self) } + unsafe { anim_encode(self) } } } @@ -151,66 +151,72 @@ unsafe fn anim_encode(all_frame: &AnimEncoder) -> Result::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, + unsafe { + 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 err_ptr = WebPAnimEncoderGetError(encoder); + let string = if !err_ptr.is_null() { + std::ffi::CStr::from_ptr(err_ptr) + .to_string_lossy() + .into_owned() + } else { + String::from("Unknown error") + }; WebPAnimEncoderDelete(encoder); - return Err(AnimEncodeError::WebPEncodingError(pic.error_code)); + return Err(AnimEncodeError::WebPAnimEncoderGetError(string)); } - 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 { - let err_ptr = WebPAnimEncoderGetError(encoder); - let string = if !err_ptr.is_null() { - unsafe { std::ffi::CStr::from_ptr(err_ptr) } - .to_string_lossy() - .into_owned() - } else { - String::from("Unknown error") - }; 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 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)) } - 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)) } #[cfg(test)] mod tests { use super::*; - use crate::shared::PixelLayout; use crate::AnimDecoder; + use crate::shared::PixelLayout; fn default_config() -> WebPConfig { let mut config = unsafe { std::mem::zeroed() }; diff --git a/src/decoder.rs b/src/decoder.rs index 38066c2..803443e 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -244,8 +244,7 @@ mod tests { assert!(dbg.contains(&format!("has_animation: {}", features.has_animation()))); assert!( dbg.contains(format_str), - "Debug output missing expected format string: {}", - format_str + "Debug output missing expected format string: {format_str}" ); } } diff --git a/src/encoder.rs b/src/encoder.rs index d44bee5..254d6ba 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -90,8 +90,7 @@ impl<'a> Encoder<'a> { pub fn encode_advanced(&self, config: &WebPConfig) -> Result { unsafe { let mut picture = new_picture(self.image, self.layout, self.width, self.height); - let res = encode(&mut *picture, config); - res + encode(&mut picture, config) } } } @@ -107,12 +106,12 @@ pub(crate) unsafe fn new_picture( picture.width = width as i32; picture.height = height as i32; match layout { - PixelLayout::Rgba => { + PixelLayout::Rgba => unsafe { WebPPictureImportRGBA(&mut picture, image.as_ptr(), width as i32 * 4); - } - PixelLayout::Rgb => { + }, + PixelLayout::Rgb => unsafe { WebPPictureImportRGB(&mut picture, image.as_ptr(), width as i32 * 3); - } + }, } ManageedPicture(picture) } @@ -120,20 +119,23 @@ unsafe fn encode( picture: &mut WebPPicture, config: &WebPConfig, ) -> Result { - if WebPValidateConfig(config) == 0 { - return Err(WebPEncodingError::VP8_ENC_ERROR_INVALID_CONFIGURATION); - } - let mut ww = std::mem::MaybeUninit::uninit(); - WebPMemoryWriterInit(ww.as_mut_ptr()); - picture.writer = Some(WebPMemoryWrite); - picture.custom_ptr = ww.as_mut_ptr() as *mut std::ffi::c_void; - let status = libwebp_sys::WebPEncode(config, picture); - let ww = ww.assume_init(); - let mem = WebPMemory(ww.mem, ww.size as usize); - if status != VP8StatusCode::VP8_STATUS_OK as i32 { - Ok(mem) - } else { - Err(picture.error_code) + unsafe { + if WebPValidateConfig(config) == 0 { + return Err(WebPEncodingError::VP8_ENC_ERROR_INVALID_CONFIGURATION); + } + let mut ww = std::mem::MaybeUninit::uninit(); + WebPMemoryWriterInit(ww.as_mut_ptr()); + picture.writer = Some(WebPMemoryWrite); + picture.custom_ptr = ww.as_mut_ptr() as *mut std::ffi::c_void; + let status = libwebp_sys::WebPEncode(config, picture); + let ww = ww.assume_init(); + let mem = WebPMemory(ww.mem, ww.size); + + if status != VP8StatusCode::VP8_STATUS_OK as i32 { + Ok(mem) + } else { + Err(picture.error_code) + } } } diff --git a/src/lib.rs b/src/lib.rs index 4b7c853..4c7dba0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,7 +187,7 @@ mod tests { decode_images.extend((&frames).into_iter().map(|a| (&a).into())); } Err(mes) => { - println!("{}", mes); + println!("{mes}"); } } let mut encode_rgba = vec![]; diff --git a/src/shared.rs b/src/shared.rs index 232fd48..2d5958e 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -213,7 +213,7 @@ mod tests { let mem = WebPMemory(ptr, len); - let dbg_str = format!("{:?}", mem); + let dbg_str = format!("{mem:?}"); assert_eq!(dbg_str, "WebpMemory"); } @@ -223,7 +223,7 @@ mod tests { let pic = unsafe { std::mem::zeroed::() }; let managed = ManageedPicture(pic); - let inner_ref: &WebPPicture = &*managed; + let inner_ref: &WebPPicture = &managed; let orig_ptr = &managed.0 as *const WebPPicture; let deref_ptr = inner_ref as *const WebPPicture; assert_eq!(orig_ptr, deref_ptr); From 642963de6196d81df2fc9e02872d6cbe639e777b Mon Sep 17 00:00:00 2001 From: Lucas de Linhares Date: Mon, 28 Jul 2025 16:18:08 +0100 Subject: [PATCH 2/4] Update libweb-sys version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 510c0d9..8859c07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ keywords = ["image", "webp", "conversion"] categories = ["external-ffi-bindings"] [dependencies] -libwebp-sys = "0.9.3" +libwebp-sys = { git = "https://github.com/lucascompython/libwebp-sys", branch = "master" } # Change to libwebp-sys crate once: https://github.com/NoXF/libwebp-sys/pull/37 is aproved image = { version = "^0.25.0", default-features = false, optional = true } [features] From 2948d20ec23d542f6deb8025ba0a0f07934fa5ff Mon Sep 17 00:00:00 2001 From: Lucas <77930083+lucascompython@users.noreply.github.com> Date: Tue, 29 Jul 2025 19:20:39 +0100 Subject: [PATCH 3/4] Use the new version of liwebp-sys with the new version of libwebp --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8859c07..bdf2b45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ keywords = ["image", "webp", "conversion"] categories = ["external-ffi-bindings"] [dependencies] -libwebp-sys = { git = "https://github.com/lucascompython/libwebp-sys", branch = "master" } # Change to libwebp-sys crate once: https://github.com/NoXF/libwebp-sys/pull/37 is aproved +libwebp-sys = "0.13" image = { version = "^0.25.0", default-features = false, optional = true } [features] From 5b5d4209cfe115b7d88ee729db7443e921a3f403 Mon Sep 17 00:00:00 2001 From: Lucas de Linhares Date: Fri, 5 Sep 2025 14:26:23 +0100 Subject: [PATCH 4/4] Fix warnings --- src/animation_decoder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/animation_decoder.rs b/src/animation_decoder.rs index 8925d91..48c154a 100644 --- a/src/animation_decoder.rs +++ b/src/animation_decoder.rs @@ -106,7 +106,7 @@ impl From> for DecodeAnimImage { } impl DecodeAnimImage { #[inline] - pub fn get_frame(&self, index: usize) -> Option { + pub fn get_frame(&self, index: usize) -> Option> { let f = self.frames.get(index)?; Some(AnimFrame::new( &f.img, @@ -118,7 +118,7 @@ impl DecodeAnimImage { )) } #[inline] - pub fn get_frames(&self, index: core::ops::Range) -> Option> { + pub fn get_frames(&self, index: core::ops::Range) -> Option>> { let dec_frames = self.frames.get(index)?; let mut frames = Vec::with_capacity(dec_frames.len()); for f in dec_frames {