use std::path::Path;
pub mod compat;
pub mod decoder;
pub mod encoder;
mod image;
#[doc(hidden)]
pub mod legacy;
pub use compat::{
CallbackResponse, DataMap, DecodeOptions, DrawCallback, DrawOptions, ImageRect, InitOptions,
Metadata, NextBlend, NextDispose, NextOption, NextOptions, ResponseCommand, RGBA,
};
pub use decoder::DecoderError;
pub use encoder::{EncoderError, LosslessEncodingOptions, LossyEncodingOptions};
pub use image::ImageBuffer;
pub use legacy::{read_header, read_u24, AnimationControl, AnimationFrame, WebpHeader};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WebpEncoding {
Lossless,
Lossy,
}
pub fn decode(data: &[u8]) -> Result<ImageBuffer, DecoderError> {
let features = decoder::get_features(data)?;
if features.has_animation {
return Err(DecoderError::Unsupported(
"animated WebP requires animation decoder API",
));
}
let image = match features.format {
decoder::WebpFormat::Lossy => decoder::decode_lossy_webp_to_rgba(data)?,
decoder::WebpFormat::Lossless => decoder::decode_lossless_webp_to_rgba(data)?,
decoder::WebpFormat::Undefined => {
return Err(DecoderError::Unsupported("unsupported WebP format"))
}
};
Ok(ImageBuffer {
width: image.width,
height: image.height,
rgba: image.rgba,
})
}
fn to_lossless_options(optimize: usize) -> Result<LosslessEncodingOptions, EncoderError> {
let optimization_level = u8::try_from(optimize)
.map_err(|_| EncoderError::InvalidParam("lossless optimization level must be in 0..=9"))?;
Ok(LosslessEncodingOptions { optimization_level })
}
fn to_lossy_options(optimize: usize, quality: usize) -> Result<LossyEncodingOptions, EncoderError> {
let optimization_level = u8::try_from(optimize)
.map_err(|_| EncoderError::InvalidParam("lossy optimization level must be in 0..=9"))?;
let quality = u8::try_from(quality)
.map_err(|_| EncoderError::InvalidParam("quality must be in 0..=100"))?;
Ok(LossyEncodingOptions {
quality,
optimization_level,
})
}
pub fn encode(
image: &ImageBuffer,
optimize: usize,
quality: usize,
compression: WebpEncoding,
exif: Option<&[u8]>,
) -> Result<Vec<u8>, EncoderError> {
match compression {
WebpEncoding::Lossless => encode_lossless(image, optimize, exif),
WebpEncoding::Lossy => encode_lossy(image, optimize, quality, exif),
}
}
pub fn encode_lossy(
image: &ImageBuffer,
optimize: usize,
quality: usize,
exif: Option<&[u8]>,
) -> Result<Vec<u8>, EncoderError> {
let options = to_lossy_options(optimize, quality)?;
encoder::encode_lossy_image_to_webp_with_options_and_exif(image, &options, exif)
}
pub fn encode_lossless(
image: &ImageBuffer,
optimize: usize,
exif: Option<&[u8]>,
) -> Result<Vec<u8>, EncoderError> {
let options = to_lossless_options(optimize)?;
encoder::encode_lossless_image_to_webp_with_options_and_exif(image, &options, exif)
}
pub fn image_from_bytes(data: &[u8]) -> Result<ImageBuffer, DecoderError> {
decode(data)
}
#[cfg(not(target_family = "wasm"))]
pub fn decode_file<P: AsRef<Path>>(filename: P) -> Result<ImageBuffer, Box<dyn std::error::Error>> {
let data = std::fs::read(filename)?;
Ok(decode(&data)?)
}
#[cfg(not(target_family = "wasm"))]
pub fn image_from_file<P: AsRef<Path>>(
filename: P,
) -> Result<ImageBuffer, Box<dyn std::error::Error>> {
decode_file(filename)
}