webp/
shared.rs

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
8/// This struct represents a safe wrapper around memory owned by libwebp.
9/// Its data contents can be accessed through the Deref and DerefMut traits.
10pub 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
61/// This struct represents a decoded image.
62/// Its data contents can be accessed through the Deref and DerefMut traits.
63/// It is also possible to create an image::DynamicImage from this struct.
64pub 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    /// Creates a DynamicImage from this WebPImage.
82    #[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    /// Returns the width of the image in pixels.
98    pub fn width(&self) -> u32 {
99        self.width
100    }
101
102    /// Returns the height of the image in pixels.
103    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/// Describes the pixel layout (the order of the color channels) of an image.
131#[derive(Copy, Clone, Debug, Eq, PartialEq)]
132pub enum PixelLayout {
133    Rgb,
134    Rgba,
135}
136
137impl PixelLayout {
138    /// Returns true if the pixel contains an alpha channel.
139    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}