Skip to main content

rs_vips/
image.rs

1// (c) Copyright 2019-2025 OLX
2// (c) Copyright 2025 mrdkprj
3use 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
35/// This is the main type of vips. It represents an image and most operations will take one as input and output a new one.
36/// In the moment this type is not thread safe. Be careful working within thread environments.
37impl VipsImage {
38    pub fn new() -> VipsImage {
39        VipsImage {
40            ctx: unsafe { bindings::vips_image_new() },
41        }
42    }
43
44    /// Creates a new `VipsImage` which, when written to, will create a memory image.
45    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    /// Opens the named file for reading.
56    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    /// Opens the named file for reading.
64    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    /// Opens the named file for simultaneous reading and writing.
103    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    /// Maps the named file and returns a `VipsImage` you can use to read it.
115    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    /// Loads an image from the formatted area of memory.
139    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    /// Loads an image from the formatted area of memory.
148    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    /// Loads an image from the formatted source.
198    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    /// Loads an image from the formatted source.
207    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    /// Wraps a VipsImage around a memory area.
247    ///
248    /// # Safety
249    ///
250    /// Because VIPS is "borrowing" data from the caller, this function is extremely dangerous.
251    /// Unless you are very careful, you will get crashes or memory corruption.
252    /// Use `new_from_memory_copy()` instead if you are at all unsure.
253    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    /// Like `new_from_memory()`, but VIPS will make a copy of the memory area.
285    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    /// Makes an image which is a matrix: a one-band `VIPS_FORMAT_DOUBLE` image held in memory.
317    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    /// As `new_matrix()`, but initialise the matrix from the argument list. Same as `new_matrix_from_array()`.
333    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    /// As `new_matrix()`, but initialise the matrix from the argument list. Same as `new_matrixv()`.
342    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    /// Creates a new image with width, height, format, interpretation, resolution and offset taken from image,
360    /// but with number of bands taken from `array` and the value of each band element set from the number of elements in the `array`.
361    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    /// Creates a new image with width, height, format, interpretation, resolution and offset taken from image, but with one band and each pixel having the value `c`.
378    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    /// Makes a `VipsImage` which, when written to, will create a temporary file on disc. The file will be automatically deleted when the image is destroyed.
394    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    /// Returns the underlying VipsImage reference that this holds
408    pub fn as_mut_ptr(&self) -> *mut bindings::VipsImage {
409        self.ctx
410    }
411
412    /// Allocates memory, renders image into it, builds a new image around the memory area, and returns that.
413    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    /// Invalidates all pixel caches on image and any downstream images, that is, images which depend on this image.
424    pub fn invalidate_all(&self) {
425        unsafe {
426            bindings::vips_image_invalidate_all(self.ctx);
427        }
428    }
429
430    /// Minimises memory use on this image and any upstream images, that is, images which this image depends upon.
431    pub fn minimise_all(&self) {
432        unsafe {
433            bindings::vips_image_minimise_all(self.ctx);
434        }
435    }
436
437    /// Gets image ready for an in-place operation
438    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    /// Sets the VipsImage.kill flag on an image.
566    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    /// Enables progress reporting on an image.
576    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    /// Writes this image to another image.
586    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    /// Writes this image to a file on disc.
601    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    /// Writes this image to a file on disc.
609    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    /// Writes this image to memory.
647    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    /// Writes this image to memory.
655    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            /* Save with the new target API if we can. Fall back to the older
662             * mechanism in case the saver we need has not been converted yet.
663             *
664             * We need to hide any errors from this first phase.
665             */
666            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    /// Writes this image to a target.
723    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    /// Writes this image to a target.
732    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    /// Writes this image to a large memory array.
772    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    /// Reads the GType for a header field.
855    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    /// Gets int from image under the name.
871    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    /// Attaches int as a metadata item on image as name.
889    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    /// Gets double from image under the name.
902    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    /// Attaches double as a metadata item on image as name.
920    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    /// Gets string from image under the name.
933    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    /// Attaches string as a metadata item on image as name.
958    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    /// Gets data from image under the name.
973    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    /// Attaches data as a metadata item on image under the name.
1000    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    /// Gets an array of int from image under the name.
1015    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    /// Attaches array as a metadata item on image as name.
1041    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    /// Gets an array of double from image under the name.
1055    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    /// Attaches array as a metadata item on image as name.
1081    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    /// Attaches image as a metadata item on image as name.
1095    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    /// Finds and removes an item of metadata.
1108    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    /// Returns the coordinates of the image minimum.
1121    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    /// Returns the coordinates of the image maximum.
1146    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}