Skip to main content

rs_vips/
image.rs

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