diff --git a/Cargo.toml b/Cargo.toml index 01219ba..bdf2b45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "webp" version = "0.3.1" authors = ["Jared Forth "] -edition = "2021" +edition = "2024" description = "WebP conversion library." @@ -15,12 +15,12 @@ keywords = ["image", "webp", "conversion"] categories = ["external-ffi-bindings"] [dependencies] -libwebp-sys = "0.9.3" +libwebp-sys = "0.13" 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 7f18c22..8803b03 100644 --- a/src/animation_decoder.rs +++ b/src/animation_decoder.rs @@ -1,3 +1,4 @@ +#![allow(clippy::uninit_vec)] use libwebp_sys::*; use crate::shared::PixelLayout; @@ -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); - buf.copy_to(img.spare_capacity_mut().as_mut_ptr().cast(), len); - img.set_len(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); + buf.copy_to(img.spare_capacity_mut().as_mut_ptr().cast(), len); + img.set_len(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 { @@ -102,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, @@ -114,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 { @@ -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 c4e9f9f..49cc409 100644 --- a/src/animation_encoder.rs +++ b/src/animation_encoder.rs @@ -148,59 +148,65 @@ 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)] 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 b64c9c5..9dd34cc 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -84,8 +84,7 @@ impl<'a> Encoder<'a> { self.e.width(), self.e.height(), ); - let res = encode(&mut *picture, config); - res + encode(&mut picture, config) } } } @@ -186,12 +185,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) } @@ -199,20 +198,22 @@ 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); - 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 6381923..707b9a1 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -220,7 +220,7 @@ mod tests { let mem = WebPMemory(ptr, len); - let dbg_str = format!("{:?}", mem); + let dbg_str = format!("{mem:?}"); assert_eq!(dbg_str, "WebpMemory"); } @@ -230,7 +230,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);