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