use libwebp_sys::*;
use crate::shared::PixelLayout;
use crate::AnimFrame;
pub struct AnimDecoder<'a> {
data: &'a [u8],
}
impl<'a> AnimDecoder<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub fn decode(&self) -> Result<DecodeAnimImage, String> {
unsafe { self.decode_internal(true) }
}
unsafe fn decode_internal(&self, mut has_alpha: bool) -> Result<DecodeAnimImage, String> {
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<DecodeAnimFrame> = 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);
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 {
img: Vec<u8>,
width: u32,
height: u32,
layout: PixelLayout,
timestamp: i32,
}
pub struct DecodeAnimImage {
frames: Vec<DecodeAnimFrame>,
pub loop_count: u32,
pub bg_color: u32,
}
impl From<Vec<DecodeAnimFrame>> for DecodeAnimImage {
fn from(frames: Vec<DecodeAnimFrame>) -> Self {
DecodeAnimImage {
frames,
loop_count: 0,
bg_color: 0,
}
}
}
impl DecodeAnimImage {
#[inline]
pub fn get_frame(&self, index: usize) -> Option<AnimFrame> {
let f = self.frames.get(index)?;
Some(AnimFrame::new(
&f.img,
f.layout,
f.width,
f.height,
f.timestamp,
None,
))
}
#[inline]
pub fn get_frames(&self, index: core::ops::Range<usize>) -> Option<Vec<AnimFrame>> {
let dec_frames = self.frames.get(index)?;
let mut frames = Vec::with_capacity(dec_frames.len());
for f in dec_frames {
frames.push(AnimFrame::new(
&f.img,
f.layout,
f.width,
f.height,
f.timestamp,
None,
));
}
Some(frames)
}
pub fn len(&self) -> usize {
self.frames.len()
}
pub fn has_animation(&self) -> bool {
self.len() > 1
}
pub fn sort_by_time_stamp(&mut self) {
self.frames.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
}
}
impl<'a> IntoIterator for &'a DecodeAnimImage {
type Item = AnimFrame<'a>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
let fs = self.get_frames(0..self.frames.len());
if let Some(v) = fs {
v.into_iter()
} else {
vec![].into_iter()
}
}
}