1use 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); }
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}