gdk_pixbuf/
pixbuf.rs

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
5use gdk_pixbuf_sys;
6use gio;
7use gio_sys;
8use glib::object::IsA;
9use glib::translate::*;
10use glib::Error;
11use glib_sys;
12use gobject_sys;
13use libc::{c_uchar, c_void};
14use std::mem;
15use std::path::Path;
16use std::ptr;
17use std::slice;
18
19#[cfg(feature = "futures")]
20use futures::future::Future;
21
22use {Colorspace, Pixbuf, PixbufFormat};
23
24impl Pixbuf {
25    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
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
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
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
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
176    #[cfg(feature = "futures")]
177    pub fn new_from_stream_async_future<P: IsA<gio::InputStream> + Clone + 'static>(
178        stream: &P,
179    ) -> Box<dyn Future<Output = Result<Pixbuf, Error>> + std::marker::Unpin> {
180        use gio::GioFuture;
181
182        let stream = stream.clone();
183        GioFuture::new(&(), move |_obj, send| {
184            use fragile::Fragile;
185
186            let cancellable = gio::Cancellable::new();
187            let send = Fragile::new(send);
188            Self::new_from_stream_async(&stream, Some(&cancellable), move |res| {
189                let _ = send.into_inner().send(res);
190            });
191
192            cancellable
193        })
194    }
195
196    pub fn new_from_stream_at_scale_async<
197        'a,
198        P: IsA<gio::InputStream>,
199        Q: IsA<gio::Cancellable>,
200        R: FnOnce(Result<Pixbuf, Error>) + Send + 'static,
201    >(
202        stream: &P,
203        width: i32,
204        height: i32,
205        preserve_aspect_ratio: bool,
206        cancellable: Option<&Q>,
207        callback: R,
208    ) {
209        let cancellable = cancellable.map(|p| p.as_ref());
210        let user_data: Box<R> = Box::new(callback);
211        unsafe extern "C" fn new_from_stream_at_scale_async_trampoline<
212            R: FnOnce(Result<Pixbuf, Error>) + Send + 'static,
213        >(
214            _source_object: *mut gobject_sys::GObject,
215            res: *mut gio_sys::GAsyncResult,
216            user_data: glib_sys::gpointer,
217        ) {
218            let mut error = ptr::null_mut();
219            let ptr = gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_finish(res, &mut error);
220            let result = if error.is_null() {
221                Ok(from_glib_full(ptr))
222            } else {
223                Err(from_glib_full(error))
224            };
225            let callback: Box<R> = Box::from_raw(user_data as *mut _);
226            callback(result);
227        }
228        let callback = new_from_stream_at_scale_async_trampoline::<R>;
229        unsafe {
230            gdk_pixbuf_sys::gdk_pixbuf_new_from_stream_at_scale_async(
231                stream.as_ref().to_glib_none().0,
232                width,
233                height,
234                preserve_aspect_ratio.to_glib(),
235                cancellable.to_glib_none().0,
236                Some(callback),
237                Box::into_raw(user_data) as *mut _,
238            );
239        }
240    }
241
242    #[cfg(feature = "futures")]
243    pub fn new_from_stream_at_scale_async_future<P: IsA<gio::InputStream> + Clone + 'static>(
244        stream: &P,
245        width: i32,
246        height: i32,
247        preserve_aspect_ratio: bool,
248    ) -> Box<dyn Future<Output = Result<Pixbuf, Error>> + std::marker::Unpin> {
249        use gio::GioFuture;
250
251        let stream = stream.clone();
252        GioFuture::new(&(), move |_obj, send| {
253            use fragile::Fragile;
254
255            let cancellable = gio::Cancellable::new();
256            let send = Fragile::new(send);
257            Self::new_from_stream_at_scale_async(
258                &stream,
259                width,
260                height,
261                preserve_aspect_ratio,
262                Some(&cancellable),
263                move |res| {
264                    let _ = send.into_inner().send(res);
265                },
266            );
267
268            cancellable
269        })
270    }
271
272    #[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
273    pub unsafe fn get_pixels(&self) -> &mut [u8] {
274        let mut len = 0;
275        let ptr =
276            gdk_pixbuf_sys::gdk_pixbuf_get_pixels_with_length(self.to_glib_none().0, &mut len);
277        slice::from_raw_parts_mut(ptr, len as usize)
278    }
279
280    pub fn put_pixel(&self, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) {
281        unsafe {
282            let n_channels = self.get_n_channels();
283            assert!(n_channels == 3 || n_channels == 4);
284            let rowstride = self.get_rowstride();
285            let pixels = self.get_pixels();
286            let pos = (y * rowstride + x * n_channels) as usize;
287
288            pixels[pos] = red;
289            pixels[pos + 1] = green;
290            pixels[pos + 2] = blue;
291            if n_channels == 4 {
292                pixels[pos + 3] = alpha;
293            }
294        }
295    }
296
297    pub fn get_file_info<T: AsRef<Path>>(filename: T) -> Option<(PixbufFormat, i32, i32)> {
298        unsafe {
299            let mut width = mem::uninitialized();
300            let mut height = mem::uninitialized();
301            let ret = gdk_pixbuf_sys::gdk_pixbuf_get_file_info(
302                filename.as_ref().to_glib_none().0,
303                &mut width,
304                &mut height,
305            );
306            if !ret.is_null() {
307                Some((from_glib_none(ret), width, height))
308            } else {
309                None
310            }
311        }
312    }
313
314    #[cfg(any(feature = "v2_32", feature = "dox"))]
315    pub fn get_file_info_async<
316        P: IsA<gio::Cancellable>,
317        Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + Send + 'static,
318        T: AsRef<Path>,
319    >(
320        filename: T,
321        cancellable: Option<&P>,
322        callback: Q,
323    ) {
324        let cancellable = cancellable.map(|p| p.as_ref());
325        let user_data: Box<Q> = Box::new(callback);
326        unsafe extern "C" fn get_file_info_async_trampoline<
327            Q: FnOnce(Result<Option<(PixbufFormat, i32, i32)>, Error>) + Send + 'static,
328        >(
329            _source_object: *mut gobject_sys::GObject,
330            res: *mut gio_sys::GAsyncResult,
331            user_data: glib_sys::gpointer,
332        ) {
333            let mut error = ptr::null_mut();
334            let mut width = mem::uninitialized();
335            let mut height = mem::uninitialized();
336            let ret = gdk_pixbuf_sys::gdk_pixbuf_get_file_info_finish(
337                res,
338                &mut width,
339                &mut height,
340                &mut error,
341            );
342            let result = if !error.is_null() {
343                Err(from_glib_full(error))
344            } else if ret.is_null() {
345                Ok(None)
346            } else {
347                Ok(Some((from_glib_none(ret), width, height)))
348            };
349            let callback: Box<Q> = Box::from_raw(user_data as *mut _);
350            callback(result);
351        }
352        let callback = get_file_info_async_trampoline::<Q>;
353        unsafe {
354            gdk_pixbuf_sys::gdk_pixbuf_get_file_info_async(
355                filename.as_ref().to_glib_none().0,
356                cancellable.to_glib_none().0,
357                Some(callback),
358                Box::into_raw(user_data) as *mut _,
359            );
360        }
361    }
362
363    #[cfg(feature = "futures")]
364    #[cfg(any(feature = "v2_32", feature = "dox"))]
365    pub fn get_file_info_async_future<T: AsRef<Path> + Clone + 'static>(
366        filename: T,
367    ) -> Box<
368        dyn Future<Output = Result<Option<(PixbufFormat, i32, i32)>, Error>> + std::marker::Unpin,
369    > {
370        use gio::GioFuture;
371
372        GioFuture::new(&(), move |_obj, send| {
373            use fragile::Fragile;
374
375            let cancellable = gio::Cancellable::new();
376            let send = Fragile::new(send);
377            Self::get_file_info_async(filename, Some(&cancellable), move |res| {
378                let _ = send.into_inner().send(res);
379            });
380
381            cancellable
382        })
383    }
384
385    pub fn save_to_bufferv(&self, type_: &str, options: &[(&str, &str)]) -> Result<Vec<u8>, Error> {
386        unsafe {
387            let mut buffer = ptr::null_mut();
388            let mut buffer_size = mem::uninitialized();
389            let mut error = ptr::null_mut();
390            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
391            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
392            let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_bufferv(
393                self.to_glib_none().0,
394                &mut buffer,
395                &mut buffer_size,
396                type_.to_glib_none().0,
397                option_keys.to_glib_none().0,
398                option_values.to_glib_none().0,
399                &mut error,
400            );
401            if error.is_null() {
402                Ok(FromGlibContainer::from_glib_full_num(
403                    buffer,
404                    buffer_size as usize,
405                ))
406            } else {
407                Err(from_glib_full(error))
408            }
409        }
410    }
411
412    #[cfg(any(feature = "v2_36", feature = "dox"))]
413    pub fn save_to_streamv<'a, P: IsA<gio::OutputStream>, Q: IsA<gio::Cancellable>>(
414        &self,
415        stream: &P,
416        type_: &str,
417        options: &[(&str, &str)],
418        cancellable: Option<&Q>,
419    ) -> Result<(), Error> {
420        let cancellable = cancellable.map(|p| p.as_ref());
421        unsafe {
422            let mut error = ptr::null_mut();
423            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
424            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
425            let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_streamv(
426                self.to_glib_none().0,
427                stream.as_ref().to_glib_none().0,
428                type_.to_glib_none().0,
429                option_keys.to_glib_none().0,
430                option_values.to_glib_none().0,
431                cancellable.to_glib_none().0,
432                &mut error,
433            );
434            if error.is_null() {
435                Ok(())
436            } else {
437                Err(from_glib_full(error))
438            }
439        }
440    }
441
442    #[cfg(any(feature = "v2_36", feature = "dox"))]
443    pub fn save_to_streamv_async<
444        'a,
445        P: IsA<gio::OutputStream>,
446        Q: IsA<gio::Cancellable>,
447        R: FnOnce(Result<(), Error>) + Send + 'static,
448    >(
449        &self,
450        stream: &P,
451        type_: &str,
452        options: &[(&str, &str)],
453        cancellable: Option<&Q>,
454        callback: R,
455    ) {
456        let cancellable = cancellable.map(|p| p.as_ref());
457        let user_data: Box<R> = Box::new(callback);
458        unsafe extern "C" fn save_to_streamv_async_trampoline<
459            R: FnOnce(Result<(), Error>) + Send + 'static,
460        >(
461            _source_object: *mut gobject_sys::GObject,
462            res: *mut gio_sys::GAsyncResult,
463            user_data: glib_sys::gpointer,
464        ) {
465            let mut error = ptr::null_mut();
466            let _ = gdk_pixbuf_sys::gdk_pixbuf_save_to_stream_finish(res, &mut error);
467            let result = if error.is_null() {
468                Ok(())
469            } else {
470                Err(from_glib_full(error))
471            };
472            let callback: Box<R> = Box::from_raw(user_data as *mut _);
473            callback(result);
474        }
475        let callback = save_to_streamv_async_trampoline::<R>;
476        unsafe {
477            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
478            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
479            gdk_pixbuf_sys::gdk_pixbuf_save_to_streamv_async(
480                self.to_glib_none().0,
481                stream.as_ref().to_glib_none().0,
482                type_.to_glib_none().0,
483                option_keys.to_glib_none().0,
484                option_values.to_glib_none().0,
485                cancellable.to_glib_none().0,
486                Some(callback),
487                Box::into_raw(user_data) as *mut _,
488            );
489        }
490    }
491
492    #[cfg(feature = "futures")]
493    #[cfg(any(feature = "v2_36", feature = "dox"))]
494    pub fn save_to_streamv_async_future<P: IsA<gio::OutputStream> + Clone + 'static>(
495        &self,
496        stream: &P,
497        type_: &str,
498        options: &[(&str, &str)],
499    ) -> Box<dyn Future<Output = Result<(), Error>> + std::marker::Unpin> {
500        use fragile::Fragile;
501        use gio::GioFuture;
502
503        let stream = stream.clone();
504        let type_ = String::from(type_);
505        let options = options
506            .iter()
507            .map(|&(k, v)| (String::from(k), String::from(v)))
508            .collect::<Vec<(String, String)>>();
509        GioFuture::new(self, move |obj, send| {
510            let cancellable = gio::Cancellable::new();
511            let send = Fragile::new(send);
512            let options = options
513                .iter()
514                .map(|&(ref k, ref v)| (k.as_str(), v.as_str()))
515                .collect::<Vec<(&str, &str)>>();
516
517            obj.save_to_streamv_async(
518                &stream,
519                &type_,
520                options.as_slice(),
521                Some(&cancellable),
522                move |res| {
523                    let _ = send.into_inner().send(res);
524                },
525            );
526
527            cancellable
528        })
529    }
530
531    pub fn savev<T: AsRef<Path>>(
532        &self,
533        filename: T,
534        type_: &str,
535        options: &[(&str, &str)],
536    ) -> Result<(), Error> {
537        unsafe {
538            let mut error = ptr::null_mut();
539            let option_keys: Vec<&str> = options.iter().map(|o| o.0).collect();
540            let option_values: Vec<&str> = options.iter().map(|o| o.1).collect();
541            let _ = gdk_pixbuf_sys::gdk_pixbuf_savev(
542                self.to_glib_none().0,
543                filename.as_ref().to_glib_none().0,
544                type_.to_glib_none().0,
545                option_keys.to_glib_none().0,
546                option_values.to_glib_none().0,
547                &mut error,
548            );
549            if error.is_null() {
550                Ok(())
551            } else {
552                Err(from_glib_full(error))
553            }
554        }
555    }
556}