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