#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![deny(clippy::float_arithmetic)]
extern crate alloc;
#[cfg(test)]
extern crate std;
mod bool_coder;
mod color;
mod frame;
#[cfg(test)]
#[path = "../fixtures/generator.rs"]
mod generator;
#[cfg(test)]
#[path = "../fixtures/png_writer.rs"]
mod png_writer;
pub(crate) mod prediction;
mod quantize;
mod residual;
mod transform;
use alloc::vec::Vec;
use core::fmt;
pub const MAX_DIMENSION: u32 = 16383;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Alpha {
#[default]
Lossless,
Discard,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Filter {
#[default]
Auto,
Level {
level: u8,
sharpness: u8,
},
Off,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Options {
pub quality: u8,
pub alpha: Alpha,
pub force_vp8x: bool,
pub filter: Filter,
}
impl Default for Options {
fn default() -> Self {
Self {
quality: 75,
alpha: Alpha::Lossless,
force_vp8x: false,
filter: Filter::Auto,
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
DimensionsOutOfRange {
width: u32,
height: u32,
},
BufferSizeMismatch {
expected: usize,
actual: usize,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DimensionsOutOfRange { width, height } => {
write!(
f,
"dimensions {width}x{height} fall outside 1..={MAX_DIMENSION}"
)
}
Self::BufferSizeMismatch { expected, actual } => {
write!(
f,
"buffer holds {actual} bytes and the dimensions ask for {expected}"
)
}
}
}
}
impl core::error::Error for Error {}
pub fn encode_rgba(rgba: &[u8], width: u32, height: u32, opts: &Options) -> Result<Vec<u8>, Error> {
check(rgba.len(), width, height, 4)?;
Ok(frame::encode(
rgba,
width as usize,
height as usize,
4,
quantize::quantizer_index(opts.quality),
opts,
)
.webp)
}
pub fn encode_rgb(rgb: &[u8], width: u32, height: u32, opts: &Options) -> Result<Vec<u8>, Error> {
check(rgb.len(), width, height, 3)?;
Ok(frame::encode(
rgb,
width as usize,
height as usize,
3,
quantize::quantizer_index(opts.quality),
opts,
)
.webp)
}
fn check(actual: usize, width: u32, height: u32, bytes_per_pixel: u64) -> Result<(), Error> {
if width == 0 || height == 0 || width > MAX_DIMENSION || height > MAX_DIMENSION {
return Err(Error::DimensionsOutOfRange { width, height });
}
let expected = (u64::from(width) * u64::from(height) * bytes_per_pixel) as usize;
if actual != expected {
return Err(Error::BufferSizeMismatch { expected, actual });
}
Ok(())
}