Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added animation encoder error handling #29

Merged
merged 2 commits into from
Aug 1, 2023
Merged
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
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