1#[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 ffi, utils::status_to_result, Content, Device, Error, Format, ImageSurface, Rectangle,
13 RectangleInt, SurfaceType,
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 debug_assert!(!ptr.is_null());
25 ffi::cairo_surface_reference(ptr);
26 Surface(ptr::NonNull::new_unchecked(ptr))
27 }
28
29 #[inline]
30 pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> {
31 debug_assert!(!ptr.is_null());
32 crate::Borrowed::new(Surface(ptr::NonNull::new_unchecked(ptr)))
33 }
34
35 #[inline]
36 pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Error> {
37 debug_assert!(!ptr.is_null());
38 let status = ffi::cairo_surface_status(ptr);
39 status_to_result(status)?;
40 Ok(Surface(ptr::NonNull::new_unchecked(ptr)))
41 }
42
43 #[inline]
44 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")]
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")]
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")]
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")]
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")]
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")]
155 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")]
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_get_content")]
176 pub fn content(&self) -> Content {
177 unsafe { ffi::cairo_surface_get_content(self.to_raw_none()) }.into()
178 }
179
180 #[doc(alias = "cairo_surface_set_device_offset")]
181 pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
182 unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
183 }
184
185 #[doc(alias = "cairo_surface_get_device_offset")]
186 #[doc(alias = "get_device_offset")]
187 pub fn device_offset(&self) -> (f64, f64) {
188 let mut x_offset = 0.0f64;
189 let mut y_offset = 0.0f64;
190 unsafe {
191 ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
192 }
193 (x_offset, y_offset)
194 }
195
196 #[doc(alias = "cairo_surface_set_device_scale")]
197 pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
198 unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
199 }
200
201 #[doc(alias = "cairo_surface_get_device_scale")]
202 #[doc(alias = "get_device_scale")]
203 pub fn device_scale(&self) -> (f64, f64) {
204 let mut x_scale = 0.0f64;
205 let mut y_scale = 0.0f64;
206 unsafe {
207 ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
208 }
209 (x_scale, y_scale)
210 }
211
212 #[doc(alias = "cairo_surface_set_fallback_resolution")]
213 pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
214 unsafe {
215 ffi::cairo_surface_set_fallback_resolution(
216 self.to_raw_none(),
217 x_pixels_per_inch,
218 y_pixels_per_inch,
219 )
220 }
221 }
222
223 #[doc(alias = "cairo_surface_get_fallback_resolution")]
224 #[doc(alias = "get_fallback_resolution")]
225 pub fn fallback_resolution(&self) -> (f64, f64) {
226 let mut x_pixels_per_inch = 0.0f64;
227 let mut y_pixels_per_inch = 0.0f64;
228 unsafe {
229 ffi::cairo_surface_get_fallback_resolution(
230 self.to_raw_none(),
231 &mut x_pixels_per_inch,
232 &mut y_pixels_per_inch,
233 );
234 }
235 (x_pixels_per_inch, y_pixels_per_inch)
236 }
237
238 #[doc(alias = "cairo_surface_create_similar_image")]
239 pub fn create_similar_image(
240 &self,
241 format: Format,
242 width: i32,
243 height: i32,
244 ) -> Result<ImageSurface, Error> {
245 unsafe {
246 ImageSurface::from_raw_full(ffi::cairo_surface_create_similar_image(
247 self.to_raw_none(),
248 format.into(),
249 width,
250 height,
251 ))
252 }
253 }
254
255 #[doc(alias = "cairo_surface_map_to_image")]
256 pub fn map_to_image(&self, extents: Option<RectangleInt>) -> Result<MappedImageSurface, Error> {
257 unsafe {
258 ImageSurface::from_raw_none(match extents {
259 Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
260 None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()),
261 })
262 .map(|s| MappedImageSurface {
263 original_surface: self.clone(),
264 image_surface: s,
265 })
266 }
267 }
268
269 #[doc(alias = "cairo_surface_mark_dirty")]
270 pub fn mark_dirty(&self) {
271 unsafe { ffi::cairo_surface_mark_dirty(self.to_raw_none()) }
272 }
273
274 #[doc(alias = "cairo_surface_mark_dirty_rectangle")]
275 pub fn mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
276 unsafe { ffi::cairo_surface_mark_dirty_rectangle(self.to_raw_none(), x, y, width, height) }
277 }
278
279 #[doc(alias = "cairo_surface_status")]
280 #[inline]
281 pub fn status(&self) -> Result<(), Error> {
282 let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) };
283 status_to_result(status)
284 }
285
286 user_data_methods! {
287 ffi::cairo_surface_get_user_data,
288 ffi::cairo_surface_set_user_data,
289 }
290}
291
292#[cfg(feature = "use_glib")]
293impl IntoGlibPtr<*mut ffi::cairo_surface_t> for Surface {
294 #[inline]
295 unsafe fn into_glib_ptr(self) -> *mut ffi::cairo_surface_t {
296 std::mem::ManuallyDrop::new(self).to_glib_none().0
297 }
298}
299
300#[cfg(feature = "use_glib")]
301impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
302 type Storage = PhantomData<&'a Surface>;
303
304 #[inline]
305 fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
306 Stash(self.to_raw_none(), PhantomData)
307 }
308
309 #[inline]
310 fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
311 unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
312 }
313}
314
315#[cfg(feature = "use_glib")]
316impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
317 #[inline]
318 unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
319 Self::from_raw_none(ptr)
320 }
321}
322
323#[cfg(feature = "use_glib")]
324impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
325 #[inline]
326 unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> {
327 Self::from_raw_borrow(ptr)
328 }
329}
330
331#[cfg(feature = "use_glib")]
332impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
333 #[inline]
334 unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
335 Self::from_raw_full(ptr).unwrap()
336 }
337}
338
339#[cfg(feature = "use_glib")]
340gvalue_impl!(
341 Surface,
342 ffi::cairo_surface_t,
343 ffi::gobject::cairo_gobject_surface_get_type
344);
345
346impl Clone for Surface {
347 #[inline]
348 fn clone(&self) -> Surface {
349 unsafe { Self::from_raw_none(self.0.as_ptr()) }
350 }
351}
352
353impl Drop for Surface {
354 #[inline]
355 fn drop(&mut self) {
356 unsafe {
357 ffi::cairo_surface_destroy(self.0.as_ptr());
358 }
359 }
360}
361
362impl AsRef<Surface> for Surface {
363 #[inline]
364 fn as_ref(&self) -> &Surface {
365 self
366 }
367}
368
369impl Surface {
370 #[doc(alias = "cairo_surface_flush")]
371 pub fn flush(&self) {
372 unsafe {
373 ffi::cairo_surface_flush(self.0.as_ptr());
374 }
375 }
376
377 #[doc(alias = "cairo_surface_finish")]
378 pub fn finish(&self) {
379 unsafe {
380 ffi::cairo_surface_finish(self.0.as_ptr());
381 }
382 }
383
384 #[doc(alias = "cairo_surface_get_type")]
385 #[doc(alias = "get_type")]
386 pub fn type_(&self) -> SurfaceType {
387 unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0.as_ptr())) }
388 }
389}
390
391#[derive(Debug)]
392pub struct MappedImageSurface {
393 original_surface: Surface,
394 image_surface: ImageSurface,
395}
396
397impl Deref for MappedImageSurface {
398 type Target = ImageSurface;
399
400 #[inline]
401 fn deref(&self) -> &ImageSurface {
402 &self.image_surface
403 }
404}
405
406impl AsRef<ImageSurface> for MappedImageSurface {
407 #[inline]
408 fn as_ref(&self) -> &ImageSurface {
409 &self.image_surface
410 }
411}
412
413impl Drop for MappedImageSurface {
414 #[inline]
415 fn drop(&mut self) {
416 unsafe {
417 ffi::cairo_surface_unmap_image(
418 self.original_surface.to_raw_none(),
419 self.image_surface.to_raw_none(),
420 );
421 }
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use crate::{constants::MIME_TYPE_PNG, Format, ImageSurface};
428
429 #[test]
430 fn mime_data() {
431 let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
432 let data = surface.mime_data(MIME_TYPE_PNG);
433 assert!(data.is_none());
435
436 assert!(surface.set_mime_data(MIME_TYPE_PNG, [1u8, 10u8]).is_ok());
437 let data = surface.mime_data(MIME_TYPE_PNG).unwrap();
438 assert_eq!(data, &[1u8, 10u8]);
439 }
440}