1use crate::{
4 bindings,
5 connection::{VipsSource, VipsTarget},
6 error::Error,
7 ops::*,
8 region::VipsBlob,
9 utils::{self, ensure_null_terminated, vips_image_result, vips_image_result_ext},
10 voption::{call, call_option_string_, Setter, VOption},
11 Result,
12};
13use num_traits::{FromPrimitive, ToPrimitive};
14use std::{
15 ffi::{c_char, c_int, c_void, CStr},
16 path::Path,
17 ptr::null_mut,
18};
19
20const NULL: *const c_void = null_mut();
21
22#[derive(Debug, Clone)]
23pub struct VipsImage {
24 pub(crate) ctx: *mut bindings::VipsImage,
25}
26
27impl Default for VipsImage {
28 fn default() -> VipsImage {
29 VipsImage {
30 ctx: unsafe { bindings::vips_image_new() },
31 }
32 }
33}
34
35impl VipsImage {
38 pub fn new() -> VipsImage {
39 VipsImage {
40 ctx: unsafe { bindings::vips_image_new() },
41 }
42 }
43
44 pub fn new_memory() -> Result<VipsImage> {
46 unsafe {
47 let res = bindings::vips_image_new_memory();
48 vips_image_result(
49 res,
50 Error::InitializationError("Could not generate object".to_string()),
51 )
52 }
53 }
54
55 pub fn new_from_file<P: AsRef<Path>>(filename: P) -> Result<VipsImage> {
57 Self::new_from_file_with_opts(
58 filename,
59 VOption::new(),
60 )
61 }
62
63 pub fn new_from_file_with_opts<P: AsRef<Path>>(
65 filename: P,
66 option: VOption,
67 ) -> Result<VipsImage> {
68 unsafe {
69 let f = utils::path_to_cstring(filename)?;
70 let filename_part = bindings::vips_filename_get_filename(f.as_ptr());
71 let string_options = bindings::vips_filename_get_options(f.as_ptr());
72
73 let operation = bindings::vips_foreign_find_load(filename_part);
74 if operation.is_null() {
75 return vips_image_result(
76 NULL as _,
77 Error::InitializationError("Could not find operation".to_string()),
78 );
79 }
80
81 let mut out_out = VipsImage::from(null_mut());
82 call_option_string_(
83 operation,
84 string_options,
85 option
86 .set(
87 "filename",
88 filename_part,
89 )
90 .set(
91 "out",
92 &mut out_out,
93 ),
94 )?;
95 vips_image_result_ext(
96 out_out,
97 Error::InitializationError("Could not initialise VipsImage from file".to_string()),
98 )
99 }
100 }
101
102 pub fn new_from_file_rw<P: AsRef<Path>>(filename: P) -> Result<VipsImage> {
104 unsafe {
105 let f = utils::path_to_cstring(filename)?;
106 let res = bindings::vips_image_new_from_file_RW(f.as_ptr());
107 vips_image_result(
108 res,
109 Error::InitializationError("Could not initialise VipsImage from file".to_string()),
110 )
111 }
112 }
113
114 pub fn new_from_file_raw<P: AsRef<Path>>(
116 filename: P,
117 x_size: i32,
118 y_size: i32,
119 bands: i32,
120 offset: u64,
121 ) -> Result<VipsImage> {
122 unsafe {
123 let f = utils::path_to_cstring(filename)?;
124 let res = bindings::vips_image_new_from_file_raw(
125 f.as_ptr(),
126 x_size,
127 y_size,
128 bands,
129 offset,
130 );
131 vips_image_result(
132 res,
133 Error::InitializationError("Could not initialise VipsImage from file".to_string()),
134 )
135 }
136 }
137
138 pub fn new_from_buffer(buffer: &[u8], option_str: &str) -> Result<VipsImage> {
140 Self::new_from_buffer_with_opts(
141 buffer,
142 option_str,
143 VOption::new(),
144 )
145 }
146
147 pub fn new_from_buffer_with_opts(
149 buffer: &[u8],
150 option_str: &str,
151 option: VOption,
152 ) -> Result<VipsImage> {
153 unsafe {
154 let operation = bindings::vips_foreign_find_load_buffer(
155 buffer.as_ptr() as *const c_void,
156 buffer.len() as u64,
157 );
158 if operation.is_null() {
159 return vips_image_result(
160 NULL as _,
161 Error::InitializationError(
162 "Could not initialise VipsImage from buffer".to_string(),
163 ),
164 );
165 }
166
167 let vips_blob = bindings::vips_blob_new(
168 None,
169 buffer.as_ptr() as *const c_void,
170 buffer.len() as u64,
171 );
172 let mut out_out = VipsImage::from(null_mut());
173 let blob = VipsBlob::from(vips_blob);
174 call_option_string_(
175 operation,
176 utils::new_c_string(option_str)?.as_ptr(),
177 option
178 .set(
179 "buffer",
180 &blob,
181 )
182 .set(
183 "out",
184 &mut out_out,
185 ),
186 )?;
187 blob.area_unref();
188 vips_image_result_ext(
189 out_out,
190 Error::InitializationError(
191 "Could not initialise VipsImage from buffer".to_string(),
192 ),
193 )
194 }
195 }
196
197 pub fn new_from_source(source: &VipsSource, option_str: &str) -> Result<VipsImage> {
199 Self::new_from_source_with_opts(
200 source,
201 option_str,
202 VOption::new(),
203 )
204 }
205
206 pub fn new_from_source_with_opts(
208 source: &VipsSource,
209 option_str: &str,
210 option: VOption,
211 ) -> Result<VipsImage> {
212 unsafe {
213 let operation = bindings::vips_foreign_find_load_source(source.ctx);
214 if operation.is_null() {
215 return vips_image_result(
216 NULL as _,
217 Error::InitializationError(
218 "Could not initialise VipsImage from source".to_string(),
219 ),
220 );
221 }
222
223 let mut out_out = VipsImage::from(null_mut());
224 call_option_string_(
225 operation,
226 utils::new_c_string(option_str)?.as_ptr(),
227 option
228 .set(
229 "source",
230 source,
231 )
232 .set(
233 "out",
234 &mut out_out,
235 ),
236 )?;
237 vips_image_result_ext(
238 out_out,
239 Error::InitializationError(
240 "Could not initialise VipsImage from buffer".to_string(),
241 ),
242 )
243 }
244 }
245
246 pub unsafe fn new_from_memory(
254 buffer: &[u8],
255 width: i32,
256 height: i32,
257 bands: i32,
258 format: BandFormat,
259 ) -> Result<VipsImage> {
260 unsafe {
261 if let Some(format) = format.to_i32() {
262 let res = bindings::vips_image_new_from_memory(
263 buffer.as_ptr() as *const c_void,
264 buffer.len() as u64,
265 width,
266 height,
267 bands,
268 format,
269 );
270 vips_image_result(
271 res,
272 Error::InitializationError(
273 "Could not initialise VipsImage from memory".to_string(),
274 ),
275 )
276 } else {
277 Err(Error::InitializationError(
278 "Invalid BandFormat. Please file a bug report, as this should never happen.".to_string(),
279 ))
280 }
281 }
282 }
283
284 pub fn new_from_memory_copy(
286 buffer: &[u8],
287 width: i32,
288 height: i32,
289 bands: i32,
290 format: BandFormat,
291 ) -> Result<VipsImage> {
292 unsafe {
293 if let Some(format) = format.to_i32() {
294 let res = bindings::vips_image_new_from_memory_copy(
295 buffer.as_ptr() as *const c_void,
296 buffer.len() as u64,
297 width,
298 height,
299 bands,
300 format,
301 );
302 vips_image_result(
303 res,
304 Error::InitializationError(
305 "Could not initialise VipsImage from memory".to_string(),
306 ),
307 )
308 } else {
309 Err(Error::InitializationError(
310 "Invalid BandFormat. Please file a bug report, as this should never happen.".to_string(),
311 ))
312 }
313 }
314 }
315
316 pub fn new_matrix(width: i32, height: i32) -> Result<VipsImage> {
318 unsafe {
319 let res = bindings::vips_image_new_matrix(
320 width,
321 height,
322 );
323 vips_image_result(
324 res,
325 Error::InitializationError(
326 "Could not initialise VipsImage from matrix".to_string(),
327 ),
328 )
329 }
330 }
331
332 pub fn new_matrixv(width: i32, height: i32, array: &[f64]) -> Result<VipsImage> {
334 Self::new_matrix_from_array(
335 width,
336 height,
337 array,
338 )
339 }
340
341 pub fn new_matrix_from_array(width: i32, height: i32, array: &[f64]) -> Result<VipsImage> {
343 unsafe {
344 let res = bindings::vips_image_new_matrix_from_array(
345 width,
346 height,
347 array.as_ptr(),
348 array.len() as c_int,
349 );
350 vips_image_result(
351 res,
352 Error::InitializationError(
353 "Could not initialise VipsImage from matrix".to_string(),
354 ),
355 )
356 }
357 }
358
359 pub fn new_from_image(image: &VipsImage, array: &[f64]) -> Result<VipsImage> {
362 unsafe {
363 let res = bindings::vips_image_new_from_image(
364 image.ctx,
365 array.as_ptr(),
366 array.len() as c_int,
367 );
368 vips_image_result(
369 res,
370 Error::InitializationError(
371 "Could not initialise VipsImage from Object".to_string(),
372 ),
373 )
374 }
375 }
376
377 pub fn new_from_image1(image: &VipsImage, c: f64) -> Result<VipsImage> {
379 unsafe {
380 let res = bindings::vips_image_new_from_image1(
381 image.ctx,
382 c,
383 );
384 vips_image_result(
385 res,
386 Error::InitializationError(
387 "Could not initialise VipsImage from Object".to_string(),
388 ),
389 )
390 }
391 }
392
393 pub fn new_temp_file(format: &str) -> Result<VipsImage> {
395 unsafe {
396 let format_c_str = utils::new_c_string(format)?;
397 let res = bindings::vips_image_new_temp_file(format_c_str.as_ptr());
398 vips_image_result(
399 res,
400 Error::InitializationError(
401 "Could not initialise VipsImage from format".to_string(),
402 ),
403 )
404 }
405 }
406
407 pub fn as_mut_ptr(&self) -> *mut bindings::VipsImage {
409 self.ctx
410 }
411
412 pub fn copy_memory(&self) -> Result<VipsImage> {
414 unsafe {
415 let result = bindings::vips_image_copy_memory(self.ctx);
416 vips_image_result(
417 result,
418 Error::OperationError("Could not copy memory".to_string()),
419 )
420 }
421 }
422
423 pub fn invalidate_all(&self) {
425 unsafe {
426 bindings::vips_image_invalidate_all(self.ctx);
427 }
428 }
429
430 pub fn minimise_all(&self) {
432 unsafe {
433 bindings::vips_image_minimise_all(self.ctx);
434 }
435 }
436
437 pub fn inplace(&mut self) -> Result<()> {
439 unsafe {
440 let res = bindings::vips_image_inplace(self.ctx);
441 utils::result(
442 res,
443 (),
444 Error::IOError("Cannot cannot be modified inplace".to_string()),
445 )
446 }
447 }
448
449 pub fn get_filename(&self) -> std::result::Result<&str, std::str::Utf8Error> {
450 unsafe {
451 let filename = bindings::vips_image_get_filename(self.ctx);
452 let res = CStr::from_ptr(filename);
453 res.to_str()
454 }
455 }
456
457 pub fn get_width(&self) -> i32 {
458 unsafe { bindings::vips_image_get_width(self.ctx) }
459 }
460
461 pub fn get_height(&self) -> i32 {
462 unsafe { bindings::vips_image_get_height(self.ctx) }
463 }
464
465 pub fn get_xoffset(&self) -> i32 {
466 unsafe { bindings::vips_image_get_xoffset(self.ctx) }
467 }
468
469 pub fn get_yoffset(&self) -> i32 {
470 unsafe { bindings::vips_image_get_yoffset(self.ctx) }
471 }
472
473 pub fn get_scale(&self) -> f64 {
474 unsafe { bindings::vips_image_get_scale(self.ctx) }
475 }
476
477 pub fn get_offset(&self) -> f64 {
478 unsafe { bindings::vips_image_get_offset(self.ctx) }
479 }
480
481 pub fn get_xres(&self) -> f64 {
482 unsafe { bindings::vips_image_get_xres(self.ctx) }
483 }
484
485 pub fn get_yres(&self) -> f64 {
486 unsafe { bindings::vips_image_get_yres(self.ctx) }
487 }
488
489 pub fn get_bands(&self) -> i32 {
490 unsafe { bindings::vips_image_get_bands(self.ctx) }
491 }
492
493 pub fn get_page_height(&self) -> i32 {
494 unsafe { bindings::vips_image_get_page_height(self.ctx) }
495 }
496
497 pub fn get_n_pages(&self) -> i32 {
498 unsafe { bindings::vips_image_get_n_pages(self.ctx) }
499 }
500
501 pub fn get_coding(&self) -> Result<Coding> {
502 unsafe {
503 let res = bindings::vips_image_get_format(self.ctx);
504 let format_enum = FromPrimitive::from_i32(res);
505 format_enum.ok_or(Error::IOError("Could get format from image".to_string()))
506 }
507 }
508
509 pub fn get_format(&self) -> Result<BandFormat> {
510 unsafe {
511 let res = bindings::vips_image_get_format(self.ctx);
512 let format_enum = FromPrimitive::from_i32(res);
513 format_enum.ok_or(Error::IOError("Could get format from image".to_string()))
514 }
515 }
516
517 pub fn guess_format(&self) -> Result<BandFormat> {
518 unsafe {
519 let res = bindings::vips_image_guess_format(self.ctx);
520 let format_enum = FromPrimitive::from_i32(res);
521 format_enum.ok_or(Error::IOError("Could get format from image".to_string()))
522 }
523 }
524
525 pub fn get_orientation(&self) -> i32 {
526 unsafe { bindings::vips_image_get_orientation(self.ctx) }
527 }
528
529 pub fn get_interpretation(&self) -> Result<Interpretation> {
530 unsafe {
531 let res = bindings::vips_image_get_interpretation(self.ctx);
532 let format_enum = FromPrimitive::from_i32(res);
533 format_enum.ok_or(Error::IOError("Could get format from image".to_string()))
534 }
535 }
536
537 pub fn guess_interpretation(&self) -> Result<Interpretation> {
538 unsafe {
539 let res = bindings::vips_image_guess_interpretation(self.ctx);
540 let format_enum = FromPrimitive::from_i32(res);
541 format_enum.ok_or(Error::IOError("Could get format from image".to_string()))
542 }
543 }
544
545 pub fn iskilled(&self) -> bool {
546 unsafe { bindings::vips_image_iskilled(self.ctx) == 1 }
547 }
548
549 pub fn isMSBfirst(&self) -> bool {
550 unsafe { bindings::vips_image_isMSBfirst(self.ctx) == 1 }
551 }
552
553 pub fn isfile(&self) -> bool {
554 unsafe { bindings::vips_image_isfile(self.ctx) == 1 }
555 }
556
557 pub fn ispartial(&self) -> bool {
558 unsafe { bindings::vips_image_ispartial(self.ctx) == 1 }
559 }
560
561 pub fn hasalpha(&self) -> bool {
562 unsafe { bindings::vips_image_hasalpha(self.ctx) == 1 }
563 }
564
565 pub fn set_kill(&self, flag: bool) {
567 unsafe {
568 bindings::vips_image_set_kill(
569 self.ctx,
570 if flag { 1 } else { 0 },
571 );
572 }
573 }
574
575 pub fn set_progress(&self, flag: bool) {
577 unsafe {
578 bindings::vips_image_set_progress(
579 self.ctx,
580 if flag { 1 } else { 0 },
581 );
582 }
583 }
584
585 pub fn write(&self, other: &VipsImage) -> Result<()> {
587 unsafe {
588 let res = bindings::vips_image_write(
589 self.ctx,
590 other.ctx,
591 );
592 utils::result(
593 res,
594 (),
595 Error::IOError("Cannot write input to output".to_string()),
596 )
597 }
598 }
599
600 pub fn write_to_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
602 self.write_to_file_with_opts(
603 filename,
604 VOption::new(),
605 )
606 }
607
608 pub fn write_to_file_with_opts<P: AsRef<Path>>(
610 &self,
611 filename: P,
612 option: VOption,
613 ) -> Result<()> {
614 unsafe {
615 let f = utils::path_to_cstring(filename)?;
616 let filename_part = bindings::vips_filename_get_filename(f.as_ptr());
617 let string_options = bindings::vips_filename_get_options(f.as_ptr());
618
619 let operation = bindings::vips_foreign_find_save(filename_part);
620 if operation.is_null() {
621 return utils::result(
622 -1,
623 (),
624 Error::IOError("Cannot write to file".to_string()),
625 );
626 }
627
628 let res = call_option_string_(
629 operation,
630 string_options,
631 option
632 .set("in", self)
633 .set(
634 "filename",
635 filename_part,
636 ),
637 )?;
638 utils::result(
639 res,
640 (),
641 Error::IOError("Cannot write to file".to_string()),
642 )
643 }
644 }
645
646 pub fn write_to_buffer(&self, suffix: &str) -> Result<Vec<u8>> {
648 self.write_to_buffer_with_opts(
649 suffix,
650 VOption::new(),
651 )
652 }
653
654 pub fn write_to_buffer_with_opts(&self, suffix: &str, option: VOption) -> Result<Vec<u8>> {
656 unsafe {
657 let f = utils::new_c_string(suffix)?;
658 let filename = bindings::vips_filename_get_filename(f.as_ptr());
659 let string_options = bindings::vips_filename_get_options(f.as_ptr());
660
661 bindings::vips_error_freeze();
667 let operation = bindings::vips_foreign_find_save_target(filename);
668 bindings::vips_error_thaw();
669
670 if !operation.is_null() {
671 let target = VipsTarget::new_to_memory()?;
672 let res = call_option_string_(
673 operation,
674 string_options,
675 option
676 .set("in", self)
677 .set(
678 "target",
679 &target,
680 ),
681 )?;
682 return utils::safe_result(
683 res,
684 target,
685 move |target| {
686 target
687 .get_blob()
688 .into()
689 },
690 Error::IOError("Cannot write to buffer".to_string()),
691 );
692 }
693
694 let operation = bindings::vips_foreign_find_save_buffer(filename);
695 if operation.is_null() {
696 return utils::result(
697 -1,
698 Vec::new(),
699 Error::IOError("Cannot write to buffer".to_string()),
700 );
701 }
702
703 let mut buffer_out = VipsBlob::from(null_mut());
704 let res = call_option_string_(
705 operation,
706 string_options,
707 option
708 .set("in", self)
709 .set(
710 "buffer",
711 &mut buffer_out,
712 ),
713 )?;
714 utils::result(
715 res,
716 buffer_out.into(),
717 Error::IOError("Cannot write to buffer".to_string()),
718 )
719 }
720 }
721
722 pub fn write_to_target(&self, suffix: &str, target: &VipsTarget) -> Result<()> {
724 self.write_to_target_with_opts(
725 suffix,
726 target,
727 VOption::new(),
728 )
729 }
730
731 pub fn write_to_target_with_opts(
733 &self,
734 suffix: &str,
735 target: &VipsTarget,
736 option: VOption,
737 ) -> Result<()> {
738 unsafe {
739 let f = utils::new_c_string(suffix)?;
740 let filename = bindings::vips_filename_get_filename(f.as_ptr());
741 let string_options = bindings::vips_filename_get_options(f.as_ptr());
742
743 let operation = bindings::vips_foreign_find_save_target(filename);
744
745 if operation.is_null() {
746 return utils::result(
747 -1,
748 (),
749 Error::IOError("Cannot write to target".to_string()),
750 );
751 }
752
753 let res = call_option_string_(
754 operation,
755 string_options,
756 option
757 .set("in", self)
758 .set(
759 "target",
760 target,
761 ),
762 )?;
763 utils::result(
764 res,
765 (),
766 Error::IOError("Cannot write to target".to_string()),
767 )
768 }
769 }
770
771 pub fn write_to_memory(&self) -> Vec<u8> {
773 unsafe {
774 let mut buffer_buf_size: u64 = 0;
775 let buffer_out = bindings::vips_image_write_to_memory(
776 self.ctx,
777 &mut buffer_buf_size,
778 );
779 let buffer = std::slice::from_raw_parts(
780 buffer_out as *mut u8,
781 buffer_buf_size as usize,
782 )
783 .to_vec();
784 bindings::g_free(buffer_out);
785 buffer
786 }
787 }
788
789 pub fn decode_predict(
790 &self,
791 ) -> Result<(
792 i32,
793 BandFormat,
794 )> {
795 unsafe {
796 let mut out_bands = 0;
797 let mut out_format = 0;
798 let res = bindings::vips_image_decode_predict(
799 self.ctx,
800 &mut out_bands,
801 &mut out_format,
802 );
803 let format_enum = FromPrimitive::from_i32(out_format);
804 if let Some(format_enum) = format_enum {
805 utils::result(
806 res,
807 (
808 out_bands,
809 format_enum,
810 ),
811 Error::IOError("Could not predict image format".to_string()),
812 )
813 } else {
814 Err(Error::IOError("Could not predict image format".to_string()))
815 }
816 }
817 }
818
819 pub fn decode(&self) -> Result<VipsImage> {
820 unsafe {
821 let mut out: *mut bindings::VipsImage = null_mut();
822 let res = bindings::vips_image_decode(
823 self.ctx,
824 &mut out,
825 );
826 utils::result(
827 res,
828 VipsImage {
829 ctx: out,
830 },
831 Error::IOError("Cannot decode image".to_string()),
832 )
833 }
834 }
835
836 pub fn encode(&self, coding: Coding) -> Result<VipsImage> {
837 unsafe {
838 let mut out: *mut bindings::VipsImage = null_mut();
839 let res = bindings::vips_image_encode(
840 self.ctx,
841 &mut out,
842 coding as i32,
843 );
844 utils::result(
845 res,
846 VipsImage {
847 ctx: out,
848 },
849 Error::IOError("Cannot encode image".to_string()),
850 )
851 }
852 }
853
854 pub fn get_typeof(&self, type_: impl AsRef<[u8]>) -> Result<u64> {
856 unsafe {
857 let type_name = ensure_null_terminated(type_)?;
858 let gtype = bindings::vips_image_get_typeof(
859 self.ctx,
860 type_name.as_ptr(),
861 );
862 utils::result(
863 0,
864 gtype,
865 Error::IOError("Cannot get type".to_string()),
866 )
867 }
868 }
869
870 pub fn get_int(&self, name: impl AsRef<[u8]>) -> Result<i32> {
872 unsafe {
873 let mut out = 0;
874 let name = ensure_null_terminated(name)?;
875 let res = bindings::vips_image_get_int(
876 self.ctx,
877 name.as_ptr(),
878 &mut out,
879 );
880 utils::result(
881 res,
882 out,
883 Error::IOError("Cannot get int".to_string()),
884 )
885 }
886 }
887
888 pub fn set_int(&mut self, name: impl AsRef<[u8]>, value: i32) -> Result<()> {
890 unsafe {
891 let name = ensure_null_terminated(name)?;
892 bindings::vips_image_set_int(
893 self.ctx,
894 name.as_ptr(),
895 value,
896 );
897 Ok(())
898 }
899 }
900
901 pub fn get_double(&self, name: impl AsRef<[u8]>) -> Result<f64> {
903 unsafe {
904 let mut out = 0.0;
905 let name = ensure_null_terminated(name)?;
906 let res = bindings::vips_image_get_double(
907 self.ctx,
908 name.as_ptr(),
909 &mut out,
910 );
911 utils::result(
912 res,
913 out,
914 Error::IOError("Cannot get int".to_string()),
915 )
916 }
917 }
918
919 pub fn set_double(&mut self, name: impl AsRef<[u8]>, value: f64) -> Result<()> {
921 unsafe {
922 let name = ensure_null_terminated(name)?;
923 bindings::vips_image_set_double(
924 self.ctx,
925 name.as_ptr(),
926 value,
927 );
928 Ok(())
929 }
930 }
931
932 pub fn get_string(&self, name: impl AsRef<[u8]>) -> Result<String> {
934 unsafe {
935 let mut out: *const c_char = std::ptr::null();
936 let name = ensure_null_terminated(name)?;
937 let res = bindings::vips_image_get_string(
938 self.ctx,
939 name.as_ptr(),
940 &mut out,
941 );
942 utils::safe_result(
943 res,
944 out,
945 move |out| {
946 if let Ok(cstr) = CStr::from_ptr(out).to_str() {
947 cstr.to_string()
948 } else {
949 String::new()
950 }
951 },
952 Error::IOError("Cannot get string".to_string()),
953 )
954 }
955 }
956
957 pub fn set_string(&mut self, name: impl AsRef<[u8]>, value: &str) -> Result<()> {
959 unsafe {
960 let name = ensure_null_terminated(name)?;
961 let value = ensure_null_terminated(value)?;
962
963 bindings::vips_image_set_string(
964 self.ctx,
965 name.as_ptr(),
966 value.as_ptr(),
967 );
968 Ok(())
969 }
970 }
971
972 pub fn get_blob(&self, name: impl AsRef<[u8]>) -> Result<Vec<u8>> {
974 unsafe {
975 let mut out: *const c_void = std::ptr::null();
976 let mut length = 0;
977 let name = ensure_null_terminated(name)?;
978 let res = bindings::vips_image_get_blob(
979 self.ctx,
980 name.as_ptr(),
981 &mut out,
982 &mut length,
983 );
984 utils::safe_result(
985 res,
986 out,
987 move |out| {
988 std::slice::from_raw_parts(
989 out as *const u8,
990 length as usize,
991 )
992 .to_vec()
993 },
994 Error::IOError("Cannot get blob".to_string()),
995 )
996 }
997 }
998
999 pub fn set_blob(&mut self, name: impl AsRef<[u8]>, blob: &[u8]) -> Result<()> {
1001 unsafe {
1002 let name = ensure_null_terminated(name)?;
1003 bindings::vips_image_set_blob(
1004 self.ctx,
1005 name.as_ptr(),
1006 None,
1007 blob.as_ptr() as *const c_void,
1008 blob.len() as u64,
1009 );
1010 Ok(())
1011 }
1012 }
1013
1014 pub fn get_array_int(&self, name: impl AsRef<[u8]>) -> Result<Vec<i32>> {
1016 unsafe {
1017 let mut out: *mut i32 = std::ptr::null_mut();
1018 let mut size = 0;
1019 let name = ensure_null_terminated(name)?;
1020 let res = bindings::vips_image_get_array_int(
1021 self.ctx,
1022 name.as_ptr(),
1023 &mut out,
1024 &mut size,
1025 );
1026 utils::safe_result(
1027 res,
1028 out,
1029 move |out| {
1030 utils::new_int_array(
1031 out,
1032 size as u64,
1033 )
1034 },
1035 Error::IOError("Cannot get array int".to_string()),
1036 )
1037 }
1038 }
1039
1040 pub fn set_array_int(&mut self, name: impl AsRef<[u8]>, value: &[i32]) -> Result<()> {
1042 unsafe {
1043 let name = ensure_null_terminated(name)?;
1044 bindings::vips_image_set_array_int(
1045 self.ctx,
1046 name.as_ptr(),
1047 value.as_ptr(),
1048 value.len() as c_int,
1049 );
1050 Ok(())
1051 }
1052 }
1053
1054 pub fn get_array_double(&self, name: impl AsRef<[u8]>) -> Result<Vec<f64>> {
1056 unsafe {
1057 let mut out: *mut f64 = std::ptr::null_mut();
1058 let mut size = 0;
1059 let name = ensure_null_terminated(name)?;
1060 let res = bindings::vips_image_get_array_double(
1061 self.ctx,
1062 name.as_ptr(),
1063 &mut out,
1064 &mut size,
1065 );
1066 utils::safe_result(
1067 res,
1068 out,
1069 move |out| {
1070 utils::new_double_array(
1071 out,
1072 size as u64,
1073 )
1074 },
1075 Error::IOError("Cannot get array double".to_string()),
1076 )
1077 }
1078 }
1079
1080 pub fn set_array_double(&mut self, name: impl AsRef<[u8]>, value: &[f64]) -> Result<()> {
1082 unsafe {
1083 let name = ensure_null_terminated(name)?;
1084 bindings::vips_image_set_array_double(
1085 self.ctx,
1086 name.as_ptr(),
1087 value.as_ptr(),
1088 value.len() as c_int,
1089 );
1090 Ok(())
1091 }
1092 }
1093
1094 pub fn set_image(&mut self, name: impl AsRef<[u8]>, value: &VipsImage) -> Result<()> {
1096 unsafe {
1097 let name = ensure_null_terminated(name)?;
1098 bindings::vips_image_set_image(
1099 self.ctx,
1100 name.as_ptr(),
1101 value.ctx,
1102 );
1103 Ok(())
1104 }
1105 }
1106
1107 pub fn remove(&mut self, name: impl AsRef<[u8]>) -> Result<bool> {
1109 unsafe {
1110 let name = ensure_null_terminated(name)?;
1111 Ok(
1112 bindings::vips_image_remove(
1113 self.ctx,
1114 name.as_ptr(),
1115 ) == 1,
1116 )
1117 }
1118 }
1119
1120 pub fn minpos(&self) -> Result<(f64, f64)> {
1122 let mut x: f64 = 0.0;
1123 let mut y: f64 = 0.0;
1124
1125 let vips_op_response = call(
1126 "min",
1127 VOption::new()
1128 .set("in", self)
1129 .set(
1130 "x",
1131 &mut x,
1132 )
1133 .set(
1134 "y",
1135 &mut y,
1136 ),
1137 )?;
1138 utils::result(
1139 vips_op_response,
1140 (x, y),
1141 Error::OperationError("minpos failed".to_string()),
1142 )
1143 }
1144
1145 pub fn maxpos(&self) -> Result<(f64, f64)> {
1147 let mut x: f64 = 0.0;
1148 let mut y: f64 = 0.0;
1149
1150 let vips_op_response = call(
1151 "max",
1152 VOption::new()
1153 .set("in", self)
1154 .set(
1155 "x",
1156 &mut x,
1157 )
1158 .set(
1159 "y",
1160 &mut y,
1161 ),
1162 )?;
1163 utils::result(
1164 vips_op_response,
1165 (x, y),
1166 Error::OperationError("maxpos failed".to_string()),
1167 )
1168 }
1169}
1170
1171impl Drop for VipsImage {
1172 fn drop(&mut self) {
1173 unsafe {
1174 if !self
1175 .ctx
1176 .is_null()
1177 {
1178 bindings::g_object_unref(self.ctx as *mut c_void);
1179 }
1180 }
1181 }
1182}
1183
1184impl From<*mut bindings::VipsImage> for VipsImage {
1185 fn from(value: *mut bindings::VipsImage) -> Self {
1186 Self {
1187 ctx: value,
1188 }
1189 }
1190}