1use crate::{take_vips_error, Error, Result, VipsInterpolate};
2use std::ffi::CString;
3use std::marker::PhantomData;
4use std::os::raw::{c_char, c_int, c_void};
5use std::ptr::{null, null_mut};
6use vips_sys::{VipsBandFormat, VipsCombineMode, VipsDirection, VipsKernel, VipsSize};
7
8pub struct VipsImage<'a> {
45 pub c: *mut vips_sys::VipsImage,
46 marker: PhantomData<&'a ()>,
47}
48
49impl<'a> Drop for VipsImage<'a> {
50 fn drop(&mut self) {
51 unsafe {
52 vips_sys::g_object_unref(self.c as *mut c_void);
53 }
54 }
55}
56
57pub unsafe extern "C" fn image_postclose(_ptr: *mut vips_sys::VipsImage, user_data: *mut c_void) {
68 let b: Box<Box<[u8]>> = Box::from_raw(user_data as *mut Box<[u8]>);
69 drop(b);
70}
71
72impl<'a> VipsImage<'a> {
73 pub fn new() -> Result<VipsImage<'a>> {
93 let c = unsafe { vips_sys::vips_image_new() };
94 result(c)
95 }
96
97 pub fn new_memory() -> Result<VipsImage<'a>> {
113 let c = unsafe { vips_sys::vips_image_new_memory() };
114 result(c)
115 }
116
117 pub fn from_file<S: Into<Vec<u8>>>(path: S) -> Result<VipsImage<'a>> {
139 let path = path.into();
140 let path = std::str::from_utf8(&path)
141 .map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
142 let path =
143 CString::new(path).map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
144 unsafe {
145 let ptr = vips_sys::vips_image_new_from_file(path.as_ptr(), null() as *const c_char);
146 result(ptr)
147 }
148 }
149
150 pub fn from_memory(
175 buf: Vec<u8>,
176 width: u32,
177 height: u32,
178 bands: u8,
179 format: VipsBandFormat,
180 ) -> Result<VipsImage<'a>> {
181 let b: Box<[_]> = buf.into_boxed_slice();
182 let c = unsafe {
183 vips_sys::vips_image_new_from_memory(
184 b.as_ptr() as *const c_void,
185 b.len(),
186 width as i32,
187 height as i32,
188 bands as i32,
189 format,
190 )
191 };
192
193 let bb: Box<Box<_>> = Box::new(b);
194 let raw: *mut c_void = Box::into_raw(bb) as *mut c_void;
195
196 unsafe {
197 let callback: unsafe extern "C" fn() =
198 std::mem::transmute(image_postclose as *const ());
199 vips_sys::g_signal_connect_data(
200 c as *mut c_void,
201 c"postclose".as_ptr(),
202 Some(callback),
203 raw,
204 None,
205 vips_sys::GConnectFlags::G_CONNECT_AFTER,
206 );
207 };
208
209 result(c)
210 }
211
212 pub fn from_memory_reference(
240 buf: &'a [u8],
241 width: u32,
242 height: u32,
243 bands: u8,
244 format: VipsBandFormat,
245 ) -> Result<VipsImage<'a>> {
246 let c = unsafe {
247 vips_sys::vips_image_new_from_memory(
248 buf.as_ptr() as *const c_void,
249 buf.len(),
250 width as i32,
251 height as i32,
252 bands as i32,
253 format,
254 )
255 };
256
257 result(c)
258 }
259
260 pub fn from_buffer(buf: &'a [u8]) -> Result<VipsImage<'a>> {
283 let c = unsafe {
284 vips_sys::vips_image_new_from_buffer(
285 buf.as_ptr() as *const c_void,
286 buf.len(),
287 null(),
288 null() as *const c_char,
289 )
290 };
291
292 result(c)
293 }
294
295 pub fn draw_rect(
327 &mut self,
328 ink: &[f64],
329 left: u32,
330 top: u32,
331 width: u32,
332 height: u32,
333 ) -> Result<()> {
334 let ret = unsafe {
335 vips_sys::vips_draw_rect(
336 self.c as *mut vips_sys::VipsImage,
337 ink.as_ptr() as *mut f64,
338 ink.len() as i32,
339 left as i32,
340 top as i32,
341 width as i32,
342 height as i32,
343 null() as *const c_char,
344 )
345 };
346 result_draw(ret)
347 }
348 pub fn draw_rect1(
349 &mut self,
350 ink: f64,
351 left: u32,
352 top: u32,
353 width: u32,
354 height: u32,
355 ) -> Result<()> {
356 let ret = unsafe {
357 vips_sys::vips_draw_rect1(
358 self.c as *mut vips_sys::VipsImage,
359 ink,
360 left as i32,
361 top as i32,
362 width as i32,
363 height as i32,
364 null() as *const c_char,
365 )
366 };
367 result_draw(ret)
368 }
369 pub fn draw_point(&mut self, ink: &[f64], x: i32, y: i32) -> Result<()> {
370 let ret = unsafe {
371 vips_sys::vips_draw_point(
372 self.c as *mut vips_sys::VipsImage,
373 ink.as_ptr() as *mut f64,
374 ink.len() as i32,
375 x,
376 y,
377 null() as *const c_char,
378 )
379 };
380 result_draw(ret)
381 }
382 pub fn draw_point1(&mut self, ink: f64, x: i32, y: i32) -> Result<()> {
383 let ret = unsafe {
384 vips_sys::vips_draw_point1(
385 self.c as *mut vips_sys::VipsImage,
386 ink,
387 x,
388 y,
389 null() as *const c_char,
390 )
391 };
392 result_draw(ret)
393 }
394 pub fn draw_image(
395 &mut self,
396 img: &VipsImage,
397 x: i32,
398 y: i32,
399 mode: VipsCombineMode,
400 ) -> Result<()> {
401 let ret = unsafe {
402 vips_sys::vips_draw_image(
403 self.c as *mut vips_sys::VipsImage,
404 img.c as *mut vips_sys::VipsImage,
405 x,
406 y,
407 c"mode".as_ptr(),
408 mode,
409 null() as *const c_char,
410 )
411 };
412 result_draw(ret)
413 }
414 pub fn draw_mask(&mut self, ink: &[f64], mask: &VipsImage, x: i32, y: i32) -> Result<()> {
415 let ret = unsafe {
416 vips_sys::vips_draw_mask(
417 self.c as *mut vips_sys::VipsImage,
418 ink.as_ptr() as *mut f64,
419 ink.len() as i32,
420 mask.c as *mut vips_sys::VipsImage,
421 x,
422 y,
423 null() as *const c_char,
424 )
425 };
426 result_draw(ret)
427 }
428 pub fn draw_mask1(&mut self, ink: f64, mask: &VipsImage, x: i32, y: i32) -> Result<()> {
429 let ret = unsafe {
430 vips_sys::vips_draw_mask1(
431 self.c as *mut vips_sys::VipsImage,
432 ink,
433 mask.c as *mut vips_sys::VipsImage,
434 x,
435 y,
436 null() as *const c_char,
437 )
438 };
439 result_draw(ret)
440 }
441 pub fn draw_line(&mut self, ink: &[f64], x1: i32, y1: i32, x2: i32, y2: i32) -> Result<()> {
442 let ret = unsafe {
443 vips_sys::vips_draw_line(
444 self.c as *mut vips_sys::VipsImage,
445 ink.as_ptr() as *mut f64,
446 ink.len() as i32,
447 x1,
448 y1,
449 x2,
450 y2,
451 null() as *const c_char,
452 )
453 };
454 result_draw(ret)
455 }
456 pub fn draw_line1(&mut self, ink: f64, x1: i32, y1: i32, x2: i32, y2: i32) -> Result<()> {
457 let ret = unsafe {
458 vips_sys::vips_draw_line1(
459 self.c as *mut vips_sys::VipsImage,
460 ink,
461 x1,
462 y1,
463 x2,
464 y2,
465 null() as *const c_char,
466 )
467 };
468 result_draw(ret)
469 }
470 pub fn draw_circle(&mut self, ink: &[f64], cx: i32, cy: i32, r: i32, fill: bool) -> Result<()> {
471 let ret = unsafe {
472 vips_sys::vips_draw_circle(
473 self.c as *mut vips_sys::VipsImage,
474 ink.as_ptr() as *mut f64,
475 ink.len() as i32,
476 cx,
477 cy,
478 r,
479 c"fill".as_ptr(),
480 fill as i32,
481 null() as *const c_char,
482 )
483 };
484 result_draw(ret)
485 }
486 pub fn draw_circle1(&mut self, ink: f64, cx: i32, cy: i32, r: i32, fill: bool) -> Result<()> {
487 let ret = unsafe {
488 vips_sys::vips_draw_circle1(
489 self.c as *mut vips_sys::VipsImage,
490 ink,
491 cx,
492 cy,
493 r,
494 c"fill".as_ptr(),
495 fill as i32,
496 null() as *const c_char,
497 )
498 };
499 result_draw(ret)
500 }
501 pub fn draw_flood(&mut self, ink: &[f64], x: i32, y: i32) -> Result<()> {
502 let ret = unsafe {
503 vips_sys::vips_draw_flood(
504 self.c as *mut vips_sys::VipsImage,
505 ink.as_ptr() as *mut f64,
506 ink.len() as i32,
507 x,
508 y,
509 null() as *const c_char,
510 )
511 };
512 result_draw(ret)
513 }
514 pub fn draw_flood1(&mut self, ink: f64, x: i32, y: i32) -> Result<()> {
515 let ret = unsafe {
516 vips_sys::vips_draw_flood1(
517 self.c as *mut vips_sys::VipsImage,
518 ink,
519 x,
520 y,
521 null() as *const c_char,
522 )
523 };
524 result_draw(ret)
525 }
526 pub fn draw_smudge(&mut self, left: u32, top: u32, width: u32, height: u32) -> Result<()> {
527 let ret = unsafe {
528 vips_sys::vips_draw_smudge(
529 self.c as *mut vips_sys::VipsImage,
530 left as i32,
531 top as i32,
532 width as i32,
533 height as i32,
534 null() as *const c_char,
535 )
536 };
537 result_draw(ret)
538 }
539
540 pub fn merge(
545 &self,
546 another: &VipsImage,
547 direction: VipsDirection,
548 dx: i32,
549 dy: i32,
550 mblend: Option<i32>,
551 ) -> Result<VipsImage<'a>> {
552 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
553 let ret = unsafe {
554 vips_sys::vips_merge(
555 self.c as *mut vips_sys::VipsImage,
556 another.c as *mut vips_sys::VipsImage,
557 &mut out_ptr,
558 direction,
559 dx,
560 dy,
561 c"mblend".as_ptr(),
562 mblend.unwrap_or(-1),
563 null() as *const c_char,
564 )
565 };
566 result_with_ret(out_ptr, ret)
567 }
568
569 #[allow(clippy::too_many_arguments)]
570 pub fn mosaic(
571 &self,
572 sec: &VipsImage,
573 direction: VipsDirection,
574 xref: i32,
575 yref: i32,
576 xsec: i32,
577 ysec: i32,
578 bandno: Option<i32>,
579 hwindow: Option<i32>,
580 harea: Option<i32>,
581 mblend: Option<i32>,
582 ) -> Result<VipsImage<'_>> {
583 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
584 let ret = unsafe {
585 vips_sys::vips_mosaic(
586 self.c as *mut vips_sys::VipsImage,
587 sec.c as *mut vips_sys::VipsImage,
588 &mut out_ptr,
589 direction,
590 xref,
591 yref,
592 xsec,
593 ysec,
594 c"bandno".as_ptr(),
595 bandno.unwrap_or(0),
596 c"hwindow".as_ptr(),
597 hwindow.unwrap_or(1),
598 c"harea".as_ptr(),
599 harea.unwrap_or(1),
600 c"mblend".as_ptr(),
601 mblend.unwrap_or(-1),
602 null() as *const c_char,
603 )
604 };
605 result_with_ret(out_ptr, ret)
606 }
607
608 #[allow(clippy::too_many_arguments)]
609 pub fn mosaic1(
610 &self,
611 sec: &VipsImage,
612 direction: VipsDirection,
613 xr1: i32,
614 yr1: i32,
615 xs1: i32,
616 ys1: i32,
617 xr2: i32,
618 yr2: i32,
619 xs2: i32,
620 ys2: i32,
621 search: Option<bool>,
622 hwindow: Option<i32>,
623 harea: Option<i32>,
624 interpolate: Option<VipsInterpolate>,
625 mblend: Option<i32>,
626 bandno: Option<i32>,
627 ) -> Result<VipsImage<'_>> {
628 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
629 let ret = unsafe {
630 match interpolate {
631 Some(interpolate) => vips_sys::vips_mosaic1(
632 self.c,
633 sec.c,
634 &mut out_ptr,
635 direction,
636 xr1,
637 yr1,
638 xs1,
639 ys1,
640 xr2,
641 yr2,
642 xs2,
643 ys2,
644 c"search".as_ptr(),
645 search.unwrap_or(false) as i32,
646 c"hwindow".as_ptr(),
647 hwindow.unwrap_or(1),
648 c"harea".as_ptr(),
649 harea.unwrap_or(1),
650 c"interpolate".as_ptr(),
651 interpolate.c,
652 c"mblend".as_ptr(),
653 mblend.unwrap_or(-1),
654 c"bandno".as_ptr(),
655 bandno.unwrap_or(0),
656 null() as *const c_char,
657 ),
658 None => vips_sys::vips_mosaic1(
659 self.c as *mut vips_sys::VipsImage,
660 sec.c as *mut vips_sys::VipsImage,
661 &mut out_ptr,
662 direction,
663 xr1,
664 yr1,
665 xs1,
666 ys1,
667 xr2,
668 yr2,
669 xs2,
670 ys2,
671 c"search".as_ptr(),
672 search.unwrap_or(false) as i32,
673 c"hwindow".as_ptr(),
674 hwindow.unwrap_or(1),
675 c"harea".as_ptr(),
676 harea.unwrap_or(1),
677 c"mblend".as_ptr(),
678 mblend.unwrap_or(-1),
679 c"bandno".as_ptr(),
680 bandno.unwrap_or(0),
681 null() as *const c_char,
682 ),
683 }
684 };
685 result_with_ret(out_ptr, ret)
686 }
687
688 #[allow(clippy::too_many_arguments)]
689 pub fn match_(
690 &self,
691 sec: &VipsImage,
692 xr1: i32,
693 yr1: i32,
694 xs1: i32,
695 ys1: i32,
696 xr2: i32,
697 yr2: i32,
698 xs2: i32,
699 ys2: i32,
700 search: Option<bool>,
701 hwindow: Option<i32>,
702 harea: Option<i32>,
703 interpolate: Option<VipsInterpolate>,
704 ) -> Result<VipsImage<'_>> {
705 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
706 let ret = unsafe {
707 match interpolate {
708 Some(interpolate) => vips_sys::vips_match(
709 self.c as *mut vips_sys::VipsImage,
710 sec.c as *mut vips_sys::VipsImage,
711 &mut out_ptr,
712 xr1,
713 yr1,
714 xs1,
715 ys1,
716 xr2,
717 yr2,
718 xs2,
719 ys2,
720 c"search".as_ptr(),
721 search.unwrap_or(false) as i32,
722 c"hwindow".as_ptr(),
723 hwindow.unwrap_or(1),
724 c"harea".as_ptr(),
725 harea.unwrap_or(1),
726 c"interpolate".as_ptr(),
727 interpolate.c as *mut vips_sys::VipsInterpolate,
728 null() as *const c_char,
729 ),
730 None => vips_sys::vips_match(
731 self.c as *mut vips_sys::VipsImage,
732 sec.c as *mut vips_sys::VipsImage,
733 &mut out_ptr,
734 xr1,
735 yr1,
736 xs1,
737 ys1,
738 xr2,
739 yr2,
740 xs2,
741 ys2,
742 c"search".as_ptr(),
743 search.unwrap_or(false) as i32,
744 c"hwindow".as_ptr(),
745 hwindow.unwrap_or(1),
746 c"harea".as_ptr(),
747 harea.unwrap_or(1),
748 null() as *const c_char,
749 ),
750 }
751 };
752 result_with_ret(out_ptr, ret)
753 }
754
755 pub fn globalbalance(
756 &self,
757 gamma: Option<f64>,
758 int_output: Option<bool>,
759 ) -> Result<VipsImage<'_>> {
760 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
761 let ret = unsafe {
762 vips_sys::vips_globalbalance(
763 self.c as *mut vips_sys::VipsImage,
764 &mut out_ptr,
765 c"gamma".as_ptr(),
766 gamma.unwrap_or(1.6),
767 c"int_output".as_ptr(),
768 int_output.unwrap_or(false) as i32,
769 null() as *const c_char,
770 )
771 };
772 result_with_ret(out_ptr, ret)
773 }
774
775 pub fn remosaic(&self, old_str: &str, new_str: &str) -> Result<VipsImage<'_>> {
776 let old_str = CString::new(old_str)
777 .map_err(|e| Error::InitFailed(format!("invalid old_str: {}", e)))?;
778 let new_str = CString::new(new_str)
779 .map_err(|e| Error::InitFailed(format!("invalid new_str: {}", e)))?;
780 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
781 let ret = unsafe {
782 vips_sys::vips_remosaic(
783 self.c as *mut vips_sys::VipsImage,
784 &mut out_ptr,
785 old_str.as_ptr(),
786 new_str.as_ptr(),
787 null() as *const c_char,
788 )
789 };
790 result_with_ret(out_ptr, ret)
791 }
792
793 #[allow(dead_code)]
798 fn width(&self) -> u32 {
799 unsafe { (*self.c).Xsize as u32 }
800 }
801
802 #[allow(dead_code)]
803 fn height(&self) -> u32 {
804 unsafe { (*self.c).Ysize as u32 }
805 }
806
807 pub fn thumbnail(&self, width: u32, height: u32, size: VipsSize) -> Result<VipsImage<'_>> {
837 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
838 unsafe {
839 vips_sys::vips_thumbnail_image(
840 self.c as *mut vips_sys::VipsImage,
841 &mut out_ptr,
842 width as i32,
843 c"height".as_ptr(),
844 height as i32,
845 c"size".as_ptr(),
846 size,
847 null() as *const c_char,
848 );
849 };
850 result(out_ptr)
851 }
852
853 pub fn resize(
879 &self,
880 scale: f64,
881 vscale: Option<f64>,
882 kernel: Option<VipsKernel>,
883 ) -> Result<VipsImage<'_>> {
884 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
885 let ret = unsafe {
886 vips_sys::vips_resize(
887 self.c as *mut vips_sys::VipsImage,
888 &mut out_ptr,
889 scale,
890 c"vscale".as_ptr(),
891 vscale.unwrap_or(scale),
892 c"kernel".as_ptr(),
893 kernel.unwrap_or(VipsKernel::VIPS_KERNEL_LANCZOS3),
894 null() as *const c_char,
895 )
896 };
897 result_with_ret(out_ptr, ret)
898 }
899 #[allow(dead_code)]
900 fn resize_to_size(
901 &self,
902 width: u32,
903 height: Option<u32>,
904 kernel: Option<VipsKernel>,
905 ) -> Result<VipsImage<'_>> {
906 self.resize(
907 width as f64 / self.width() as f64,
908 height.map(|h| h as f64 / self.height() as f64),
909 kernel,
910 )
911 }
912
913 #[allow(dead_code)]
917 fn reduce(
918 &self,
919 hshrink: f64,
920 vshrink: f64,
921 kernel: Option<VipsKernel>,
922 centre: Option<bool>,
923 ) -> VipsImage<'_> {
924 let mut out_ptr: *mut vips_sys::VipsImage = null_mut();
925 let ret = unsafe {
926 vips_sys::vips_reduce(
927 self.c as *mut vips_sys::VipsImage,
928 &mut out_ptr,
929 hshrink,
930 vshrink,
931 c"kernel".as_ptr(),
932 kernel.unwrap_or(VipsKernel::VIPS_KERNEL_LANCZOS3),
933 c"centre".as_ptr(),
934 centre.unwrap_or(false) as i32,
935 null() as *const c_char,
936 )
937 };
938 if ret == 0 {
939 VipsImage {
940 c: out_ptr,
941 marker: PhantomData,
942 }
943 } else {
944 panic!(
945 "{}",
946 take_vips_error().unwrap_or_else(|| { "Unknown error from libvips".to_string() })
947 )
948 }
949 }
950
951 #[allow(dead_code)]
952 fn shrink(&self) -> VipsImage<'_> {
953 self.reduce(2.0, 2.0, Some(VipsKernel::VIPS_KERNEL_LINEAR), Some(false))
956 }
957
958 #[allow(dead_code)]
963 fn jpegsave<S: Into<Vec<u8>>>(&mut self, path: S) -> Result<()> {
964 let path = path.into();
965 let path = std::str::from_utf8(&path)
966 .map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
967 let path =
968 CString::new(path).map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
969 let ret = unsafe {
970 vips_sys::vips_jpegsave(
971 self.c as *mut vips_sys::VipsImage,
972 path.as_ptr(),
973 null() as *const c_char,
974 )
975 };
976 result_draw(ret)
977 }
978
979 pub fn write_to_file<S: Into<Vec<u8>>>(&self, path: S) -> Result<()> {
980 let path = path.into();
981 let path = std::str::from_utf8(&path)
982 .map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
983 let path =
984 CString::new(path).map_err(|e| Error::InitFailed(format!("invalid path: {}", e)))?;
985 let ret = unsafe {
986 vips_sys::vips_image_write_to_file(
987 self.c as *mut vips_sys::VipsImage,
988 path.as_ptr(),
989 null() as *const c_char,
990 )
991 };
992 result_draw(ret)
993 }
994
995 #[allow(dead_code)]
1000 fn to_vec(&self) -> Vec<u8> {
1001 unsafe {
1002 let mut result_size: usize = 0;
1003 let ptr = vips_sys::vips_image_write_to_memory(self.c, &mut result_size as *mut usize)
1004 as *mut u8;
1005 if ptr.is_null() {
1006 return Vec::new();
1007 }
1008 let slice = std::slice::from_raw_parts(ptr as *const u8, result_size);
1009 let vec = slice.to_vec();
1010 vips_sys::g_free(ptr as *mut c_void); vec
1012 }
1013 }
1014}
1015
1016fn result<'a>(ptr: *mut vips_sys::VipsImage) -> Result<VipsImage<'a>> {
1017 if ptr.is_null() {
1018 Err(Error::Vips(take_vips_error().unwrap_or_else(|| {
1019 "Unknown error from libvips".to_string()
1020 })))
1021 } else {
1022 Ok(VipsImage {
1023 c: ptr,
1024 marker: PhantomData,
1025 })
1026 }
1027}
1028
1029fn result_with_ret<'a>(ptr: *mut vips_sys::VipsImage, ret: c_int) -> Result<VipsImage<'a>> {
1030 match ret {
1031 0 => Ok(VipsImage {
1032 c: ptr,
1033 marker: PhantomData,
1034 }),
1035 -1 => {
1036 Err(Error::Vips(take_vips_error().unwrap_or_else(|| {
1037 "Unknown error from libvips".to_string()
1038 })))
1039 }
1040 _ => Err(Error::Vips("Unknown error from libvips".to_string())),
1041 }
1042}
1043
1044fn result_draw(ret: c_int) -> Result<()> {
1045 match ret {
1046 0 => Ok(()),
1047 -1 => {
1048 Err(Error::Vips(take_vips_error().unwrap_or_else(|| {
1049 "Unknown error from libvips".to_string()
1050 })))
1051 }
1052 _ => Err(Error::Vips("Unknown error from libvips".to_string())),
1053 }
1054}