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