1use std::fmt::{Debug, Error, Formatter};
2use std::ops::{Deref, DerefMut};
3
4#[cfg(feature = "img")]
5use image::*;
6use libwebp_sys::{WebPFree, WebPPicture, WebPPictureFree};
7
8pub struct WebPMemory(pub(crate) *mut u8, pub(crate) usize);
11
12impl Debug for WebPMemory {
13 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
14 f.debug_struct("WebpMemory").finish()
15 }
16}
17
18impl Drop for WebPMemory {
19 fn drop(&mut self) {
20 unsafe { WebPFree(self.0 as _) }
21 }
22}
23
24impl Deref for WebPMemory {
25 type Target = [u8];
26
27 fn deref(&self) -> &Self::Target {
28 unsafe { std::slice::from_raw_parts(self.0, self.1) }
29 }
30}
31
32impl DerefMut for WebPMemory {
33 fn deref_mut(&mut self) -> &mut Self::Target {
34 unsafe { std::slice::from_raw_parts_mut(self.0, self.1) }
35 }
36}
37
38#[derive(Debug)]
39pub(crate) struct ManageedPicture(pub(crate) WebPPicture);
40
41impl Drop for ManageedPicture {
42 fn drop(&mut self) {
43 unsafe { WebPPictureFree(&mut self.0 as _) }
44 }
45}
46
47impl Deref for ManageedPicture {
48 type Target = WebPPicture;
49
50 fn deref(&self) -> &Self::Target {
51 &self.0
52 }
53}
54
55impl DerefMut for ManageedPicture {
56 fn deref_mut(&mut self) -> &mut Self::Target {
57 &mut self.0
58 }
59}
60
61pub struct WebPImage {
65 data: WebPMemory,
66 layout: PixelLayout,
67 width: u32,
68 height: u32,
69}
70
71impl WebPImage {
72 pub(crate) fn new(data: WebPMemory, layout: PixelLayout, width: u32, height: u32) -> Self {
73 Self {
74 data,
75 layout,
76 width,
77 height,
78 }
79 }
80
81 #[cfg(feature = "img")]
83 pub fn to_image(&self) -> DynamicImage {
84 if self.layout.is_alpha() {
85 let image = ImageBuffer::from_raw(self.width, self.height, self.data.to_owned())
86 .expect("ImageBuffer couldn't be created");
87
88 DynamicImage::ImageRgba8(image)
89 } else {
90 let image = ImageBuffer::from_raw(self.width, self.height, self.data.to_owned())
91 .expect("ImageBuffer couldn't be created");
92
93 DynamicImage::ImageRgb8(image)
94 }
95 }
96
97 pub fn width(&self) -> u32 {
99 self.width
100 }
101
102 pub fn height(&self) -> u32 {
104 self.height
105 }
106
107 pub fn is_alpha(&self) -> bool {
108 self.layout.is_alpha()
109 }
110
111 pub fn layout(&self) -> PixelLayout {
112 self.layout
113 }
114}
115
116impl Deref for WebPImage {
117 type Target = [u8];
118
119 fn deref(&self) -> &Self::Target {
120 self.data.deref()
121 }
122}
123
124impl DerefMut for WebPImage {
125 fn deref_mut(&mut self) -> &mut Self::Target {
126 self.data.deref_mut()
127 }
128}
129
130#[derive(Copy, Clone, Debug, Eq, PartialEq)]
132pub enum PixelLayout {
133 Rgb,
134 Rgba,
135}
136
137impl PixelLayout {
138 pub fn is_alpha(self) -> bool {
140 self == PixelLayout::Rgba
141 }
142
143 pub fn bytes_per_pixel(self) -> u8 {
144 match self {
145 PixelLayout::Rgb => 3,
146 PixelLayout::Rgba => 4,
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_pixel_layout_is_alpha() {
157 assert!(!PixelLayout::Rgb.is_alpha());
158 assert!(PixelLayout::Rgba.is_alpha());
159 }
160
161 #[test]
162 fn test_webpimage_accessors() {
163 let data = vec![10, 20, 30, 40, 50, 60, 70, 80];
164
165 let mut boxed = data.clone().into_boxed_slice();
166 let ptr = boxed.as_mut_ptr();
167 let len = boxed.len();
168 std::mem::forget(boxed);
169
170 let mem = WebPMemory(ptr, len);
171 let img = WebPImage::new(mem, PixelLayout::Rgba, 2, 1);
172
173 assert_eq!(img.width(), 2);
174 assert_eq!(img.height(), 1);
175 assert!(img.is_alpha());
176 assert_eq!(img.layout(), PixelLayout::Rgba);
177
178 assert_eq!(&img[..], &data[..]);
179 }
180
181 #[test]
182 fn test_webpimage_deref_mut() {
183 let data = vec![1, 2, 3, 4];
184 let mut boxed = data.clone().into_boxed_slice();
185 let ptr = boxed.as_mut_ptr();
186 let len = boxed.len();
187 std::mem::forget(boxed);
188
189 let mem = WebPMemory(ptr, len);
190 let mut img = WebPImage::new(mem, PixelLayout::Rgb, 2, 1);
191
192 img.deref_mut()[0] = 42;
193 assert_eq!(img[0], 42);
194 }
195
196 #[test]
197 fn test_webpmemory_drop_calls_webpfree() {
198 let data = vec![1, 2, 3, 4];
199 let mut boxed = data.clone().into_boxed_slice();
200 let ptr = boxed.as_mut_ptr();
201 let len = boxed.len();
202 std::mem::forget(boxed);
203
204 let _mem = WebPMemory(ptr, len);
205 }
206
207 #[test]
208 fn test_pixel_layout_equality() {
209 assert_eq!(PixelLayout::Rgb, PixelLayout::Rgb);
210 assert_ne!(PixelLayout::Rgb, PixelLayout::Rgba);
211 }
212
213 #[test]
214 fn test_webpmemory_debug_exact() {
215 let data = vec![1u8, 2, 3];
216 let mut boxed = data.into_boxed_slice();
217 let ptr = boxed.as_mut_ptr();
218 let len = boxed.len();
219 std::mem::forget(boxed);
220
221 let mem = WebPMemory(ptr, len);
222
223 let dbg_str = format!("{:?}", mem);
224
225 assert_eq!(dbg_str, "WebpMemory");
226 }
227
228 #[test]
229 fn test_manageedpicture_deref() {
230 let pic = unsafe { std::mem::zeroed::<WebPPicture>() };
231 let managed = ManageedPicture(pic);
232
233 let inner_ref: &WebPPicture = &*managed;
234 let orig_ptr = &managed.0 as *const WebPPicture;
235 let deref_ptr = inner_ref as *const WebPPicture;
236 assert_eq!(orig_ptr, deref_ptr);
237 }
238}