Skip to content

Commit

Permalink
Merge pull request #29 from kozakura913/master
Browse files Browse the repository at this point in the history
Added animation encoder error handling
  • Loading branch information
Jared Forth committed Aug 1, 2023
2 parents af575a3 + c5f0a46 commit 5bee819
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 169 deletions.
205 changes: 205 additions & 0 deletions src/animation_encoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::ffi::CString;

#[cfg(feature = "img")]
use image::DynamicImage;
use libwebp_sys::*;

#[cfg(feature = "img")]
use image::*;

use crate::{shared::*, Encoder};

pub struct AnimFrame<'a> {
image: &'a [u8],
layout: PixelLayout,
width: u32,
height: u32,
timestamp: i32,
config: Option<&'a WebPConfig>,
}
impl<'a> AnimFrame<'a> {
pub fn new(
image: &'a [u8],
layout: PixelLayout,
width: u32,
height: u32,
timestamp: i32,
config: Option<&'a WebPConfig>,
) -> Self {
Self {
image,
layout,
width,
height,
timestamp,
config,
}
}
#[cfg(feature = "img")]
pub fn from_image(image: &'a DynamicImage, timestamp: i32) -> Result<Self, &str> {
match image {
DynamicImage::ImageLuma8(_) => Err("Unimplemented"),
DynamicImage::ImageLumaA8(_) => Err("Unimplemented"),
DynamicImage::ImageRgb8(image) => Ok(Self::from_rgb(
image.as_ref(),
image.width(),
image.height(),
timestamp,
)),
DynamicImage::ImageRgba8(image) => Ok(Self::from_rgba(
image.as_ref(),
image.width(),
image.height(),
timestamp,
)),
_ => Err("Unimplemented"),
}
}
/// Creates a new encoder from the given image data in the RGB pixel layout.
pub fn from_rgb(image: &'a [u8], width: u32, height: u32, timestamp: i32) -> Self {
Self::new(image, PixelLayout::Rgb, width, height, timestamp, None)
}
/// Creates a new encoder from the given image data in the RGBA pixel layout.
pub fn from_rgba(image: &'a [u8], width: u32, height: u32, timestamp: i32) -> Self {
Self::new(image, PixelLayout::Rgba, width, height, timestamp, None)
}
pub fn get_image(&self) -> &[u8] {
&self.image
}
pub fn get_layout(&self) -> PixelLayout {
self.layout
}
pub fn get_time_ms(&self) -> i32 {
self.timestamp
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
}
impl<'a> From<&'a AnimFrame<'a>> for Encoder<'a> {
fn from(f: &'a AnimFrame) -> Self {
Encoder::new(f.get_image(), f.layout, f.width, f.height)
}
}
#[cfg(feature = "img")]
impl Into<DynamicImage> for &AnimFrame<'_> {
fn into(self) -> DynamicImage {
if self.layout.is_alpha() {
let image =
ImageBuffer::from_raw(self.width(), self.height(), self.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())
.expect("ImageBuffer couldn't be created");
DynamicImage::ImageRgb8(image)
}
}
}
pub struct AnimEncoder<'a> {
frames: Vec<AnimFrame<'a>>,
width: u32,
height: u32,
config: &'a WebPConfig,
muxparams: WebPMuxAnimParams,
}
impl<'a> AnimEncoder<'a> {
pub fn new(width: u32, height: u32, config: &'a WebPConfig) -> Self {
Self {
frames: vec![],
width,
height,
config,
muxparams: WebPMuxAnimParams {
bgcolor: 0,
loop_count: 0,
},
}
}
pub fn set_bgcolor(&mut self, rgba: [u8; 4]) {
let bgcolor = (u32::from(rgba[3]) << 24)
+ (u32::from(rgba[2]) << 16)
+ (u32::from(rgba[1]) << 8)
+ (u32::from(rgba[0]));
self.muxparams.bgcolor = bgcolor;
}
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 encode(&self) -> WebPMemory {
self.try_encode().unwrap()
}
pub fn try_encode(&self) -> Result<WebPMemory, AnimEncodeError> {
unsafe { anim_encode(&self) }
}
}

#[derive(Debug)]
pub enum AnimEncodeError {
WebPEncodingError(WebPEncodingError),
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))
}
Loading

0 comments on commit 5bee819

Please sign in to comment.