rs_vips/
image.rs

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