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 mut data_ptr: *mut u8 = ptr::null_mut();
82 let mut length: c_ulong = 0;
83 #[allow(clippy::unnecessary_mut_passed)]
85 unsafe {
86 let mime_type = CString::new(mime_type).unwrap();
87 ffi::cairo_surface_get_mime_data(
88 self.to_raw_none(),
89 mime_type.as_ptr(),
90 &mut data_ptr,
91 &mut length,
92 );
93 if !data_ptr.is_null() && length != 0 {
94 Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec())
95 } else {
96 None
97 }
98 }
99 }
100
101 #[doc(alias = "cairo_surface_get_mime_data")]
102 #[doc(alias = "get_mime_data_raw")]
103 pub unsafe fn mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
104 let mut data_ptr: *mut u8 = ptr::null_mut();
105 let mut length: c_ulong = 0;
106 let mime_type = CString::new(mime_type).unwrap();
107 #[allow(clippy::unnecessary_mut_passed)]
109 {
110 ffi::cairo_surface_get_mime_data(
111 self.to_raw_none(),
112 mime_type.as_ptr(),
113 &mut data_ptr,
114 &mut length,
115 );
116 }
117 if !data_ptr.is_null() && length != 0 {
118 Some(slice::from_raw_parts(
119 data_ptr as *const u8,
120 length as usize,
121 ))
122 } else {
123 None
124 }
125 }
126
127 #[doc(alias = "cairo_surface_set_mime_data")]
128 pub fn set_mime_data<T: AsRef<[u8]> + 'static>(
129 &self,
130 mime_type: &str,
131 slice: T,
132 ) -> Result<(), Error> {
133 let b = Box::new(slice);
134 let (size, data) = {
135 let slice = (*b).as_ref();
136 (slice.len(), slice.as_ptr())
137 };
138
139 let user_data = Box::into_raw(b);
140
141 unsafe extern "C" fn unbox<T>(data: *mut c_void) {
142 let data: Box<T> = Box::from_raw(data as *mut T);
143 drop(data);
144 }
145
146 let status = unsafe {
147 let mime_type = CString::new(mime_type).unwrap();
148 ffi::cairo_surface_set_mime_data(
149 self.to_raw_none(),
150 mime_type.as_ptr(),
151 data,
152 size as c_ulong,
153 Some(unbox::<T>),
154 user_data as *mut _,
155 )
156 };
157 status_to_result(status)
158 }
159
160 #[doc(alias = "cairo_surface_supports_mime_type")]
161 pub fn supports_mime_type(&self, mime_type: &str) -> bool {
162 unsafe {
163 let mime_type = CString::new(mime_type).unwrap();
164 ffi::cairo_surface_supports_mime_type(self.0.as_ptr(), mime_type.as_ptr()).as_bool()
165 }
166 }
167
168 #[doc(alias = "cairo_surface_get_device")]
169 #[doc(alias = "get_device")]
170 pub fn device(&self) -> Option<Device> {
171 unsafe {
172 let device = ffi::cairo_surface_get_device(self.to_raw_none());
173 if device.is_null() {
174 None
175 } else {
176 Some(Device::from_raw_none(device))
177 }
178 }
179 }
180
181 #[doc(alias = "cairo_surface_get_content")]
182 pub fn content(&self) -> Content {
183 unsafe { ffi::cairo_surface_get_content(self.to_raw_none()) }.into()
184 }
185
186 #[doc(alias = "cairo_surface_set_device_offset")]
187 pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
188 unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
189 }
190
191 #[doc(alias = "cairo_surface_get_device_offset")]
192 #[doc(alias = "get_device_offset")]
193 pub fn device_offset(&self) -> (f64, f64) {
194 let mut x_offset = 0.0f64;
195 let mut y_offset = 0.0f64;
196 unsafe {
197 ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
198 }
199 (x_offset, y_offset)
200 }
201
202 #[doc(alias = "cairo_surface_set_device_scale")]
203 pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
204 unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
205 }
206
207 #[doc(alias = "cairo_surface_get_device_scale")]
208 #[doc(alias = "get_device_scale")]
209 pub fn device_scale(&self) -> (f64, f64) {
210 let mut x_scale = 0.0f64;
211 let mut y_scale = 0.0f64;
212 unsafe {
213 ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
214 }
215 (x_scale, y_scale)
216 }
217
218 #[doc(alias = "cairo_surface_set_fallback_resolution")]
219 pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
220 unsafe {
221 ffi::cairo_surface_set_fallback_resolution(
222 self.to_raw_none(),
223 x_pixels_per_inch,
224 y_pixels_per_inch,
225 )
226 }
227 }
228
229 #[doc(alias = "cairo_surface_get_fallback_resolution")]
230 #[doc(alias = "get_fallback_resolution")]
231 pub fn fallback_resolution(&self) -> (f64, f64) {
232 let mut x_pixels_per_inch = 0.0f64;
233 let mut y_pixels_per_inch = 0.0f64;
234 unsafe {
235 ffi::cairo_surface_get_fallback_resolution(
236 self.to_raw_none(),
237 &mut x_pixels_per_inch,
238 &mut y_pixels_per_inch,
239 );
240 }
241 (x_pixels_per_inch, y_pixels_per_inch)
242 }
243
244 #[doc(alias = "cairo_surface_create_similar_image")]
245 pub fn create_similar_image(
246 &self,
247 format: Format,
248 width: i32,
249 height: i32,
250 ) -> Result<ImageSurface, Error> {
251 unsafe {
252 ImageSurface::from_raw_full(ffi::cairo_surface_create_similar_image(
253 self.to_raw_none(),
254 format.into(),
255 width,
256 height,
257 ))
258 }
259 }
260
261 #[doc(alias = "cairo_surface_map_to_image")]
262 pub fn map_to_image(&self, extents: Option<RectangleInt>) -> Result<MappedImageSurface, Error> {
263 unsafe {
264 ImageSurface::from_raw_none(match extents {
265 Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
266 None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()),
267 })
268 .map(|s| MappedImageSurface {
269 original_surface: self.clone(),
270 image_surface: s,
271 })
272 }
273 }
274
275 #[doc(alias = "cairo_surface_mark_dirty")]
276 pub fn mark_dirty(&self) {
277 unsafe { ffi::cairo_surface_mark_dirty(self.to_raw_none()) }
278 }
279
280 #[doc(alias = "cairo_surface_mark_dirty_rectangle")]
281 pub fn mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
282 unsafe { ffi::cairo_surface_mark_dirty_rectangle(self.to_raw_none(), x, y, width, height) }
283 }
284
285 #[doc(alias = "cairo_surface_status")]
286 #[inline]
287 pub fn status(&self) -> Result<(), Error> {
288 let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) };
289 status_to_result(status)
290 }
291
292 user_data_methods! {
293 ffi::cairo_surface_get_user_data,
294 ffi::cairo_surface_set_user_data,
295 }
296}
297
298#[cfg(feature = "use_glib")]
299impl IntoGlibPtr<*mut ffi::cairo_surface_t> for Surface {
300 #[inline]
301 fn into_glib_ptr(self) -> *mut ffi::cairo_surface_t {
302 std::mem::ManuallyDrop::new(self).to_glib_none().0
303 }
304}
305
306#[cfg(feature = "use_glib")]
307impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
308 type Storage = PhantomData<&'a Surface>;
309
310 #[inline]
311 fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
312 Stash(self.to_raw_none(), PhantomData)
313 }
314
315 #[inline]
316 fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
317 unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
318 }
319}
320
321#[cfg(feature = "use_glib")]
322impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
323 #[inline]
324 unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
325 Self::from_raw_none(ptr)
326 }
327}
328
329#[cfg(feature = "use_glib")]
330impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
331 #[inline]
332 unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> {
333 Self::from_raw_borrow(ptr)
334 }
335}
336
337#[cfg(feature = "use_glib")]
338impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
339 #[inline]
340 unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
341 Self::from_raw_full(ptr).unwrap()
342 }
343}
344
345#[cfg(feature = "use_glib")]
346gvalue_impl!(
347 Surface,
348 ffi::cairo_surface_t,
349 ffi::gobject::cairo_gobject_surface_get_type
350);
351
352impl Clone for Surface {
353 #[inline]
354 fn clone(&self) -> Surface {
355 unsafe { Self::from_raw_none(self.0.as_ptr()) }
356 }
357}
358
359impl Drop for Surface {
360 #[inline]
361 fn drop(&mut self) {
362 unsafe {
363 ffi::cairo_surface_destroy(self.0.as_ptr());
364 }
365 }
366}
367
368impl AsRef<Surface> for Surface {
369 #[inline]
370 fn as_ref(&self) -> &Surface {
371 self
372 }
373}
374
375impl Surface {
376 #[doc(alias = "cairo_surface_flush")]
377 pub fn flush(&self) {
378 unsafe {
379 ffi::cairo_surface_flush(self.0.as_ptr());
380 }
381 }
382
383 #[doc(alias = "cairo_surface_finish")]
384 pub fn finish(&self) {
385 unsafe {
386 ffi::cairo_surface_finish(self.0.as_ptr());
387 }
388 }
389
390 #[doc(alias = "cairo_surface_get_type")]
391 #[doc(alias = "get_type")]
392 pub fn type_(&self) -> SurfaceType {
393 unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0.as_ptr())) }
394 }
395}
396
397#[derive(Debug)]
398pub struct MappedImageSurface {
399 original_surface: Surface,
400 image_surface: ImageSurface,
401}
402
403impl Deref for MappedImageSurface {
404 type Target = ImageSurface;
405
406 #[inline]
407 fn deref(&self) -> &ImageSurface {
408 &self.image_surface
409 }
410}
411
412impl AsRef<ImageSurface> for MappedImageSurface {
413 #[inline]
414 fn as_ref(&self) -> &ImageSurface {
415 &self.image_surface
416 }
417}
418
419impl Drop for MappedImageSurface {
420 #[inline]
421 fn drop(&mut self) {
422 unsafe {
423 ffi::cairo_surface_unmap_image(
424 self.original_surface.to_raw_none(),
425 self.image_surface.to_raw_none(),
426 );
427 }
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use crate::{constants::MIME_TYPE_PNG, Format, ImageSurface};
434
435 #[test]
436 fn mime_data() {
437 let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
438 let data = surface.mime_data(MIME_TYPE_PNG);
439 assert!(data.is_none());
441
442 assert!(surface.set_mime_data(MIME_TYPE_PNG, [1u8, 10u8]).is_ok());
443 let data = surface.mime_data(MIME_TYPE_PNG).unwrap();
444 assert_eq!(data, &[1u8, 10u8]);
445 }
446}