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