cairo/
image_surface.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4    ops::{Deref, DerefMut},
5    rc::Rc,
6    slice,
7};
8
9#[cfg(feature = "use_glib")]
10use glib::translate::*;
11
12use crate::{ffi, utils::status_to_result, BorrowError, Error, Format, Surface, SurfaceType};
13
14declare_surface!(ImageSurface, SurfaceType::Image);
15
16impl ImageSurface {
17    #[doc(alias = "cairo_image_surface_create")]
18    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error> {
19        unsafe {
20            Self::from_raw_full(ffi::cairo_image_surface_create(
21                format.into(),
22                width,
23                height,
24            ))
25        }
26    }
27
28    // rustdoc-stripper-ignore-next
29    /// Creates an image surface for the provided pixel data.
30    /// - The pointer `data` is the beginning of the underlying slice,
31    ///   and at least `width * stride` succeeding bytes should be allocated.
32    /// - `data` must live longer than any reference to the returned surface.
33    /// - You have to free `data` by yourself.
34    #[doc(alias = "cairo_image_surface_create_for_data")]
35    pub unsafe fn create_for_data_unsafe(
36        data: *mut u8,
37        format: Format,
38        width: i32,
39        height: i32,
40        stride: i32,
41    ) -> Result<ImageSurface, Error> {
42        ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
43            data,
44            format.into(),
45            width,
46            height,
47            stride,
48        ))
49    }
50
51    #[doc(alias = "cairo_image_surface_create_for_data")]
52    pub fn create_for_data<D: AsMut<[u8]> + 'static>(
53        data: D,
54        format: Format,
55        width: i32,
56        height: i32,
57        stride: i32,
58    ) -> Result<ImageSurface, Error> {
59        let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);
60
61        let (ptr, len) = {
62            let data: &mut [u8] = (*data).as_mut();
63
64            (data.as_mut_ptr(), data.len())
65        };
66
67        assert!(width >= 0, "width must be non-negative");
68        assert!(height >= 0, "height must be non-negative");
69        assert!(stride >= 0, "stride must be non-negative");
70
71        // check if there is integer overflow
72        assert!(len >= height.checked_mul(stride).unwrap() as usize);
73        let result = unsafe {
74            ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
75                ptr,
76                format.into(),
77                width,
78                height,
79                stride,
80            ))
81        };
82        if let Ok(surface) = &result {
83            static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
84                crate::UserDataKey::new();
85            surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))?;
86        }
87        result
88    }
89
90    #[doc(alias = "cairo_image_surface_get_data")]
91    #[doc(alias = "get_data")]
92    pub fn data(&mut self) -> Result<ImageSurfaceData<'_>, BorrowError> {
93        unsafe {
94            if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
95                return Err(BorrowError::NonExclusive);
96            }
97
98            self.flush();
99            let status = ffi::cairo_surface_status(self.to_raw_none());
100            if let Some(err) = status_to_result(status).err() {
101                return Err(BorrowError::from(err));
102            }
103            if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self)
104            {
105                return Err(BorrowError::from(Error::SurfaceFinished));
106            }
107            Ok(ImageSurfaceData::new(self))
108        }
109    }
110
111    pub fn take_data(self) -> Result<ImageSurfaceDataOwned, BorrowError> {
112        unsafe {
113            if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
114                return Err(BorrowError::NonExclusive);
115            }
116
117            self.flush();
118            let status = ffi::cairo_surface_status(self.to_raw_none());
119            if let Some(err) = status_to_result(status).err() {
120                return Err(BorrowError::from(err));
121            }
122            if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(&self)
123            {
124                return Err(BorrowError::from(Error::SurfaceFinished));
125            }
126            Ok(ImageSurfaceDataOwned { surface: self })
127        }
128    }
129
130    pub fn with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError> {
131        self.flush();
132        unsafe {
133            let status = ffi::cairo_surface_status(self.to_raw_none());
134            if let Some(err) = status_to_result(status).err() {
135                return Err(BorrowError::from(err));
136            }
137            let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none());
138            if ptr.is_null() || is_finished(self) {
139                return Err(BorrowError::from(Error::SurfaceFinished));
140            }
141            let len = self.height() as usize * self.stride() as usize;
142            let data = if len == 0 {
143                &[]
144            } else {
145                slice::from_raw_parts(ptr, len)
146            };
147            f(data);
148        }
149        Ok(())
150    }
151
152    #[doc(alias = "cairo_image_surface_get_format")]
153    #[doc(alias = "get_format")]
154    pub fn format(&self) -> Format {
155        unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
156    }
157
158    #[doc(alias = "cairo_image_surface_get_height")]
159    #[doc(alias = "get_height")]
160    pub fn height(&self) -> i32 {
161        unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
162    }
163
164    #[doc(alias = "cairo_image_surface_get_stride")]
165    #[doc(alias = "get_stride")]
166    pub fn stride(&self) -> i32 {
167        unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
168    }
169
170    #[doc(alias = "cairo_image_surface_get_width")]
171    #[doc(alias = "get_width")]
172    pub fn width(&self) -> i32 {
173        unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
174    }
175}
176
177pub struct ImageSurfaceDataOwned {
178    surface: ImageSurface,
179}
180
181unsafe impl Send for ImageSurfaceDataOwned {}
182unsafe impl Sync for ImageSurfaceDataOwned {}
183
184impl ImageSurfaceDataOwned {
185    #[inline]
186    pub fn into_inner(self) -> ImageSurface {
187        self.surface
188    }
189}
190
191impl AsRef<[u8]> for ImageSurfaceDataOwned {
192    #[inline]
193    fn as_ref(&self) -> &[u8] {
194        let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
195        unsafe {
196            let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
197            if ptr.is_null() || len == 0 {
198                return &[];
199            }
200            slice::from_raw_parts(ptr, len)
201        }
202    }
203}
204
205impl AsMut<[u8]> for ImageSurfaceDataOwned {
206    #[inline]
207    fn as_mut(&mut self) -> &mut [u8] {
208        let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
209        unsafe {
210            let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
211            if ptr.is_null() || len == 0 {
212                return &mut [];
213            }
214            slice::from_raw_parts_mut(ptr, len)
215        }
216    }
217}
218
219impl Deref for ImageSurfaceDataOwned {
220    type Target = [u8];
221
222    #[inline]
223    fn deref(&self) -> &Self::Target {
224        self.as_ref()
225    }
226}
227
228impl DerefMut for ImageSurfaceDataOwned {
229    #[inline]
230    fn deref_mut(&mut self) -> &mut Self::Target {
231        self.as_mut()
232    }
233}
234
235#[derive(Debug)]
236pub struct ImageSurfaceData<'a> {
237    surface: &'a mut ImageSurface,
238    slice: &'a mut [u8],
239    dirty: bool,
240}
241
242unsafe impl Send for ImageSurfaceData<'_> {}
243unsafe impl Sync for ImageSurfaceData<'_> {}
244
245impl<'a> ImageSurfaceData<'a> {
246    fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
247        unsafe {
248            let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
249            let len = (surface.stride() as usize) * (surface.height() as usize);
250            ImageSurfaceData {
251                surface,
252                slice: if ptr.is_null() || len == 0 {
253                    &mut []
254                } else {
255                    slice::from_raw_parts_mut(ptr, len)
256                },
257                dirty: false,
258            }
259        }
260    }
261}
262
263impl Drop for ImageSurfaceData<'_> {
264    #[inline]
265    fn drop(&mut self) {
266        if self.dirty {
267            self.surface.mark_dirty()
268        }
269    }
270}
271
272impl Deref for ImageSurfaceData<'_> {
273    type Target = [u8];
274
275    #[inline]
276    fn deref(&self) -> &[u8] {
277        self.slice
278    }
279}
280
281impl DerefMut for ImageSurfaceData<'_> {
282    #[inline]
283    fn deref_mut(&mut self) -> &mut [u8] {
284        self.dirty = true;
285        self.slice
286    }
287}
288
289// Workaround for cairo not having a direct way to check if the surface is finished.
290// See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/406
291fn is_finished(surface: &ImageSurface) -> bool {
292    use super::Context;
293    Context::new(surface).is_err()
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn create_with_invalid_size_yields_error() {
302        let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
303        assert!(result.is_err());
304    }
305
306    #[test]
307    fn create_for_data_with_invalid_stride_yields_error() {
308        let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride
309        assert!(result.is_err());
310    }
311
312    #[test]
313    fn create_with_valid_size() {
314        let result = ImageSurface::create(Format::ARgb32, 10, 10);
315        assert!(result.is_ok());
316
317        let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
318        assert!(result.is_ok());
319    }
320
321    #[test]
322    fn no_crash_after_finish() {
323        let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap();
324
325        surf.finish();
326
327        assert!(surf.data().is_err());
328    }
329
330    #[test]
331    fn create_from_owned() {
332        let result = ImageSurface::create(Format::ARgb32, 10, 10);
333        assert!(result.is_ok());
334        let image_surface = result.unwrap();
335        let stride = image_surface.stride();
336        let data = image_surface.take_data().unwrap();
337        let second = ImageSurface::create_for_data(data, Format::ARgb32, 10, 10, stride);
338        assert!(second.is_ok())
339    }
340
341    #[cfg(feature = "use_glib")]
342    #[test]
343    fn surface_gvalues() {
344        use glib::prelude::*;
345
346        let surface = ImageSurface::create(Format::ARgb32, 10, 10).unwrap();
347        let value = surface.to_value();
348        assert_eq!(value.get::<ImageSurface>().unwrap().width(), 10);
349        let _ = surface.to_value();
350        let surface = Some(surface);
351        let value = surface.to_value();
352        assert_eq!(
353            value
354                .get::<Option<ImageSurface>>()
355                .unwrap()
356                .map(|s| s.width()),
357            Some(10)
358        );
359        let _ = surface.as_ref().to_value();
360        assert_eq!(
361            value
362                .get::<Option<&ImageSurface>>()
363                .unwrap()
364                .map(|s| s.width()),
365            Some(10)
366        );
367    }
368}