cairo/
surface.rs

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