cairo/
image_surface.rs

1// Copyright 2015, 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 std::convert::TryFrom;
6use std::ops::{Deref, DerefMut};
7use std::rc::Rc;
8use std::slice;
9
10use enums::{Format, SurfaceType};
11use ffi;
12#[cfg(feature = "use_glib")]
13use glib::translate::*;
14
15use std::fmt;
16use surface::Surface;
17use BorrowError;
18use Status;
19
20#[derive(Debug)]
21pub struct ImageSurface(Surface);
22
23impl TryFrom<Surface> for ImageSurface {
24    type Error = Surface;
25
26    fn try_from(surface: Surface) -> Result<ImageSurface, Surface> {
27        if surface.get_type() == SurfaceType::Image {
28            Ok(ImageSurface(surface))
29        } else {
30            Err(surface)
31        }
32    }
33}
34
35impl ImageSurface {
36    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<ImageSurface, Status> {
37        let surface = Self::try_from(Surface::from_raw_full(ptr)).unwrap();
38        let status = surface.status();
39        match status {
40            Status::Success => Ok(surface),
41            _ => Err(status),
42        }
43    }
44
45    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Status> {
46        unsafe {
47            Self::from_raw_full(ffi::cairo_image_surface_create(
48                format.into(),
49                width,
50                height,
51            ))
52        }
53    }
54
55    pub fn create_for_data<D: AsMut<[u8]> + 'static>(
56        data: D,
57        format: Format,
58        width: i32,
59        height: i32,
60        stride: i32,
61    ) -> Result<ImageSurface, Status> {
62        let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);
63
64        let (ptr, len) = {
65            let data: &mut [u8] = (*data).as_mut();
66
67            (data.as_mut_ptr(), data.len())
68        };
69
70        assert!(len >= (height * stride) as usize);
71        let result = unsafe {
72            ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
73                ptr,
74                format.into(),
75                width,
76                height,
77                stride,
78            ))
79        };
80        if let Ok(surface) = &result {
81            static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
82                crate::UserDataKey::new();
83            surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))
84        }
85        result
86    }
87
88    pub fn get_data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
89        unsafe {
90            if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
91                return Err(BorrowError::NonExclusive);
92            }
93            self.flush();
94            match self.status() {
95                Status::Success => (),
96                status => return Err(BorrowError::from(status)),
97            }
98            if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() {
99                return Err(BorrowError::from(Status::SurfaceFinished));
100            }
101            Ok(ImageSurfaceData::new(self))
102        }
103    }
104
105    pub fn get_format(&self) -> Format {
106        unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
107    }
108
109    pub fn get_height(&self) -> i32 {
110        unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
111    }
112
113    pub fn get_stride(&self) -> i32 {
114        unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
115    }
116
117    pub fn get_width(&self) -> i32 {
118        unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
119    }
120}
121
122#[cfg(feature = "use_glib")]
123impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for ImageSurface {
124    type Storage = &'a Surface;
125
126    #[inline]
127    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
128        let stash = self.0.to_glib_none();
129        Stash(stash.0, stash.1)
130    }
131
132    #[inline]
133    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
134        unsafe { ffi::cairo_surface_reference(self.0.to_glib_none().0) }
135    }
136}
137
138#[cfg(feature = "use_glib")]
139impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for ImageSurface {
140    #[inline]
141    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> ImageSurface {
142        Self::try_from(from_glib_none::<_, Surface>(ptr)).unwrap()
143    }
144}
145
146#[cfg(feature = "use_glib")]
147impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for ImageSurface {
148    #[inline]
149    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> ImageSurface {
150        Self::try_from(from_glib_borrow::<_, Surface>(ptr)).unwrap()
151    }
152}
153
154#[cfg(feature = "use_glib")]
155impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for ImageSurface {
156    #[inline]
157    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> ImageSurface {
158        Self::from_raw_full(ptr).unwrap()
159    }
160}
161
162#[cfg(feature = "use_glib")]
163gvalue_impl!(
164    ImageSurface,
165    ffi::cairo_surface_t,
166    ffi::gobject::cairo_gobject_surface_get_type
167);
168
169impl Deref for ImageSurface {
170    type Target = Surface;
171
172    fn deref(&self) -> &Surface {
173        &self.0
174    }
175}
176
177impl Clone for ImageSurface {
178    fn clone(&self) -> ImageSurface {
179        ImageSurface(self.0.clone())
180    }
181}
182
183impl fmt::Display for ImageSurface {
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        write!(f, "ImageSurface")
186    }
187}
188
189#[derive(Debug)]
190pub struct ImageSurfaceData<'a> {
191    surface: &'a mut ImageSurface,
192    slice: &'a mut [u8],
193    dirty: bool,
194}
195
196impl<'a> ImageSurfaceData<'a> {
197    fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
198        unsafe {
199            let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
200            debug_assert!(!ptr.is_null());
201            let len = (surface.get_stride() as usize) * (surface.get_height() as usize);
202            ImageSurfaceData {
203                surface,
204                slice: slice::from_raw_parts_mut(ptr, len),
205                dirty: false,
206            }
207        }
208    }
209}
210
211impl<'a> Drop for ImageSurfaceData<'a> {
212    fn drop(&mut self) {
213        if self.dirty {
214            unsafe { ffi::cairo_surface_mark_dirty(self.surface.to_raw_none()) }
215        }
216    }
217}
218
219impl<'a> Deref for ImageSurfaceData<'a> {
220    type Target = [u8];
221
222    fn deref(&self) -> &[u8] {
223        self.slice
224    }
225}
226
227impl<'a> DerefMut for ImageSurfaceData<'a> {
228    fn deref_mut(&mut self) -> &mut [u8] {
229        self.dirty = true;
230        self.slice
231    }
232}
233
234impl<'a> fmt::Display for ImageSurfaceData<'a> {
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        write!(f, "ImageSurfaceData")
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    #[test]
245    fn create_with_invalid_size_yields_error() {
246        let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
247        assert!(result.is_err());
248    }
249
250    #[test]
251    fn create_for_data_with_invalid_stride_yields_error() {
252        let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride
253        assert!(result.is_err());
254    }
255
256    #[test]
257    fn create_with_valid_size() {
258        let result = ImageSurface::create(Format::ARgb32, 10, 10);
259        assert!(result.is_ok());
260
261        let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
262        assert!(result.is_ok());
263    }
264}