use std::fmt::{Debug, Error, Formatter};
use libwebp_sys::*;
use crate::shared::{PixelLayout, WebPImage, WebPMemory};
pub struct Decoder<'a> {
data: &'a [u8],
}
impl<'a> Decoder<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub fn decode(&self) -> Option<WebPImage> {
let features = BitstreamFeatures::new(self.data)?;
if features.has_animation() {
return None;
}
let width = features.width();
let height = features.height();
let pixel_count = width * height;
let image_ptr = unsafe {
let mut width = width as i32;
let mut height = height as i32;
if features.has_alpha() {
WebPDecodeRGBA(
self.data.as_ptr(),
self.data.len(),
&mut width as *mut _,
&mut height as *mut _,
)
} else {
WebPDecodeRGB(
self.data.as_ptr(),
self.data.len(),
&mut width as *mut _,
&mut height as *mut _,
)
}
};
if image_ptr.is_null() {
return None;
}
let image = if features.has_alpha() {
let len = 4 * pixel_count as usize;
WebPImage::new(WebPMemory(image_ptr, len), PixelLayout::Rgba, width, height)
} else {
let len = 3 * pixel_count as usize;
WebPImage::new(WebPMemory(image_ptr, len), PixelLayout::Rgb, width, height)
};
Some(image)
}
}
pub struct BitstreamFeatures(WebPBitstreamFeatures);
impl BitstreamFeatures {
pub fn new(data: &[u8]) -> Option<Self> {
unsafe {
let mut features: WebPBitstreamFeatures = std::mem::zeroed();
let result = WebPGetFeatures(data.as_ptr(), data.len(), &mut features as *mut _);
if result == VP8StatusCode::VP8_STATUS_OK {
return Some(Self(features));
}
}
None
}
pub fn width(&self) -> u32 {
self.0.width as u32
}
pub fn height(&self) -> u32 {
self.0.height as u32
}
pub fn has_alpha(&self) -> bool {
self.0.has_alpha == 1
}
pub fn has_animation(&self) -> bool {
self.0.has_animation == 1
}
pub fn format(&self) -> Option<BitstreamFormat> {
match self.0.format {
0 => Some(BitstreamFormat::Undefined),
1 => Some(BitstreamFormat::Lossy),
2 => Some(BitstreamFormat::Lossless),
_ => None,
}
}
}
impl Debug for BitstreamFeatures {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut debug_struct = f.debug_struct("BitstreamFeatures");
debug_struct
.field("width", &self.width())
.field("height", &self.height())
.field("has_alpha", &self.has_alpha())
.field("has_animation", &self.has_animation());
match self.format() {
Some(BitstreamFormat::Undefined) => debug_struct.field("format", &"Undefined"),
Some(BitstreamFormat::Lossy) => debug_struct.field("format", &"Lossy"),
Some(BitstreamFormat::Lossless) => debug_struct.field("format", &"Lossless"),
None => debug_struct.field("format", &"Error"),
};
debug_struct.finish()
}
}
#[derive(Debug)]
pub enum BitstreamFormat {
Undefined = 0,
Lossy = 1,
Lossless = 2,
}