cairo/
surface.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#[cfg(feature = "use_glib")]
4use std::marker::PhantomData;
5use std::{ffi::CString, ops::Deref, ptr, slice};
6
7#[cfg(feature = "use_glib")]
8use glib::translate::*;
9use libc::{c_ulong, c_void};
10
11use crate::{
12    Content, Device, Error, Format, ImageSurface, Rectangle, RectangleInt, SurfaceType, ffi,
13    utils::status_to_result,
14};
15
16#[derive(Debug)]
17#[doc(alias = "cairo_surface_t")]
18#[repr(transparent)]
19pub struct Surface(ptr::NonNull<ffi::cairo_surface_t>);
20
21impl Surface {
22    #[inline]
23    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
24        unsafe {
25            debug_assert!(!ptr.is_null());
26            ffi::cairo_surface_reference(ptr);
27            Surface(ptr::NonNull::new_unchecked(ptr))
28        }
29    }
30
31    #[inline]
32    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> {
33        unsafe {
34            debug_assert!(!ptr.is_null());
35            crate::Borrowed::new(Surface(ptr::NonNull::new_unchecked(ptr)))
36        }
37    }
38
39    #[inline]
40    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Error> {
41        unsafe {
42            debug_assert!(!ptr.is_null());
43            let status = ffi::cairo_surface_status(ptr);
44            status_to_result(status)?;
45            Ok(Surface(ptr::NonNull::new_unchecked(ptr)))
46        }
47    }
48
49    #[inline]
50    pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
51        self.0.as_ptr()
52    }
53
54    #[doc(alias = "cairo_surface_create_similar")]
55    pub fn create_similar(
56        &self,
57        content: Content,
58        width: i32,
59        height: i32,
60    ) -> Result<Surface, Error> {
61        unsafe {
62            Self::from_raw_full(ffi::cairo_surface_create_similar(
63                self.0.as_ptr(),
64                content.into(),
65                width,
66                height,
67            ))
68        }
69    }
70
71    #[doc(alias = "cairo_surface_create_for_rectangle")]
72    pub fn create_for_rectangle(&self, bounds: Rectangle) -> Result<Surface, Error> {
73        unsafe {
74            Self::from_raw_full(ffi::cairo_surface_create_for_rectangle(
75                self.0.as_ptr(),
76                bounds.x(),
77                bounds.y(),
78                bounds.width(),
79                bounds.height(),
80            ))
81        }
82    }
83
84    #[doc(alias = "cairo_surface_get_mime_data")]
85    #[doc(alias = "get_mime_data")]
86    pub fn mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
87        let mut data_ptr: *mut u8 = ptr::null_mut();
88        let mut length: c_ulong = 0;
89        // The function actually needs a mutable pointer
90        #[allow(clippy::unnecessary_mut_passed)]
91        unsafe {
92            let mime_type = CString::new(mime_type).unwrap();
93            ffi::cairo_surface_get_mime_data(
94                self.to_raw_none(),
95                mime_type.as_ptr(),
96                &mut data_ptr,
97                &mut length,
98            );
99            if !data_ptr.is_null() && length != 0 {
100                Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec())
101            } else {
102                None
103            }
104        }
105    }
106
107    #[doc(alias = "cairo_surface_get_mime_data")]
108    #[doc(alias = "get_mime_data_raw")]
109    pub unsafe fn mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
110        unsafe {
111            let mut data_ptr: *mut u8 = ptr::null_mut();
112            let mut length: c_ulong = 0;
113            let mime_type = CString::new(mime_type).unwrap();
114            // The function actually needs a mutable pointer
115            #[allow(clippy::unnecessary_mut_passed)]
116            {
117                ffi::cairo_surface_get_mime_data(
118                    self.to_raw_none(),
119                    mime_type.as_ptr(),
120                    &mut data_ptr,
121                    &mut length,
122                );
123            }
124            if !data_ptr.is_null() && length != 0 {
125                Some(slice::from_raw_parts(
126                    data_ptr as *const u8,
127                    length as usize,
128                ))
129            } else {
130                None
131            }
132        }
133    }
134
135    #[doc(alias = "cairo_surface_set_mime_data")]
136    pub fn set_mime_data<T: AsRef<[u8]> + 'static>(
137        &self,
138        mime_type: &str,
139        slice: T,
140    ) -> Result<(), Error> {
141        let b = Box::new(slice);
142        let (size, data) = {
143            let slice = (*b).as_ref();
144            (slice.len(), slice.as_ptr())
145        };
146
147        let user_data = Box::into_raw(b);
148
149        unsafe extern "C" fn unbox<T>(data: *mut c_void) {
150            unsafe {
151                let data: Box<T> = Box::from_raw(data as *mut T);
152                drop(data);
153            }
154        }
155
156        let status = unsafe {
157            let mime_type = CString::new(mime_type).unwrap();
158            ffi::cairo_surface_set_mime_data(
159                self.to_raw_none(),
160                mime_type.as_ptr(),
161                data,
162                size as c_ulong,
163                Some(unbox::<T>),
164                user_data as *mut _,
165            )
166        };
167        status_to_result(status)
168    }
169
170    #[doc(alias = "cairo_surface_supports_mime_type")]
171    pub fn supports_mime_type(&self, mime_type: &str) -> bool {
172        unsafe {
173            let mime_type = CString::new(mime_type).unwrap();
174            ffi::cairo_surface_supports_mime_type(self.0.as_ptr(), mime_type.as_ptr()).as_bool()
175        }
176    }
177
178    #[doc(alias = "cairo_surface_get_device")]
179    #[doc(alias = "get_device")]
180    pub fn device(&self) -> Option<Device> {
181        unsafe {
182            let device = ffi::cairo_surface_get_device(self.to_raw_none());
183            if device.is_null() {
184                None
185            } else {
186                Some(Device::from_raw_none(device))
187            }
188        }
189    }
190
191    #[doc(alias = "cairo_surface_get_content")]
192    pub fn content(&self) -> Content {
193        unsafe { ffi::cairo_surface_get_content(self.to_raw_none()) }.into()
194    }
195
196    #[doc(alias = "cairo_surface_set_device_offset")]
197    pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
198        unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
199    }
200
201    #[doc(alias = "cairo_surface_get_device_offset")]
202    #[doc(alias = "get_device_offset")]
203    pub fn device_offset(&self) -> (f64, f64) {
204        let mut x_offset = 0.0f64;
205        let mut y_offset = 0.0f64;
206        unsafe {
207            ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
208        }
209        (x_offset, y_offset)
210    }
211
212    #[doc(alias = "cairo_surface_set_device_scale")]
213    pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
214        unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
215    }
216
217    #[doc(alias = "cairo_surface_get_device_scale")]
218    #[doc(alias = "get_device_scale")]
219    pub fn device_scale(&self) -> (f64, f64) {
220        let mut x_scale = 0.0f64;
221        let mut y_scale = 0.0f64;
222        unsafe {
223            ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
224        }
225        (x_scale, y_scale)
226    }
227
228    #[doc(alias = "cairo_surface_set_fallback_resolution")]
229    pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
230        unsafe {
231            ffi::cairo_surface_set_fallback_resolution(
232                self.to_raw_none(),
233                x_pixels_per_inch,
234                y_pixels_per_inch,
235            )
236        }
237    }
238
239    #[doc(alias = "cairo_surface_get_fallback_resolution")]
240    #[doc(alias = "get_fallback_resolution")]
241    pub fn fallback_resolution(&self) -> (f64, f64) {
242        let mut x_pixels_per_inch = 0.0f64;
243        let mut y_pixels_per_inch = 0.0f64;
244        unsafe {
245            ffi::cairo_surface_get_fallback_resolution(
246                self.to_raw_none(),
247                &mut x_pixels_per_inch,
248                &mut y_pixels_per_inch,
249            );
250        }
251        (x_pixels_per_inch, y_pixels_per_inch)
252    }
253
254    #[doc(alias = "cairo_surface_create_similar_image")]
255    pub fn create_similar_image(
256        &self,
257        format: Format,
258        width: i32,
259        height: i32,
260    ) -> Result<ImageSurface, Error> {
261        unsafe {
262            ImageSurface::from_raw_full(ffi::cairo_surface_create_similar_image(
263                self.to_raw_none(),
264                format.into(),
265                width,
266                height,
267            ))
268        }
269    }
270
271    #[doc(alias = "cairo_surface_map_to_image")]
272    pub fn map_to_image(&self, extents: Option<RectangleInt>) -> Result<MappedImageSurface, Error> {
273        unsafe {
274            ImageSurface::from_raw_none(match extents {
275                Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
276                None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()),
277            })
278            .map(|s| MappedImageSurface {
279                original_surface: self.clone(),
280                image_surface: s,
281            })
282        }
283    }
284
285    #[doc(alias = "cairo_surface_mark_dirty")]
286    pub fn mark_dirty(&self) {
287        unsafe { ffi::cairo_surface_mark_dirty(self.to_raw_none()) }
288    }
289
290    #[doc(alias = "cairo_surface_mark_dirty_rectangle")]
291    pub fn mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
292        unsafe { ffi::cairo_surface_mark_dirty_rectangle(self.to_raw_none(), x, y, width, height) }
293    }
294
295    #[doc(alias = "cairo_surface_status")]
296    #[inline]
297    pub fn status(&self) -> Result<(), Error> {
298        let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) };
299        status_to_result(status)
300    }
301
302    user_data_methods! {
303        ffi::cairo_surface_get_user_data,
304        ffi::cairo_surface_set_user_data,
305    }
306}
307
308#[cfg(feature = "use_glib")]
309impl IntoGlibPtr<*mut ffi::cairo_surface_t> for Surface {
310    #[inline]
311    fn into_glib_ptr(self) -> *mut ffi::cairo_surface_t {
312        std::mem::ManuallyDrop::new(self).to_glib_none().0
313    }
314}
315
316#[cfg(feature = "use_glib")]
317impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
318    type Storage = PhantomData<&'a Surface>;
319
320    #[inline]
321    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
322        Stash(self.to_raw_none(), PhantomData)
323    }
324
325    #[inline]
326    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
327        unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
328    }
329}
330
331#[cfg(feature = "use_glib")]
332impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
333    #[inline]
334    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
335        unsafe { Self::from_raw_none(ptr) }
336    }
337}
338
339#[cfg(feature = "use_glib")]
340impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
341    #[inline]
342    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> {
343        unsafe { Self::from_raw_borrow(ptr) }
344    }
345}
346
347#[cfg(feature = "use_glib")]
348impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
349    #[inline]
350    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
351        unsafe { Self::from_raw_full(ptr).unwrap() }
352    }
353}
354
355#[cfg(feature = "use_glib")]
356gvalue_impl!(
357    Surface,
358    ffi::cairo_surface_t,
359    ffi::gobject::cairo_gobject_surface_get_type
360);
361
362impl Clone for Surface {
363    #[inline]
364    fn clone(&self) -> Surface {
365        unsafe { Self::from_raw_none(self.0.as_ptr()) }
366    }
367}
368
369impl Drop for Surface {
370    #[inline]
371    fn drop(&mut self) {
372        unsafe {
373            ffi::cairo_surface_destroy(self.0.as_ptr());
374        }
375    }
376}
377
378impl AsRef<Surface> for Surface {
379    #[inline]
380    fn as_ref(&self) -> &Surface {
381        self
382    }
383}
384
385impl Surface {
386    #[doc(alias = "cairo_surface_flush")]
387    pub fn flush(&self) {
388        unsafe {
389            ffi::cairo_surface_flush(self.0.as_ptr());
390        }
391    }
392
393    #[doc(alias = "cairo_surface_finish")]
394    pub fn finish(&self) {
395        unsafe {
396            ffi::cairo_surface_finish(self.0.as_ptr());
397        }
398    }
399
400    #[doc(alias = "cairo_surface_get_type")]
401    #[doc(alias = "get_type")]
402    pub fn type_(&self) -> SurfaceType {
403        unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0.as_ptr())) }
404    }
405}
406
407#[derive(Debug)]
408pub struct MappedImageSurface {
409    original_surface: Surface,
410    image_surface: ImageSurface,
411}
412
413impl Deref for MappedImageSurface {
414    type Target = ImageSurface;
415
416    #[inline]
417    fn deref(&self) -> &ImageSurface {
418        &self.image_surface
419    }
420}
421
422impl AsRef<ImageSurface> for MappedImageSurface {
423    #[inline]
424    fn as_ref(&self) -> &ImageSurface {
425        &self.image_surface
426    }
427}
428
429impl Drop for MappedImageSurface {
430    #[inline]
431    fn drop(&mut self) {
432        unsafe {
433            ffi::cairo_surface_unmap_image(
434                self.original_surface.to_raw_none(),
435                self.image_surface.to_raw_none(),
436            );
437        }
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use crate::{Format, ImageSurface, constants::MIME_TYPE_PNG};
444
445    #[test]
446    fn mime_data() {
447        let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
448        let data = surface.mime_data(MIME_TYPE_PNG);
449        /* Initially the data for any mime type has to be none */
450        assert!(data.is_none());
451
452        assert!(surface.set_mime_data(MIME_TYPE_PNG, [1u8, 10u8]).is_ok());
453        let data = surface.mime_data(MIME_TYPE_PNG).unwrap();
454        assert_eq!(data, &[1u8, 10u8]);
455    }
456}