1use alloc::vec::Vec;
36use core::ops::Deref;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum PixelLayout {
41 Rgb,
43 Rgba,
45}
46
47impl PixelLayout {
48 pub fn bytes_per_pixel(&self) -> u8 {
50 match self {
51 PixelLayout::Rgb => 3,
52 PixelLayout::Rgba => 4,
53 }
54 }
55}
56
57#[derive(Debug)]
61pub struct WebPMemory(Vec<u8>);
62
63impl WebPMemory {
64 pub fn is_empty(&self) -> bool {
66 self.0.is_empty()
67 }
68
69 pub fn len(&self) -> usize {
71 self.0.len()
72 }
73}
74
75impl Deref for WebPMemory {
76 type Target = [u8];
77
78 fn deref(&self) -> &Self::Target {
79 &self.0
80 }
81}
82
83impl AsRef<[u8]> for WebPMemory {
84 fn as_ref(&self) -> &[u8] {
85 &self.0
86 }
87}
88
89#[derive(Debug)]
91pub struct WebPImage {
92 data: Vec<u8>,
93 layout: PixelLayout,
94 width: u32,
95 height: u32,
96}
97
98impl WebPImage {
99 pub fn data(&self) -> &[u8] {
101 &self.data
102 }
103
104 pub fn layout(&self) -> PixelLayout {
106 self.layout
107 }
108
109 pub fn width(&self) -> u32 {
111 self.width
112 }
113
114 pub fn height(&self) -> u32 {
116 self.height
117 }
118}
119
120#[derive(Debug)]
122pub struct BitstreamFeatures {
123 width: u32,
124 height: u32,
125 has_alpha: bool,
126 has_animation: bool,
127}
128
129impl BitstreamFeatures {
130 pub fn new(data: &[u8]) -> Option<Self> {
132 crate::ImageInfo::from_webp(data).ok().map(|info| Self {
133 width: info.width,
134 height: info.height,
135 has_alpha: info.has_alpha,
136 has_animation: info.has_animation,
137 })
138 }
139
140 pub fn width(&self) -> u32 {
142 self.width
143 }
144
145 pub fn height(&self) -> u32 {
147 self.height
148 }
149
150 pub fn has_alpha(&self) -> bool {
152 self.has_alpha
153 }
154
155 pub fn has_animation(&self) -> bool {
157 self.has_animation
158 }
159}
160
161pub struct Encoder<'a> {
163 image: &'a [u8],
164 layout: PixelLayout,
165 width: u32,
166 height: u32,
167}
168
169impl<'a> Encoder<'a> {
170 pub fn new(image: &'a [u8], layout: PixelLayout, width: u32, height: u32) -> Self {
172 Self {
173 image,
174 layout,
175 width,
176 height,
177 }
178 }
179
180 pub fn from_rgb(image: &'a [u8], width: u32, height: u32) -> Self {
182 Self::new(image, PixelLayout::Rgb, width, height)
183 }
184
185 pub fn from_rgba(image: &'a [u8], width: u32, height: u32) -> Self {
187 Self::new(image, PixelLayout::Rgba, width, height)
188 }
189
190 pub fn encode(&self, quality: f32) -> WebPMemory {
192 self.encode_simple(false, quality)
193 .unwrap_or_else(|_| WebPMemory(Vec::new()))
194 }
195
196 pub fn encode_lossless(&self) -> WebPMemory {
198 self.encode_simple(true, 75.0)
199 .unwrap_or_else(|_| WebPMemory(Vec::new()))
200 }
201
202 pub fn encode_simple(&self, lossless: bool, quality: f32) -> crate::Result<WebPMemory> {
204 use crate::Unstoppable;
205
206 let config = crate::EncoderConfig::new()
207 .quality(quality)
208 .lossless(lossless);
209
210 let data = match self.layout {
211 PixelLayout::Rgba => {
212 config.encode_rgba(self.image, self.width, self.height, Unstoppable)?
213 }
214 PixelLayout::Rgb => {
215 config.encode_rgb(self.image, self.width, self.height, Unstoppable)?
216 }
217 };
218
219 Ok(WebPMemory(data))
220 }
221}
222
223pub struct Decoder<'a> {
225 data: &'a [u8],
226}
227
228impl<'a> Decoder<'a> {
229 pub fn new(data: &'a [u8]) -> Self {
231 Self { data }
232 }
233
234 pub fn decode(&self) -> Option<WebPImage> {
238 let features = BitstreamFeatures::new(self.data)?;
239
240 if features.has_animation() {
242 return None;
243 }
244
245 let (data, width, height) = if features.has_alpha() {
246 crate::decode_rgba(self.data).ok()?
247 } else {
248 crate::decode_rgb(self.data).ok()?
249 };
250
251 let layout = if features.has_alpha() {
252 PixelLayout::Rgba
253 } else {
254 PixelLayout::Rgb
255 };
256
257 Some(WebPImage {
258 data,
259 layout,
260 width,
261 height,
262 })
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_encode_decode_roundtrip() {
272 let rgba = vec![
273 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255,
274 ];
275 let encoder = Encoder::from_rgba(&rgba, 2, 2);
276 let webp = encoder.encode_lossless();
277
278 assert!(!webp.is_empty());
279
280 let decoder = Decoder::new(&webp);
281 let image = decoder.decode().expect("decode failed");
282 assert_eq!(image.width(), 2);
283 assert_eq!(image.height(), 2);
284 assert!(image.layout() == PixelLayout::Rgba || image.layout() == PixelLayout::Rgb);
287 }
288
289 #[test]
290 fn test_bitstream_features() {
291 let rgba = vec![0u8; 4 * 4 * 4];
292 let encoder = Encoder::from_rgba(&rgba, 4, 4);
293 let webp = encoder.encode(85.0);
294
295 let features = BitstreamFeatures::new(&webp).expect("features");
296 assert_eq!(features.width(), 4);
297 assert_eq!(features.height(), 4);
298 assert!(!features.has_animation());
299 }
300}