rs_vips/
image.rs

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