1 // Copyright 2013-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 5 use gdk_pixbuf_sys; 6 use gio; 7 use gio_sys; 8 use glib::object::IsA; 9 use glib::translate::*; 10 use glib::Error; 11 use glib_sys; 12 use gobject_sys; 13 use libc::{c_uchar, c_void}; 14 use std::mem; 15 use std::path::Path; 16 use std::pin::Pin; 17 use std::ptr; 18 use std::slice; 19 20 use std::future::Future; 21 22 use {Colorspace, Pixbuf, PixbufFormat}; 23 24 impl Pixbuf { new_from_mut_slice<T: AsMut<[u8]>>( data: T, colorspace: Colorspace, has_alpha: bool, bits_per_sample: i32, width: i32, height: i32, row_stride: i32, ) -> Pixbuf25 pub fn new_from_mut_slice<T: AsMut<[u8]>>( 26 data: T, 27 colorspace: Colorspace, 28 has_alpha: bool, 29 bits_per_sample: i32, 30 width: i32, 31 height: i32, 32 row_stride: i32, 33 ) -> Pixbuf { 34 unsafe extern "C" fn destroy<T: AsMut<[u8]>>(_: *mut c_uchar, data: *mut c_void) { 35 let _data: Box<T> = Box::from_raw(data as *mut T); // the data will be destroyed now 36 } 37 38 assert!(bits_per_sample == 8); 39 let n_channels = if has_alpha { 4 } else { 3 }; 40 let last_row_len = width * ((n_channels * bits_per_sample + 7) / 8); 41 42 let mut data: Box<T> = Box::new(data); 43 44 let ptr = { 45 let data: &mut [u8] = (*data).as_mut(); 46 assert!(data.len() == ((height - 1) * row_stride + last_row_len) as usize); 47 data.as_mut_ptr() 48 }; 49 50 unsafe { 51 from_glib_full(gdk_pixbuf_sys::gdk_pixbuf_new_from_data( 52 ptr, 53 colorspace.to_glib(), 54 has_alpha.to_glib(), 55 bits_per_sample, 56 width, 57 height, 58 row_stride, 59 Some(destroy::<T>), 60 Box::into_raw(data) as *mut _, 61 )) 62 } 63 } 64 new_from_file<T: AsRef<Path>>(filename: T) -> Result<Pixbuf, Error>65 pub fn new_from_file<T: AsRef<Path>>(filename: T) -> Result<Pixbuf, Error> { 66 #[cfg(not(windows))] 67 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file; 68 #[cfg(windows)] 69 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file_utf8 as gdk_pixbuf_new_from_file; 70 71 unsafe { 72 let mut error = ptr::null_mut(); 73 let ptr = gdk_pixbuf_new_from_file(filename.as_ref().to_glib_none().0, &mut error); 74 if error.is_null() { 75 Ok(from_glib_full(ptr)) 76 } else { 77 Err(from_glib_full(error)) 78 } 79 } 80 } 81 new_from_file_at_size<T: AsRef<Path>>( filename: T, width: i32, height: i32, ) -> Result<Pixbuf, Error>82 pub fn new_from_file_at_size<T: AsRef<Path>>( 83 filename: T, 84 width: i32, 85 height: i32, 86 ) -> Result<Pixbuf, Error> { 87 #[cfg(not(windows))] 88 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file_at_size; 89 #[cfg(windows)] 90 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file_at_size_utf8 as gdk_pixbuf_new_from_file_at_size; 91 92 unsafe { 93 let mut error = ptr::null_mut(); 94 let ptr = gdk_pixbuf_new_from_file_at_size( 95 filename.as_ref().to_glib_none().0, 96 width, 97 height, 98 &mut error, 99 ); 100 if error.is_null() { 101 Ok(from_glib_full(ptr)) 102 } else { 103 Err(from_glib_full(error)) 104 } 105 } 106 } 107 new_from_file_at_scale<T: AsRef<Path>>( filename: T, width: i32, height: i32, preserve_aspect_ratio: bool, ) -> Result<Pixbuf, Error>108 pub fn new_from_file_at_scale<T: AsRef<Path>>( 109 filename: T, 110 width: i32, 111 height: i32, 112 preserve_aspect_ratio: bool, 113 ) -> Result<Pixbuf, Error> { 114 #[cfg(not(windows))] 115 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file_at_scale; 116 #[cfg(windows)] 117 use gdk_pixbuf_sys::gdk_pixbuf_new_from_file_at_scale_utf8 as gdk_pixbuf_new_from_file_at_scale; 118 119 unsafe { 120 let mut error = ptr::null_mut(); 121 let ptr = gdk_pixbuf_new_from_file_at_scale( 122 filename.as_ref().to_glib_none().0, 123 width, 124 height, 125 preserve_aspect_ratio.to_glib(), 126 &mut error, 127 ); 128 if error.is_null() { 129 Ok(from_glib_full(ptr)) 130 } else { 131 Err(from_glib_full(error)) 132 } 133 } 134 } 135 new_from_stream_async< 'a, P: IsA<gio::InputStream>, Q: IsA<gio::Cancellable>, R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, >( stream: &P, cancellable: Option<&Q>, callback: R, )136 pub fn new_from_stream_async< 137 'a, 138 P: IsA<gio::InputStream>, 139 Q: IsA<gio::Cancellable>, 140 R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, 141 >( 142 stream: &P, 143 cancellable: Option<&Q>, 144 callback: R, 145 ) { 146 let cancellable = cancellable.map(|p| p.as_ref()); 147 let user_data: Box<R> = Box::new(callback); 148 unsafe extern "C" fn new_from_stream_async_trampoline< 149 R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, 150 >( 151 _source_object: *mut gobject_sys::GObject, 152 res: *mut gio_sys::GAsyncResult, 153 user_data: glib_sys::gpointer, 154 ) { 155 let mut error = ptr::null_mut(); 156 let ptr = gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_finish(res, &mut error); 157 let result = if error.is_null() { 158 Ok(from_glib_full(ptr)) 159 } else { 160 Err(from_glib_full(error)) 161 }; 162 let callback: Box<R> = Box::from_raw(user_data as *mut _); 163 callback(result); 164 } 165 let callback = new_from_stream_async_trampoline::<R>; 166 unsafe { 167 gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_async( 168 stream.as_ref().to_glib_none().0, 169 cancellable.to_glib_none().0, 170 Some(callback), 171 Box::into_raw(user_data) as *mut _, 172 ); 173 } 174 } 175 new_from_stream_async_future<P: IsA<gio::InputStream> + Clone + 'static>( stream: &P, ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>>176 pub fn new_from_stream_async_future<P: IsA<gio::InputStream> + Clone + 'static>( 177 stream: &P, 178 ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>> { 179 let stream = stream.clone(); 180 Box::pin(gio::GioFuture::new(&(), move |_obj, send| { 181 let cancellable = gio::Cancellable::new(); 182 Self::new_from_stream_async(&stream, Some(&cancellable), move |res| { 183 send.resolve(res); 184 }); 185 186 cancellable 187 })) 188 } 189 new_from_stream_at_scale_async< 'a, P: IsA<gio::InputStream>, Q: IsA<gio::Cancellable>, R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, >( stream: &P, width: i32, height: i32, preserve_aspect_ratio: bool, cancellable: Option<&Q>, callback: R, )190 pub fn new_from_stream_at_scale_async< 191 'a, 192 P: IsA<gio::InputStream>, 193 Q: IsA<gio::Cancellable>, 194 R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, 195 >( 196 stream: &P, 197 width: i32, 198 height: i32, 199 preserve_aspect_ratio: bool, 200 cancellable: Option<&Q>, 201 callback: R, 202 ) { 203 let cancellable = cancellable.map(|p| p.as_ref()); 204 let user_data: Box<R> = Box::new(callback); 205 unsafe extern "C" fn new_from_stream_at_scale_async_trampoline< 206 R: FnOnce(Result<Pixbuf, Error>) + Send + 'static, 207 >( 208 _source_object: *mut gobject_sys::GObject, 209 res: *mut gio_sys::GAsyncResult, 210 user_data: glib_sys::gpointer, 211 ) { 212 let mut error = ptr::null_mut(); 213 let ptr = gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_finish(res, &mut error); 214 let result = if error.is_null() { 215 Ok(from_glib_full(ptr)) 216 } else { 217 Err(from_glib_full(error)) 218 }; 219 let callback: Box<R> = Box::from_raw(user_data as *mut _); 220 callback(result); 221 } 222 let callback = new_from_stream_at_scale_async_trampoline::<R>; 223 unsafe { 224 gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_at_scale_async( 225 stream.as_ref().to_glib_none().0, 226 width, 227 height, 228 preserve_aspect_ratio.to_glib(), 229 cancellable.to_glib_none().0, 230 Some(callback), 231 Box::into_raw(user_data) as *mut _, 232 ); 233 } 234 } 235 new_from_stream_at_scale_async_future<P: IsA<gio::InputStream> + Clone + 'static>( stream: &P, width: i32, height: i32, preserve_aspect_ratio: bool, ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>>236 pub fn new_from_stream_at_scale_async_future<P: IsA<gio::InputStream> + Clone + 'static>( 237 stream: &P, 238 width: i32, 239 height: i32, 240 preserve_aspect_ratio: bool, 241 ) -> Pin<Box<dyn Future<Output = Result<Pixbuf, Error>> + 'static>> { 242 let stream = stream.clone(); 243 Box::pin(gio::GioFuture::new(&(), move |_obj, send| { 244 let cancellable = gio::Cancellable::new(); 245 Self::new_from_stream_at_scale_async( 246 &stream, 247 width, 248 height, 249 preserve_aspect_ratio, 250 Some(&cancellable), 251 move |res| { 252 send.resolve(res); 253 }, 254 ); 255 256 cancellable 257 })) 258 } 259 260 #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))] get_pixels(&self) -> &mut [u8]261 pub unsafe fn get_pixels(&self) -> &mut [u8] { 262 let mut len = 0; 263 let ptr = 264 gdk_pixbuf_sys::gdk_pixbuf_get_pixels_with_length(self.to_glib_none().0, &mut len); 265 slice::from_raw_parts_mut(ptr, len as usize) 266 } 267 put_pixel(&self, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8)268 pub fn put_pixel(&self, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) { 269 unsafe { 270 let n_channels = self.get_n_channels(); 271 assert!(n_channels == 3 || n_channels == 4); 272 let rowstride = self.get_rowstride(); 273 let pixels = self.get_pixels(); 274 let pos = (y * rowstride + x * n_channels) as usize; 275 276 pixels[pos] = red; 277 pixels[pos + 1] = green; 278 pixels[pos + 2] = blue; 279 if n_channels == 4 { 280 pixels[pos + 3] = alpha; 281 } 282 } 283 } 284 get_file_info<T: AsRef<Path>>(filename: T) -> Option<(PixbufFormat, i32, i32)>285 pub fn get_file_info<T: AsRef<Path>>(filename: T) -> Option<(PixbufFormat, i32, i32)> { 286 unsafe { 287 let mut width = mem::MaybeUninit::uninit(); 288 let mut height = mem::MaybeUninit::uninit(); 289 let ret = gdk_pixbuf_sys::gdk_pixbuf_get_file_info( 290 filename.as_ref().to_glib_none().0, 291 width.as_mut_ptr(), 292 height.as_mut_ptr(), 293 ); 294 if !ret.is_null() { 295 Some(( 296 from_glib_none(ret), 297 width.assume_init(), 298 height.assume_init(), 299 )) 300 } else { 301 None 302 } 303 } 304 } 305 306 #[cfg(any(feature = "v2_32", feature = "dox"))] get_file_info_async< P: IsA<gio::Cancellable>, Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + Send + 'static, T: AsRef<Path>, >( filename: T, cancellable: Option<&P>, callback: Q, )307 pub fn get_file_info_async< 308 P: IsA<gio::Cancellable>, 309 Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + Send + 'static, 310 T: AsRef<Path>, 311 >( 312 filename: T, 313 cancellable: Option<&P>, 314 callback: Q, 315 ) { 316 let cancellable = cancellable.map(|p| p.as_ref()); 317 let user_data: Box<Q> = Box::new(callback); 318 unsafe extern "C" fn get_file_info_async_trampoline< 319 Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + Send + 'static, 320 >( 321 _source_object: *mut gobject_sys::GObject, 322 res: *mut gio_sys::GAsyncResult, 323 user_data: glib_sys::gpointer, 324 ) { 325 let mut error = ptr::null_mut(); 326 let mut width = mem::MaybeUninit::uninit(); 327 let mut height = mem::MaybeUninit::uninit(); 328 let ret = gdk_pixbuf_sys::gdk_pixbuf_get_file_info_finish( 329 res, 330 width.as_mut_ptr(), 331 height.as_mut_ptr(), 332 &mut error, 333 ); 334 let result = if !error.is_null() { 335 Err(from_glib_full(error)) 336 } else if ret.is_null() { 337 Ok(None) 338 } else { 339 Ok(Some(( 340 from_glib_none(ret), 341 width.assume_init(), 342 height.assume_init(), 343 ))) 344 }; 345 let callback: Box<Q> = Box::from_raw(user_data as *mut _); 346 callback(result); 347 } 348 let callback = get_file_info_async_trampoline::<Q>; 349 unsafe { 350 gdk_pixbuf_sys::gdk_pixbuf_get_file_info_async( 351 filename.as_ref().to_glib_none().0, 352 cancellable.to_glib_none().0, 353 Some(callback), 354 Box::into_raw(user_data) as *mut _, 355 ); 356 } 357 } 358 359 #[cfg(any(feature = "v2_32", feature = "dox"))] get_file_info_async_future<T: AsRef<Path> + Clone + 'static>( filename: T, ) -> Pin<Box<dyn Future<Output = Result<Option<(PixbufFormat, i32, i32)>, Error>> + 'static>>360 pub fn get_file_info_async_future<T: AsRef<Path> + Clone + 'static>( 361 filename: T, 362 ) -> Pin<Box<dyn Future<Output = Result<Option<(PixbufFormat, i32, i32)>, Error>> + 'static>> 363 { 364 Box::pin(gio::GioFuture::new(&(), move |_obj, send| { 365 let cancellable = gio::Cancellable::new(); 366 Self::get_file_info_async(filename, Some(&cancellable), move |res| { 367 send.resolve(res); 368 }); 369 370 cancellable 371 })) 372 } 373 save_to_bufferv(&self, type_: &str, options: &[(&str, &str)]) -> Result<Vec<u8>, Error>374 pub fn save_to_bufferv(&self, type_: &str, options: &[(&str, &str)]) -> Result<Vec<u8>, Error> { 375 unsafe { 376 let mut buffer = ptr::null_mut(); 377 let mut buffer_size = mem::MaybeUninit::uninit(); 378 let mut error = ptr::null_mut(); 379 let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect(); 380 let option_values: Vec<&str> = options.iter().map(|o| o.1).collect(); 381 let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_bufferv( 382 self.to_glib_none().0, 383 &mut buffer, 384 buffer_size.as_mut_ptr(), 385 type_.to_glib_none().0, 386 option_keys.to_glib_none().0, 387 option_values.to_glib_none().0, 388 &mut error, 389 ); 390 if error.is_null() { 391 Ok(FromGlibContainer::from_glib_full_num( 392 buffer, 393 buffer_size.assume_init() as usize, 394 )) 395 } else { 396 Err(from_glib_full(error)) 397 } 398 } 399 } 400 401 #[cfg(any(feature = "v2_36", feature = "dox"))] save_to_streamv<'a, P: IsA<gio::OutputStream>, Q: IsA<gio::Cancellable>>( &self, stream: &P, type_: &str, options: &[(&str, &str)], cancellable: Option<&Q>, ) -> Result<(), Error>402 pub fn save_to_streamv<'a, P: IsA<gio::OutputStream>, Q: IsA<gio::Cancellable>>( 403 &self, 404 stream: &P, 405 type_: &str, 406 options: &[(&str, &str)], 407 cancellable: Option<&Q>, 408 ) -> Result<(), Error> { 409 let cancellable = cancellable.map(|p| p.as_ref()); 410 unsafe { 411 let mut error = ptr::null_mut(); 412 let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect(); 413 let option_values: Vec<&str> = options.iter().map(|o| o.1).collect(); 414 let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_streamv( 415 self.to_glib_none().0, 416 stream.as_ref().to_glib_none().0, 417 type_.to_glib_none().0, 418 option_keys.to_glib_none().0, 419 option_values.to_glib_none().0, 420 cancellable.to_glib_none().0, 421 &mut error, 422 ); 423 if error.is_null() { 424 Ok(()) 425 } else { 426 Err(from_glib_full(error)) 427 } 428 } 429 } 430 431 #[cfg(any(feature = "v2_36", feature = "dox"))] save_to_streamv_async< 'a, P: IsA<gio::OutputStream>, Q: IsA<gio::Cancellable>, R: FnOnce(Result<(), Error>) + Send + 'static, >( &self, stream: &P, type_: &str, options: &[(&str, &str)], cancellable: Option<&Q>, callback: R, )432 pub fn save_to_streamv_async< 433 'a, 434 P: IsA<gio::OutputStream>, 435 Q: IsA<gio::Cancellable>, 436 R: FnOnce(Result<(), Error>) + Send + 'static, 437 >( 438 &self, 439 stream: &P, 440 type_: &str, 441 options: &[(&str, &str)], 442 cancellable: Option<&Q>, 443 callback: R, 444 ) { 445 let cancellable = cancellable.map(|p| p.as_ref()); 446 let user_data: Box<R> = Box::new(callback); 447 unsafe extern "C" fn save_to_streamv_async_trampoline< 448 R: FnOnce(Result<(), Error>) + Send + 'static, 449 >( 450 _source_object: *mut gobject_sys::GObject, 451 res: *mut gio_sys::GAsyncResult, 452 user_data: glib_sys::gpointer, 453 ) { 454 let mut error = ptr::null_mut(); 455 let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_stream_finish(res, &mut error); 456 let result = if error.is_null() { 457 Ok(()) 458 } else { 459 Err(from_glib_full(error)) 460 }; 461 let callback: Box<R> = Box::from_raw(user_data as *mut _); 462 callback(result); 463 } 464 let callback = save_to_streamv_async_trampoline::<R>; 465 unsafe { 466 let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect(); 467 let option_values: Vec<&str> = options.iter().map(|o| o.1).collect(); 468 gdk_pixbuf_sys::gdk_pixbuf_save_to_streamv_async( 469 self.to_glib_none().0, 470 stream.as_ref().to_glib_none().0, 471 type_.to_glib_none().0, 472 option_keys.to_glib_none().0, 473 option_values.to_glib_none().0, 474 cancellable.to_glib_none().0, 475 Some(callback), 476 Box::into_raw(user_data) as *mut _, 477 ); 478 } 479 } 480 481 #[cfg(any(feature = "v2_36", feature = "dox"))] save_to_streamv_async_future<P: IsA<gio::OutputStream> + Clone + 'static>( &self, stream: &P, type_: &str, options: &[(&str, &str)], ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>>482 pub fn save_to_streamv_async_future<P: IsA<gio::OutputStream> + Clone + 'static>( 483 &self, 484 stream: &P, 485 type_: &str, 486 options: &[(&str, &str)], 487 ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>> { 488 let stream = stream.clone(); 489 let type_ = String::from(type_); 490 let options = options 491 .iter() 492 .map(|&(k, v)| (String::from(k), String::from(v))) 493 .collect::<Vec<(String, String)>>(); 494 Box::pin(gio::GioFuture::new(self, move |obj, send| { 495 let cancellable = gio::Cancellable::new(); 496 let options = options 497 .iter() 498 .map(|&(ref k, ref v)| (k.as_str(), v.as_str())) 499 .collect::<Vec<(&str, &str)>>(); 500 501 obj.save_to_streamv_async( 502 &stream, 503 &type_, 504 options.as_slice(), 505 Some(&cancellable), 506 move |res| { 507 send.resolve(res); 508 }, 509 ); 510 511 cancellable 512 })) 513 } 514 savev<T: AsRef<Path>>( &self, filename: T, type_: &str, options: &[(&str, &str)], ) -> Result<(), Error>515 pub fn savev<T: AsRef<Path>>( 516 &self, 517 filename: T, 518 type_: &str, 519 options: &[(&str, &str)], 520 ) -> Result<(), Error> { 521 unsafe { 522 let mut error = ptr::null_mut(); 523 let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect(); 524 let option_values: Vec<&str> = options.iter().map(|o| o.1).collect(); 525 let _ = gdk_pixbuf_sys::gdk_pixbuf_savev( 526 self.to_glib_none().0, 527 filename.as_ref().to_glib_none().0, 528 type_.to_glib_none().0, 529 option_keys.to_glib_none().0, 530 option_values.to_glib_none().0, 531 &mut error, 532 ); 533 if error.is_null() { 534 Ok(()) 535 } else { 536 Err(from_glib_full(error)) 537 } 538 } 539 } 540 } 541