1use std::path::Path;
13
14pub mod compat;
15pub mod decoder;
16pub mod encoder;
17mod image;
18#[doc(hidden)]
19pub mod legacy;
20
21pub use compat::{
22 CallbackResponse, DataMap, DecodeOptions, DrawCallback, DrawOptions, ImageRect, InitOptions,
23 Metadata, NextBlend, NextDispose, NextOption, NextOptions, ResponseCommand, RGBA,
24};
25pub use decoder::DecoderError;
26pub use encoder::{EncoderError, LosslessEncodingOptions, LossyEncodingOptions};
27pub use image::ImageBuffer;
28pub use legacy::{read_header, read_u24, AnimationControl, AnimationFrame, WebpHeader};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum WebpEncoding {
33 Lossless,
35 Lossy,
37}
38
39pub fn decode(data: &[u8]) -> Result<ImageBuffer, DecoderError> {
44 let features = decoder::get_features(data)?;
45 if features.has_animation {
46 return Err(DecoderError::Unsupported(
47 "animated WebP requires animation decoder API",
48 ));
49 }
50
51 let image = match features.format {
52 decoder::WebpFormat::Lossy => decoder::decode_lossy_webp_to_rgba(data)?,
53 decoder::WebpFormat::Lossless => decoder::decode_lossless_webp_to_rgba(data)?,
54 decoder::WebpFormat::Undefined => {
55 return Err(DecoderError::Unsupported("unsupported WebP format"))
56 }
57 };
58
59 Ok(ImageBuffer {
60 width: image.width,
61 height: image.height,
62 rgba: image.rgba,
63 })
64}
65
66fn to_lossless_options(optimize: usize) -> Result<LosslessEncodingOptions, EncoderError> {
67 let optimization_level = u8::try_from(optimize)
68 .map_err(|_| EncoderError::InvalidParam("lossless optimization level must be in 0..=9"))?;
69 Ok(LosslessEncodingOptions { optimization_level })
70}
71
72fn to_lossy_options(optimize: usize, quality: usize) -> Result<LossyEncodingOptions, EncoderError> {
73 let optimization_level = u8::try_from(optimize)
74 .map_err(|_| EncoderError::InvalidParam("lossy optimization level must be in 0..=9"))?;
75 let quality = u8::try_from(quality)
76 .map_err(|_| EncoderError::InvalidParam("quality must be in 0..=100"))?;
77 Ok(LossyEncodingOptions {
78 quality,
79 optimization_level,
80 })
81}
82
83pub fn encode(
91 image: &ImageBuffer,
92 optimize: usize,
93 quality: usize,
94 compression: WebpEncoding,
95 exif: Option<&[u8]>,
96) -> Result<Vec<u8>, EncoderError> {
97 match compression {
98 WebpEncoding::Lossless => encode_lossless(image, optimize, exif),
99 WebpEncoding::Lossy => encode_lossy(image, optimize, quality, exif),
100 }
101}
102
103pub fn encode_lossy(
109 image: &ImageBuffer,
110 optimize: usize,
111 quality: usize,
112 exif: Option<&[u8]>,
113) -> Result<Vec<u8>, EncoderError> {
114 let options = to_lossy_options(optimize, quality)?;
115 encoder::encode_lossy_image_to_webp_with_options_and_exif(image, &options, exif)
116}
117
118pub fn encode_lossless(
124 image: &ImageBuffer,
125 optimize: usize,
126 exif: Option<&[u8]>,
127) -> Result<Vec<u8>, EncoderError> {
128 let options = to_lossless_options(optimize)?;
129 encoder::encode_lossless_image_to_webp_with_options_and_exif(image, &options, exif)
130}
131
132pub fn image_from_bytes(data: &[u8]) -> Result<ImageBuffer, DecoderError> {
134 decode(data)
135}
136
137#[cfg(not(target_family = "wasm"))]
139pub fn decode_file<P: AsRef<Path>>(filename: P) -> Result<ImageBuffer, Box<dyn std::error::Error>> {
140 let data = std::fs::read(filename)?;
141 Ok(decode(&data)?)
142}
143
144#[cfg(not(target_family = "wasm"))]
146pub fn image_from_file<P: AsRef<Path>>(
147 filename: P,
148) -> Result<ImageBuffer, Box<dyn std::error::Error>> {
149 decode_file(filename)
150}