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