1 // Take a look at the license at the top of the repository in the LICENSE file. 2 3 use libc::{c_ulong, c_void}; 4 use std::ffi::CString; 5 use std::fmt; 6 use std::ops::Deref; 7 use std::ptr; 8 use std::slice; 9 10 use crate::enums::{Content, Format, SurfaceType}; 11 use crate::error::Error; 12 use crate::utils::status_to_result; 13 #[cfg(feature = "use_glib")] 14 use glib::translate::*; 15 16 use crate::device::Device; 17 use crate::image_surface::ImageSurface; 18 use crate::rectangle::Rectangle; 19 use crate::rectangle_int::RectangleInt; 20 21 #[derive(Debug)] 22 #[doc(alias = "cairo_surface_t")] 23 pub struct Surface(ptr::NonNull<ffi::cairo_surface_t>); 24 25 impl Surface { from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface26 pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface { 27 assert!(!ptr.is_null()); 28 ffi::cairo_surface_reference(ptr); 29 Surface(ptr::NonNull::new_unchecked(ptr)) 30 } 31 from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface>32 pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> { 33 assert!(!ptr.is_null()); 34 crate::Borrowed::new(Surface(ptr::NonNull::new_unchecked(ptr))) 35 } 36 from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Error>37 pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Error> { 38 assert!(!ptr.is_null()); 39 let status = ffi::cairo_surface_status(ptr); 40 status_to_result(status)?; 41 Ok(Surface(ptr::NonNull::new_unchecked(ptr))) 42 } 43 to_raw_none(&self) -> *mut ffi::cairo_surface_t44 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")] create_similar( &self, content: Content, width: i32, height: i32, ) -> Result<Surface, Error>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")] create_for_rectangle(&self, bounds: Rectangle) -> Result<Surface, Error>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")] mime_data(&self, mime_type: &str) -> Option<Vec<u8>>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")] mime_data_raw(&self, mime_type: &str) -> Option<&[u8]>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")] set_mime_data<T: AsRef<[u8]> + 'static>( &self, mime_type: &str, slice: T, ) -> Result<(), Error>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")] supports_mime_type(&self, mime_type: &str) -> bool155 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")] device(&self) -> Option<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_set_device_offset")] set_device_offset(&self, x_offset: f64, y_offset: f64)176 pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) { 177 unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) } 178 } 179 180 #[doc(alias = "cairo_surface_get_device_offset")] 181 #[doc(alias = "get_device_offset")] device_offset(&self) -> (f64, f64)182 pub fn device_offset(&self) -> (f64, f64) { 183 let mut x_offset = 0.0f64; 184 let mut y_offset = 0.0f64; 185 unsafe { 186 ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset); 187 } 188 (x_offset, y_offset) 189 } 190 191 #[doc(alias = "cairo_surface_set_device_scale")] set_device_scale(&self, x_scale: f64, y_scale: f64)192 pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) { 193 unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) } 194 } 195 196 #[doc(alias = "cairo_surface_get_device_scale")] 197 #[doc(alias = "get_device_scale")] device_scale(&self) -> (f64, f64)198 pub fn device_scale(&self) -> (f64, f64) { 199 let mut x_scale = 0.0f64; 200 let mut y_scale = 0.0f64; 201 unsafe { 202 ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale); 203 } 204 (x_scale, y_scale) 205 } 206 207 #[doc(alias = "cairo_surface_set_fallback_resolution")] set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64)208 pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) { 209 unsafe { 210 ffi::cairo_surface_set_fallback_resolution( 211 self.to_raw_none(), 212 x_pixels_per_inch, 213 y_pixels_per_inch, 214 ) 215 } 216 } 217 218 #[doc(alias = "cairo_surface_get_fallback_resolution")] 219 #[doc(alias = "get_fallback_resolution")] fallback_resolution(&self) -> (f64, f64)220 pub fn fallback_resolution(&self) -> (f64, f64) { 221 let mut x_pixels_per_inch = 0.0f64; 222 let mut y_pixels_per_inch = 0.0f64; 223 unsafe { 224 ffi::cairo_surface_get_fallback_resolution( 225 self.to_raw_none(), 226 &mut x_pixels_per_inch, 227 &mut y_pixels_per_inch, 228 ); 229 } 230 (x_pixels_per_inch, y_pixels_per_inch) 231 } 232 233 #[doc(alias = "cairo_surface_create_similar_image")] create_similar_image( &self, format: Format, width: i32, height: i32, ) -> Result<Surface, Error>234 pub fn create_similar_image( 235 &self, 236 format: Format, 237 width: i32, 238 height: i32, 239 ) -> Result<Surface, Error> { 240 unsafe { 241 Self::from_raw_full(ffi::cairo_surface_create_similar_image( 242 self.to_raw_none(), 243 format.into(), 244 width, 245 height, 246 )) 247 } 248 } 249 250 #[doc(alias = "cairo_surface_map_to_image")] map_to_image(&self, extents: Option<RectangleInt>) -> Result<MappedImageSurface, Error>251 pub fn map_to_image(&self, extents: Option<RectangleInt>) -> Result<MappedImageSurface, Error> { 252 unsafe { 253 ImageSurface::from_raw_none(match extents { 254 Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()), 255 None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()), 256 }) 257 .map(|s| MappedImageSurface { 258 original_surface: self.clone(), 259 image_surface: s, 260 }) 261 } 262 } 263 264 #[doc(alias = "cairo_surface_mark_dirty")] mark_dirty(&self)265 pub fn mark_dirty(&self) { 266 unsafe { ffi::cairo_surface_mark_dirty(self.to_raw_none()) } 267 } 268 269 #[doc(alias = "cairo_surface_mark_dirty_rectangle")] mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32)270 pub fn mark_dirty_rectangle(&self, x: i32, y: i32, width: i32, height: i32) { 271 unsafe { ffi::cairo_surface_mark_dirty_rectangle(self.to_raw_none(), x, y, width, height) } 272 } 273 274 #[doc(alias = "cairo_surface_status")] status(&self) -> Result<(), Error>275 pub fn status(&self) -> Result<(), Error> { 276 let status = unsafe { ffi::cairo_surface_status(self.to_raw_none()) }; 277 status_to_result(status) 278 } 279 280 user_data_methods! { 281 ffi::cairo_surface_get_user_data, 282 ffi::cairo_surface_set_user_data, 283 } 284 } 285 286 #[cfg(feature = "use_glib")] 287 impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface { 288 type Storage = &'a Surface; 289 290 #[inline] to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self>291 fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> { 292 Stash(self.to_raw_none(), self) 293 } 294 295 #[inline] to_glib_full(&self) -> *mut ffi::cairo_surface_t296 fn to_glib_full(&self) -> *mut ffi::cairo_surface_t { 297 unsafe { ffi::cairo_surface_reference(self.to_raw_none()) } 298 } 299 } 300 301 #[cfg(feature = "use_glib")] 302 impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface { 303 #[inline] from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface304 unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface { 305 Self::from_raw_none(ptr) 306 } 307 } 308 309 #[cfg(feature = "use_glib")] 310 impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface { 311 #[inline] from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface>312 unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> crate::Borrowed<Surface> { 313 Self::from_raw_borrow(ptr) 314 } 315 } 316 317 #[cfg(feature = "use_glib")] 318 impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface { 319 #[inline] from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface320 unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface { 321 Self::from_raw_full(ptr).unwrap() 322 } 323 } 324 325 #[cfg(feature = "use_glib")] 326 gvalue_impl!( 327 Surface, 328 ffi::cairo_surface_t, 329 ffi::gobject::cairo_gobject_surface_get_type 330 ); 331 332 impl Clone for Surface { clone(&self) -> Surface333 fn clone(&self) -> Surface { 334 unsafe { Self::from_raw_none(self.0.as_ptr()) } 335 } 336 } 337 338 impl Drop for Surface { drop(&mut self)339 fn drop(&mut self) { 340 unsafe { 341 ffi::cairo_surface_destroy(self.0.as_ptr()); 342 } 343 } 344 } 345 346 impl fmt::Display for Surface { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result347 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 348 write!(f, "Surface") 349 } 350 } 351 352 impl Surface { 353 #[doc(alias = "cairo_surface_flush")] flush(&self)354 pub fn flush(&self) { 355 unsafe { 356 ffi::cairo_surface_flush(self.0.as_ptr()); 357 } 358 } 359 360 #[doc(alias = "cairo_surface_finish")] finish(&self)361 pub fn finish(&self) { 362 unsafe { 363 ffi::cairo_surface_finish(self.0.as_ptr()); 364 } 365 } 366 367 #[doc(alias = "cairo_surface_get_type")] 368 #[doc(alias = "get_type")] type_(&self) -> SurfaceType369 pub fn type_(&self) -> SurfaceType { 370 unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0.as_ptr())) } 371 } 372 } 373 374 #[derive(Debug)] 375 pub struct MappedImageSurface { 376 original_surface: Surface, 377 image_surface: ImageSurface, 378 } 379 380 impl Deref for MappedImageSurface { 381 type Target = ImageSurface; 382 deref(&self) -> &ImageSurface383 fn deref(&self) -> &ImageSurface { 384 &self.image_surface 385 } 386 } 387 388 impl Drop for MappedImageSurface { drop(&mut self)389 fn drop(&mut self) { 390 unsafe { 391 ffi::cairo_surface_unmap_image( 392 self.original_surface.to_raw_none(), 393 self.image_surface.to_raw_none(), 394 ); 395 } 396 } 397 } 398 399 impl fmt::Display for MappedImageSurface { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 401 write!(f, "MappedImageSurface") 402 } 403 } 404 405 #[cfg(test)] 406 mod tests { 407 use crate::constants::MIME_TYPE_PNG; 408 use crate::Format; 409 use crate::ImageSurface; 410 411 #[test] mime_data()412 fn mime_data() { 413 let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap(); 414 let data = surface.mime_data(MIME_TYPE_PNG); 415 /* Initially the data for any mime type has to be none */ 416 assert!(data.is_none()); 417 418 assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok()); 419 let data = surface.mime_data(MIME_TYPE_PNG).unwrap(); 420 assert_eq!(data, &[1u8, 10u8]); 421 } 422 } 423