Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
57 changes: 32 additions & 25 deletions examples/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
let width = 512u32;
let height = 512u32;

fn dumy_image(width: u32, height: u32, frame: u32, total_frames: u32) -> Vec<u8> {
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")
Expand Down
126 changes: 65 additions & 61 deletions src/animation_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,34 @@ impl Into<DynamicImage> for &AnimFrame<'_> {
}
}
pub struct AnimEncoder<'a> {
frames: Vec<AnimFrame<'a>>,
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::<WebPAnimEncoderOptions>::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,
Expand All @@ -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<WebPMemory, AnimEncodeError> {
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::<WebPData>::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::<WebPData>::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) };
}
}

Expand All @@ -147,59 +207,3 @@ pub enum AnimEncodeError {
WebPMuxError(WebPMuxError),
WebPAnimEncoderGetError(String),
}
unsafe fn anim_encode(all_frame: &AnimEncoder) -> Result<WebPMemory, AnimEncodeError> {
let width = all_frame.width;
let height = all_frame.height;
let mut uninit = std::mem::MaybeUninit::<WebPAnimEncoderOptions>::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::<WebPData>::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::<WebPData>::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))
}