1 // Copyright 2015-2016, The Gtk-rs Project Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution.
3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4 
5 use libc::{c_ulong, c_void};
6 use std::ffi::CString;
7 use std::fmt;
8 use std::ops::Deref;
9 use std::ptr;
10 use std::slice;
11 
12 use enums::{Content, Format, Status, SurfaceType};
13 use ffi;
14 #[cfg(feature = "use_glib")]
15 use glib::translate::*;
16 
17 use image_surface::ImageSurface;
18 use rectangle_int::RectangleInt;
19 
20 #[derive(Debug)]
21 pub struct Surface(*mut ffi::cairo_surface_t, bool);
22 
23 impl Surface {
from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface24     pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
25         assert!(!ptr.is_null());
26         ffi::cairo_surface_reference(ptr);
27         Surface(ptr, false)
28     }
29 
from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface30     pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
31         assert!(!ptr.is_null());
32         Surface(ptr, true)
33     }
34 
from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Status>35     pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Status> {
36         assert!(!ptr.is_null());
37         let status = Status::from(ffi::cairo_surface_status(ptr));
38         status.to_result(Surface(ptr, false))
39     }
40 
to_raw_none(&self) -> *mut ffi::cairo_surface_t41     pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
42         self.0
43     }
44 
create_similar( &self, content: Content, width: i32, height: i32, ) -> Result<Surface, Status>45     pub fn create_similar(
46         &self,
47         content: Content,
48         width: i32,
49         height: i32,
50     ) -> Result<Surface, Status> {
51         unsafe {
52             Self::from_raw_full(ffi::cairo_surface_create_similar(
53                 self.0,
54                 content.into(),
55                 width,
56                 height,
57             ))
58         }
59     }
60 
get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>>61     pub fn get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
62         let data_ptr: *mut u8 = ptr::null_mut();
63         let mut length: c_ulong = 0;
64         unsafe {
65             let mime_type = CString::new(mime_type).unwrap();
66             ffi::cairo_surface_get_mime_data(
67                 self.to_raw_none(),
68                 mime_type.as_ptr(),
69                 &data_ptr,
70                 &mut length,
71             );
72             if !data_ptr.is_null() && length != 0 {
73                 Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec())
74             } else {
75                 None
76             }
77         }
78     }
79 
get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]>80     pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
81         let data_ptr: *mut u8 = ptr::null_mut();
82         let mut length: c_ulong = 0;
83         let mime_type = CString::new(mime_type).unwrap();
84         ffi::cairo_surface_get_mime_data(
85             self.to_raw_none(),
86             mime_type.as_ptr(),
87             &data_ptr,
88             &mut length,
89         );
90         if !data_ptr.is_null() && length != 0 {
91             Some(slice::from_raw_parts(
92                 data_ptr as *const u8,
93                 length as usize,
94             ))
95         } else {
96             None
97         }
98     }
99 
set_mime_data<T: AsRef<[u8]> + 'static>( &self, mime_type: &str, slice: T, ) -> Result<(), Status>100     pub fn set_mime_data<T: AsRef<[u8]> + 'static>(
101         &self,
102         mime_type: &str,
103         slice: T,
104     ) -> Result<(), Status> {
105         let b = Box::new(slice);
106         let (size, data) = {
107             let slice = (*b).as_ref();
108             (slice.len(), slice.as_ptr())
109         };
110 
111         let user_data = Box::into_raw(b);
112 
113         unsafe extern "C" fn unbox<T>(data: *mut c_void) {
114             let data: Box<T> = Box::from_raw(data as *mut T);
115             drop(data);
116         }
117 
118         let status = unsafe {
119             let mime_type = CString::new(mime_type).unwrap();
120             Status::from(ffi::cairo_surface_set_mime_data(
121                 self.to_raw_none(),
122                 mime_type.as_ptr(),
123                 data,
124                 size as c_ulong,
125                 Some(unbox::<T>),
126                 user_data as *mut _,
127             ))
128         };
129 
130         status.to_result(())
131     }
132 
supports_mime_type(&self, mime_type: &str) -> bool133     pub fn supports_mime_type(&self, mime_type: &str) -> bool {
134         unsafe {
135             let mime_type = CString::new(mime_type).unwrap();
136             ffi::cairo_surface_supports_mime_type(self.0, mime_type.as_ptr()).as_bool()
137         }
138     }
139 
set_device_offset(&self, x_offset: f64, y_offset: f64)140     pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
141         unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
142     }
143 
get_device_offset(&self) -> (f64, f64)144     pub fn get_device_offset(&self) -> (f64, f64) {
145         let mut x_offset = 0.0f64;
146         let mut y_offset = 0.0f64;
147         unsafe {
148             ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
149         }
150         (x_offset, y_offset)
151     }
152 
153     #[cfg(any(feature = "v1_14", feature = "dox"))]
set_device_scale(&self, x_scale: f64, y_scale: f64)154     pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
155         unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
156     }
157 
158     #[cfg(any(feature = "v1_14", feature = "dox"))]
get_device_scale(&self) -> (f64, f64)159     pub fn get_device_scale(&self) -> (f64, f64) {
160         let mut x_scale = 0.0f64;
161         let mut y_scale = 0.0f64;
162         unsafe {
163             ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
164         }
165         (x_scale, y_scale)
166     }
167 
set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64)168     pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
169         unsafe {
170             ffi::cairo_surface_set_fallback_resolution(
171                 self.to_raw_none(),
172                 x_pixels_per_inch,
173                 y_pixels_per_inch,
174             )
175         }
176     }
177 
get_fallback_resolution(&self) -> (f64, f64)178     pub fn get_fallback_resolution(&self) -> (f64, f64) {
179         let mut x_pixels_per_inch = 0.0f64;
180         let mut y_pixels_per_inch = 0.0f64;
181         unsafe {
182             ffi::cairo_surface_get_fallback_resolution(
183                 self.to_raw_none(),
184                 &mut x_pixels_per_inch,
185                 &mut y_pixels_per_inch,
186             );
187         }
188         (x_pixels_per_inch, y_pixels_per_inch)
189     }
190 
create_similar_image( &self, format: Format, width: i32, height: i32, ) -> Result<Surface, Status>191     pub fn create_similar_image(
192         &self,
193         format: Format,
194         width: i32,
195         height: i32,
196     ) -> Result<Surface, Status> {
197         unsafe {
198             Self::from_raw_full(ffi::cairo_surface_create_similar_image(
199                 self.to_raw_none(),
200                 format.into(),
201                 width,
202                 height,
203             ))
204         }
205     }
206 
map_to_image( &self, extents: Option<RectangleInt>, ) -> Result<MappedImageSurface, Status>207     pub fn map_to_image(
208         &self,
209         extents: Option<RectangleInt>,
210     ) -> Result<MappedImageSurface, Status> {
211         unsafe {
212             ImageSurface::from_raw_full(match extents {
213                 Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
214                 None => ffi::cairo_surface_map_to_image(self.to_raw_none(), 0 as *const _),
215             })
216             .map(|s| MappedImageSurface {
217                 original_surface: self.clone(),
218                 image_surface: s,
219             })
220         }
221     }
222 
223     user_data_methods! {
224         ffi::cairo_surface_get_user_data,
225         ffi::cairo_surface_set_user_data,
226     }
227 }
228 
229 #[cfg(feature = "use_glib")]
230 impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
231     type Storage = &'a Surface;
232 
233     #[inline]
to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self>234     fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
235         Stash(self.to_raw_none(), self)
236     }
237 
238     #[inline]
to_glib_full(&self) -> *mut ffi::cairo_surface_t239     fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
240         unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
241     }
242 }
243 
244 #[cfg(feature = "use_glib")]
245 impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
246     #[inline]
from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface247     unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
248         Self::from_raw_none(ptr)
249     }
250 }
251 
252 #[cfg(feature = "use_glib")]
253 impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
254     #[inline]
from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface255     unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
256         Self::from_raw_borrow(ptr)
257     }
258 }
259 
260 #[cfg(feature = "use_glib")]
261 impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
262     #[inline]
from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface263     unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
264         Self::from_raw_full(ptr).unwrap()
265     }
266 }
267 
268 #[cfg(feature = "use_glib")]
269 gvalue_impl!(
270     Surface,
271     ffi::cairo_surface_t,
272     ffi::gobject::cairo_gobject_surface_get_type
273 );
274 
275 impl Clone for Surface {
clone(&self) -> Surface276     fn clone(&self) -> Surface {
277         unsafe { Self::from_raw_none(self.0) }
278     }
279 }
280 
281 impl Drop for Surface {
drop(&mut self)282     fn drop(&mut self) {
283         if !self.1 {
284             unsafe {
285                 ffi::cairo_surface_destroy(self.0);
286             }
287         }
288     }
289 }
290 
291 impl fmt::Display for Surface {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result292     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293         write!(f, "Surface")
294     }
295 }
296 
297 impl Surface {
flush(&self)298     pub fn flush(&self) {
299         unsafe {
300             ffi::cairo_surface_flush(self.0);
301         }
302     }
303 
finish(&self)304     pub fn finish(&self) {
305         unsafe {
306             ffi::cairo_surface_finish(self.0);
307         }
308     }
309 
get_type(&self) -> SurfaceType310     pub fn get_type(&self) -> SurfaceType {
311         unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0)) }
312     }
313 
status(&self) -> Status314     pub fn status(&self) -> Status {
315         unsafe { Status::from(ffi::cairo_surface_status(self.0)) }
316     }
317 }
318 
319 #[derive(Debug)]
320 pub struct MappedImageSurface {
321     original_surface: Surface,
322     image_surface: ImageSurface,
323 }
324 
325 impl Deref for MappedImageSurface {
326     type Target = ImageSurface;
327 
deref(&self) -> &ImageSurface328     fn deref(&self) -> &ImageSurface {
329         &self.image_surface
330     }
331 }
332 
333 impl Drop for MappedImageSurface {
drop(&mut self)334     fn drop(&mut self) {
335         unsafe {
336             ffi::cairo_surface_unmap_image(
337                 self.original_surface.to_raw_none(),
338                 self.image_surface.to_raw_none(),
339             );
340             ffi::cairo_surface_reference(self.image_surface.to_raw_none());
341         }
342     }
343 }
344 
345 impl fmt::Display for MappedImageSurface {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result346     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
347         write!(f, "MappedImageSurface")
348     }
349 }
350 
351 #[cfg(test)]
352 mod tests {
353     use constants::MIME_TYPE_PNG;
354     use Format;
355     use ImageSurface;
356 
357     #[test]
mime_data()358     fn mime_data() {
359         let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
360         let data = surface.get_mime_data(MIME_TYPE_PNG);
361         /* Initially the data for any mime type has to be none */
362         assert!(data.is_none());
363 
364         assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok());
365         let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap();
366         assert_eq!(data, &[1u8, 10u8]);
367     }
368 }
369