1use alloc::vec::Vec;
4use rgb::alt::{BGR8, BGRA8};
5use rgb::{RGB8, RGBA8};
6use whereat::*;
7
8#[doc(hidden)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PixelLayout {
12 Rgba,
14 Bgra,
16 Rgb,
18 Bgr,
20}
21
22impl PixelLayout {
23 #[must_use]
25 pub const fn bytes_per_pixel(self) -> usize {
26 match self {
27 PixelLayout::Rgba | PixelLayout::Bgra => 4,
28 PixelLayout::Rgb | PixelLayout::Bgr => 3,
29 }
30 }
31
32 #[must_use]
34 pub const fn has_alpha(self) -> bool {
35 matches!(self, PixelLayout::Rgba | PixelLayout::Bgra)
36 }
37}
38
39#[doc(hidden)]
45pub trait EncodePixel: Copy + 'static + private::Sealed {
46 const LAYOUT: PixelLayout;
48}
49
50impl EncodePixel for RGBA8 {
51 const LAYOUT: PixelLayout = PixelLayout::Rgba;
52}
53
54impl EncodePixel for BGRA8 {
55 const LAYOUT: PixelLayout = PixelLayout::Bgra;
56}
57
58impl EncodePixel for RGB8 {
59 const LAYOUT: PixelLayout = PixelLayout::Rgb;
60}
61
62impl EncodePixel for BGR8 {
63 const LAYOUT: PixelLayout = PixelLayout::Bgr;
64}
65
66#[doc(hidden)]
72pub trait DecodePixel: Copy + 'static + private::Sealed {
73 const LAYOUT: PixelLayout;
75
76 #[doc(hidden)]
81 fn decode_new(data: &[u8]) -> Option<(*mut u8, i32, i32)>;
82
83 #[doc(hidden)]
89 unsafe fn decode_into(data: &[u8], output: *mut u8, output_len: usize, stride: i32) -> bool;
90}
91
92impl DecodePixel for RGBA8 {
93 const LAYOUT: PixelLayout = PixelLayout::Rgba;
94
95 fn decode_new(data: &[u8]) -> Option<(*mut u8, i32, i32)> {
96 let mut width: i32 = 0;
97 let mut height: i32 = 0;
98 let ptr = unsafe {
99 libwebp_sys::WebPDecodeRGBA(data.as_ptr(), data.len(), &mut width, &mut height)
100 };
101 if ptr.is_null() {
102 None
103 } else {
104 Some((ptr, width, height))
105 }
106 }
107
108 unsafe fn decode_into(data: &[u8], output: *mut u8, output_len: usize, stride: i32) -> bool {
109 let result = unsafe {
111 libwebp_sys::WebPDecodeRGBAInto(data.as_ptr(), data.len(), output, output_len, stride)
112 };
113 !result.is_null()
114 }
115}
116
117impl DecodePixel for BGRA8 {
118 const LAYOUT: PixelLayout = PixelLayout::Bgra;
119
120 fn decode_new(data: &[u8]) -> Option<(*mut u8, i32, i32)> {
121 let mut width: i32 = 0;
122 let mut height: i32 = 0;
123 let ptr = unsafe {
124 libwebp_sys::WebPDecodeBGRA(data.as_ptr(), data.len(), &mut width, &mut height)
125 };
126 if ptr.is_null() {
127 None
128 } else {
129 Some((ptr, width, height))
130 }
131 }
132
133 unsafe fn decode_into(data: &[u8], output: *mut u8, output_len: usize, stride: i32) -> bool {
134 let result = unsafe {
136 libwebp_sys::WebPDecodeBGRAInto(data.as_ptr(), data.len(), output, output_len, stride)
137 };
138 !result.is_null()
139 }
140}
141
142impl DecodePixel for RGB8 {
143 const LAYOUT: PixelLayout = PixelLayout::Rgb;
144
145 fn decode_new(data: &[u8]) -> Option<(*mut u8, i32, i32)> {
146 let mut width: i32 = 0;
147 let mut height: i32 = 0;
148 let ptr = unsafe {
149 libwebp_sys::WebPDecodeRGB(data.as_ptr(), data.len(), &mut width, &mut height)
150 };
151 if ptr.is_null() {
152 None
153 } else {
154 Some((ptr, width, height))
155 }
156 }
157
158 unsafe fn decode_into(data: &[u8], output: *mut u8, output_len: usize, stride: i32) -> bool {
159 let result = unsafe {
161 libwebp_sys::WebPDecodeRGBInto(data.as_ptr(), data.len(), output, output_len, stride)
162 };
163 !result.is_null()
164 }
165}
166
167impl DecodePixel for BGR8 {
168 const LAYOUT: PixelLayout = PixelLayout::Bgr;
169
170 fn decode_new(data: &[u8]) -> Option<(*mut u8, i32, i32)> {
171 let mut width: i32 = 0;
172 let mut height: i32 = 0;
173 let ptr = unsafe {
174 libwebp_sys::WebPDecodeBGR(data.as_ptr(), data.len(), &mut width, &mut height)
175 };
176 if ptr.is_null() {
177 None
178 } else {
179 Some((ptr, width, height))
180 }
181 }
182
183 unsafe fn decode_into(data: &[u8], output: *mut u8, output_len: usize, stride: i32) -> bool {
184 let result = unsafe {
186 libwebp_sys::WebPDecodeBGRInto(data.as_ptr(), data.len(), output, output_len, stride)
187 };
188 !result.is_null()
189 }
190}
191
192mod private {
193 use super::*;
194
195 pub trait Sealed {}
196 impl Sealed for RGBA8 {}
197 impl Sealed for BGRA8 {}
198 impl Sealed for RGB8 {}
199 impl Sealed for BGR8 {}
200}
201
202#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct ImageInfo {
205 pub width: u32,
207 pub height: u32,
209 pub has_alpha: bool,
211 pub has_animation: bool,
213 pub frame_count: u32,
215 pub format: BitstreamFormat,
217}
218
219impl ImageInfo {
220 pub fn from_webp(data: &[u8]) -> crate::Result<Self> {
222 let mut width: i32 = 0;
223 let mut height: i32 = 0;
224
225 let result =
226 unsafe { libwebp_sys::WebPGetInfo(data.as_ptr(), data.len(), &mut width, &mut height) };
227
228 if result == 0 {
229 return Err(at!(crate::Error::InvalidWebP));
230 }
231
232 let mut features = core::mem::MaybeUninit::<libwebp_sys::WebPBitstreamFeatures>::uninit();
234 let status = unsafe {
235 libwebp_sys::WebPGetFeatures(data.as_ptr(), data.len(), features.as_mut_ptr())
236 };
237 let features = unsafe { features.assume_init() };
238
239 if status != libwebp_sys::VP8StatusCode::VP8_STATUS_OK {
240 return Err(at!(crate::Error::DecodeFailed(
241 crate::error::DecodingError::from(status as i32),
242 )));
243 }
244
245 let format = match features.format {
246 0 => BitstreamFormat::Undefined,
247 1 => BitstreamFormat::Lossy,
248 2 => BitstreamFormat::Lossless,
249 _ => BitstreamFormat::Undefined,
250 };
251
252 Ok(ImageInfo {
253 width: width as u32,
254 height: height as u32,
255 has_alpha: features.has_alpha != 0,
256 has_animation: features.has_animation != 0,
257 frame_count: if features.has_animation != 0 { 0 } else { 1 }, format,
259 })
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
265#[non_exhaustive]
266pub enum BitstreamFormat {
267 #[default]
269 Undefined,
270 Lossy,
272 Lossless,
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
278#[non_exhaustive]
279pub enum ColorMode {
280 #[default]
282 Rgba,
283 Bgra,
285 Argb,
287 Rgb,
289 Bgr,
291 Yuv420,
293 Yuva420,
295}
296
297impl ColorMode {
298 pub fn bytes_per_pixel(self) -> Option<usize> {
300 match self {
301 ColorMode::Rgba | ColorMode::Bgra | ColorMode::Argb => Some(4),
302 ColorMode::Rgb | ColorMode::Bgr => Some(3),
303 ColorMode::Yuv420 | ColorMode::Yuva420 => None, }
305 }
306
307 pub fn has_alpha(self) -> bool {
309 matches!(
310 self,
311 ColorMode::Rgba | ColorMode::Bgra | ColorMode::Argb | ColorMode::Yuva420
312 )
313 }
314
315 pub fn is_yuv(self) -> bool {
317 matches!(self, ColorMode::Yuv420 | ColorMode::Yuva420)
318 }
319}
320
321#[derive(Debug, Clone)]
323pub struct YuvPlanes {
324 pub y: Vec<u8>,
326 pub y_stride: usize,
328 pub u: Vec<u8>,
330 pub u_stride: usize,
332 pub v: Vec<u8>,
334 pub v_stride: usize,
336 pub a: Option<Vec<u8>>,
338 pub a_stride: usize,
340 pub width: u32,
342 pub height: u32,
344}
345
346impl YuvPlanes {
347 pub fn new(width: u32, height: u32, with_alpha: bool) -> Self {
351 let y_stride = width as usize;
352 let uv_stride = (width as usize).div_ceil(2);
353 let uv_height = (height as usize).div_ceil(2);
354
355 Self {
356 y: alloc::vec![0u8; y_stride * height as usize],
357 y_stride,
358 u: alloc::vec![0u8; uv_stride * uv_height],
359 u_stride: uv_stride,
360 v: alloc::vec![0u8; uv_stride * uv_height],
361 v_stride: uv_stride,
362 a: if with_alpha {
363 Some(alloc::vec![0u8; y_stride * height as usize])
364 } else {
365 None
366 },
367 a_stride: y_stride,
368 width,
369 height,
370 }
371 }
372
373 pub fn uv_dimensions(&self) -> (u32, u32) {
375 (self.width.div_ceil(2), self.height.div_ceil(2))
376 }
377}
378
379#[derive(Debug, Clone, Copy)]
381pub struct YuvPlanesRef<'a> {
382 pub y: &'a [u8],
384 pub y_stride: usize,
386 pub u: &'a [u8],
388 pub u_stride: usize,
390 pub v: &'a [u8],
392 pub v_stride: usize,
394 pub a: Option<&'a [u8]>,
396 pub a_stride: usize,
398 pub width: u32,
400 pub height: u32,
402}
403
404impl<'a> From<&'a YuvPlanes> for YuvPlanesRef<'a> {
405 fn from(planes: &'a YuvPlanes) -> Self {
406 Self {
407 y: &planes.y,
408 y_stride: planes.y_stride,
409 u: &planes.u,
410 u_stride: planes.u_stride,
411 v: &planes.v,
412 v_stride: planes.v_stride,
413 a: planes.a.as_deref(),
414 a_stride: planes.a_stride,
415 width: planes.width,
416 height: planes.height,
417 }
418 }
419}
420
421pub struct WebPData {
448 ptr: *mut u8,
449 len: usize,
450}
451
452unsafe impl Send for WebPData {}
454unsafe impl Sync for WebPData {}
455
456impl WebPData {
457 #[must_use]
465 pub(crate) unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
466 Self { ptr, len }
467 }
468
469 #[must_use]
471 pub fn len(&self) -> usize {
472 self.len
473 }
474
475 #[must_use]
477 pub fn is_empty(&self) -> bool {
478 self.len == 0
479 }
480
481 #[must_use]
483 pub fn as_slice(&self) -> &[u8] {
484 if self.ptr.is_null() || self.len == 0 {
485 &[]
486 } else {
487 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
489 }
490 }
491}
492
493impl Drop for WebPData {
494 fn drop(&mut self) {
495 if !self.ptr.is_null() {
496 unsafe {
498 libwebp_sys::WebPFree(self.ptr as *mut core::ffi::c_void);
499 }
500 }
501 }
502}
503
504impl core::ops::Deref for WebPData {
505 type Target = [u8];
506
507 fn deref(&self) -> &Self::Target {
508 self.as_slice()
509 }
510}
511
512impl AsRef<[u8]> for WebPData {
513 fn as_ref(&self) -> &[u8] {
514 self.as_slice()
515 }
516}
517
518impl From<WebPData> for Vec<u8> {
519 fn from(data: WebPData) -> Self {
520 data.as_slice().to_vec()
521 }
522}
523
524impl core::fmt::Debug for WebPData {
525 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
526 f.debug_struct("WebPData")
527 .field("len", &self.len)
528 .finish_non_exhaustive()
529 }
530}