Skip to main content

rs_vips/
voption.rs

1// (c) Copyright 2025 mrdkprj
2use crate::{
3    bindings::{
4        g_object_get_property, g_object_ref, g_object_set_property, g_object_unref,
5        g_type_check_instance_is_a, g_value_get_boolean, g_value_get_double, g_value_get_int,
6        g_value_get_object, g_value_get_string, g_value_init, g_value_set_boolean,
7        g_value_set_double, g_value_set_enum, g_value_set_int, g_value_set_object,
8        g_value_set_string, g_value_set_uint64, g_value_unset, size_t, vips_array_double_get_type,
9        vips_array_image_get_type, vips_array_int_get_type, vips_blob_get_type,
10        vips_cache_operation_buildp, vips_enum_from_nick, vips_error_clear, vips_image_get_type,
11        vips_interpolate_get_type, vips_object_get_argument, vips_object_set_from_string,
12        vips_object_unref_outputs, vips_operation_new, vips_source_get_type, vips_target_get_type,
13        vips_value_get_array_double, vips_value_get_array_image, vips_value_get_blob,
14        vips_value_set_array_double, vips_value_set_array_image, vips_value_set_array_int,
15        vips_value_set_blob, GParamSpec, GTypeInstance, GValue, VipsArgumentClass,
16        VipsArgumentInstance, VipsImage, VipsObject, VipsOperation,
17    },
18    utils::{
19        g_warning, get_g_type, new_c_string, G_TYPE_BOOLEAN, G_TYPE_DOUBLE, G_TYPE_INT,
20        G_TYPE_STRING, G_TYPE_UINT64,
21    },
22    Result,
23};
24use std::{
25    ffi::{c_char, c_int, c_void},
26    mem::MaybeUninit,
27    sync::Arc,
28};
29
30const ERROR_CODE: c_int = -1;
31
32/// Runs the vips operation with options
33pub fn call(operation: &str, option: VOption) -> Result<c_int> {
34    call_option_string(
35        operation,
36        "",
37        option,
38    )
39}
40
41/// Runs the vips operation with options
42pub fn call_option_string(operation: &str, option_string: &str, option: VOption) -> Result<c_int> {
43    let operation = new_c_string(operation)?;
44    let option_string = new_c_string(option_string)?;
45    call_option_string_(
46        operation.as_ptr(),
47        option_string.as_ptr(),
48        option,
49    )
50}
51
52pub(crate) fn call_option_string_(
53    operation: *const c_char,
54    option_string: *const c_char,
55    option: VOption,
56) -> Result<c_int> {
57    unsafe {
58        let mut vips_operation = vips_operation_new(operation);
59
60        // Set option_string before set options
61        if !option_string.is_null()
62            && vips_object_set_from_string(
63                vips_operation as *mut VipsObject,
64                option_string,
65            ) < 0
66        {
67            vips_object_unref_outputs(vips_operation as *mut VipsObject);
68            g_object_unref(vips_operation as *mut c_void);
69            return Ok(ERROR_CODE);
70        }
71
72        // Collect input buffer/image/images beforehand for output image
73        let image_source = get_image_source(&option);
74
75        // Set input args
76        set_opreration(
77            vips_operation,
78            &option,
79            &image_source,
80        )?;
81
82        let result = vips_cache_operation_buildp(&mut vips_operation);
83
84        if result < 0 {
85            vips_object_unref_outputs(vips_operation as *mut VipsObject);
86            g_object_unref(vips_operation as *mut c_void);
87            return Ok(ERROR_CODE);
88        }
89
90        // Write output
91        get_operation(
92            vips_operation,
93            option,
94            image_source,
95        )?;
96
97        g_object_unref(vips_operation as *mut c_void);
98
99        Ok(result)
100    }
101}
102
103#[derive(Default)]
104struct ImageSource {
105    buffer: Option<Arc<[u8]>>,
106    images: Vec<Arc<crate::Image>>,
107}
108
109fn get_image_source(option: &VOption) -> ImageSource {
110    let mut image_source = ImageSource::default();
111    let has_output_image = &option
112        .options
113        .iter()
114        .any(|pair| {
115            matches!(
116                pair.value,
117                VipsValue::MutImage(_)
118            )
119        });
120    if *has_output_image {
121        for pair in &option.options {
122            if !pair.input {
123                continue;
124            }
125
126            match pair.value {
127                VipsValue::Blob(buffer) => {
128                    // Take ownership of buffer
129                    image_source.buffer = Some(Arc::from(
130                        buffer,
131                    ));
132                }
133                VipsValue::Image(vips_image) => {
134                    image_source
135                        .images
136                        .push(Arc::clone(&vips_image.image));
137                }
138                VipsValue::ImageArray(vips_images) => {
139                    for vips_image in vips_images {
140                        image_source
141                            .images
142                            .push(Arc::clone(&vips_image.image));
143                    }
144                }
145                _ => {}
146            }
147        }
148    }
149    image_source
150}
151
152#[allow(clippy::arc_with_non_send_sync)]
153fn get_operation(
154    vips_operation: *mut VipsOperation,
155    option: VOption,
156    image_source: ImageSource,
157) -> Result<()> {
158    unsafe {
159        for pair in option.options {
160            if pair.input {
161                continue;
162            }
163
164            let mut gvalue = MaybeUninit::<GValue>::zeroed();
165            let gvalue_ptr = gvalue.as_mut_ptr();
166            let name = new_c_string(pair.name)?;
167
168            match pair.value {
169                VipsValue::MutBool(out) => {
170                    g_value_init(
171                        gvalue_ptr,
172                        get_g_type(G_TYPE_BOOLEAN)?,
173                    );
174                    g_object_get_property(
175                        vips_operation.cast(),
176                        name.as_ptr(),
177                        gvalue_ptr,
178                    );
179                    *out = g_value_get_boolean(gvalue_ptr) != 0;
180                }
181                VipsValue::MutInt(out) => {
182                    g_value_init(
183                        gvalue_ptr,
184                        get_g_type(G_TYPE_INT)?,
185                    );
186                    g_object_get_property(
187                        vips_operation.cast(),
188                        name.as_ptr(),
189                        gvalue_ptr,
190                    );
191                    *out = g_value_get_int(gvalue_ptr);
192                }
193                VipsValue::MutDouble(out) => {
194                    g_value_init(
195                        gvalue_ptr,
196                        get_g_type(G_TYPE_DOUBLE)?,
197                    );
198                    g_object_get_property(
199                        vips_operation.cast(),
200                        name.as_ptr(),
201                        gvalue_ptr,
202                    );
203                    *out = g_value_get_double(gvalue_ptr);
204                }
205                VipsValue::MutDoubleArray(out) => {
206                    g_value_init(
207                        gvalue_ptr,
208                        vips_array_double_get_type(),
209                    );
210                    g_object_get_property(
211                        vips_operation.cast(),
212                        name.as_ptr(),
213                        gvalue_ptr,
214                    );
215                    let mut len = 0;
216                    let array = vips_value_get_array_double(
217                        gvalue_ptr,
218                        &mut len,
219                    );
220                    let result = std::slice::from_raw_parts(
221                        array,
222                        len as usize,
223                    );
224                    out.extend(result);
225                }
226                VipsValue::MutBlob(bytes) => {
227                    g_value_init(
228                        gvalue_ptr,
229                        vips_blob_get_type(),
230                    );
231                    g_object_get_property(
232                        vips_operation.cast(),
233                        name.as_ptr(),
234                        gvalue_ptr,
235                    );
236                    let mut length = 0;
237                    let ptr = vips_value_get_blob(
238                        gvalue_ptr,
239                        &mut length,
240                    );
241                    if length > 0 {
242                        let slice = std::slice::from_raw_parts(
243                            ptr as *const u8,
244                            length,
245                        );
246                        bytes.extend(slice);
247                    }
248                }
249                VipsValue::MutImage(out) => {
250                    g_value_init(
251                        gvalue_ptr,
252                        vips_image_get_type(),
253                    );
254                    g_object_get_property(
255                        vips_operation.cast(),
256                        name.as_ptr(),
257                        gvalue_ptr,
258                    );
259                    let out_image: *mut VipsImage = g_value_get_object(gvalue_ptr).cast();
260                    // Copy underlying buffer/images
261                    out.image = Arc::new(
262                        crate::Image {
263                            ctx: out_image,
264                            buffer: image_source
265                                .buffer
266                                .clone(),
267                            refs: image_source
268                                .images
269                                .clone(),
270                        },
271                    );
272                }
273                _ => {}
274            }
275            g_value_unset(gvalue_ptr);
276        }
277    }
278
279    Ok(())
280}
281
282fn set_opreration(
283    operation: *mut VipsOperation,
284    option: &VOption,
285    image_source: &ImageSource,
286) -> Result<()> {
287    unsafe {
288        for pair in &option.options {
289            if !pair.input {
290                continue;
291            }
292
293            let mut gvalue = MaybeUninit::<GValue>::zeroed();
294            let gvalue_ptr = gvalue.as_mut_ptr();
295
296            match pair.value {
297                VipsValue::Bool(value) => {
298                    g_value_init(
299                        gvalue_ptr,
300                        get_g_type(G_TYPE_BOOLEAN)?,
301                    );
302                    g_value_set_boolean(
303                        gvalue_ptr,
304                        value.into(),
305                    );
306                }
307                VipsValue::Int(value) => {
308                    g_value_init(
309                        gvalue_ptr,
310                        get_g_type(G_TYPE_INT)?,
311                    );
312                    g_value_set_int(
313                        gvalue_ptr,
314                        value,
315                    );
316                }
317                VipsValue::Uint(value) => {
318                    g_value_init(
319                        gvalue_ptr,
320                        get_g_type(G_TYPE_UINT64)?,
321                    );
322                    g_value_set_uint64(
323                        gvalue_ptr,
324                        value,
325                    );
326                }
327                VipsValue::Double(value) => {
328                    g_value_init(
329                        gvalue_ptr,
330                        get_g_type(G_TYPE_DOUBLE)?,
331                    );
332                    g_value_set_double(
333                        gvalue_ptr,
334                        value,
335                    );
336                }
337                VipsValue::Str(value) => {
338                    let str = new_c_string(value)?;
339                    g_value_init(
340                        gvalue_ptr,
341                        get_g_type(G_TYPE_STRING)?,
342                    );
343                    g_value_set_string(
344                        gvalue_ptr,
345                        str.as_ptr(),
346                    );
347                }
348                VipsValue::CStr(value) => {
349                    g_value_init(
350                        gvalue_ptr,
351                        get_g_type(G_TYPE_STRING)?,
352                    );
353                    g_value_set_string(
354                        gvalue_ptr,
355                        value,
356                    );
357                }
358                VipsValue::IntArray(value) => {
359                    g_value_init(
360                        gvalue_ptr,
361                        vips_array_int_get_type(),
362                    );
363                    vips_value_set_array_int(
364                        gvalue_ptr,
365                        value.as_ptr(),
366                        value.len() as c_int,
367                    );
368                }
369                VipsValue::DoubleArray(value) => {
370                    g_value_init(
371                        gvalue_ptr,
372                        vips_array_double_get_type(),
373                    );
374                    vips_value_set_array_double(
375                        gvalue_ptr,
376                        value.as_ptr(),
377                        value.len() as c_int,
378                    );
379                }
380                VipsValue::Image(value) => {
381                    g_value_init(
382                        gvalue_ptr,
383                        vips_image_get_type(),
384                    );
385                    g_value_set_object(
386                        gvalue_ptr,
387                        value
388                            .image
389                            .ctx as *mut c_void,
390                    );
391                }
392                VipsValue::ImageArray(value) => {
393                    g_value_init(
394                        gvalue_ptr,
395                        vips_array_image_get_type(),
396                    );
397                    vips_value_set_array_image(
398                        gvalue_ptr,
399                        value.len() as c_int,
400                    );
401                    let array = vips_value_get_array_image(
402                        gvalue_ptr,
403                        &mut 0,
404                    );
405                    let array = std::slice::from_raw_parts_mut(
406                        array,
407                        value.len(),
408                    );
409                    for i in 0..value.len() {
410                        g_object_ref(
411                            value[i]
412                                .image
413                                .ctx as *mut c_void,
414                        );
415                        array[i] = value[i]
416                            .image
417                            .ctx;
418                    }
419                }
420                VipsValue::Blob(_) => {
421                    g_value_init(
422                        gvalue_ptr,
423                        vips_blob_get_type(),
424                    );
425                    vips_value_set_blob(
426                        gvalue_ptr,
427                        None,
428                        image_source
429                            .buffer
430                            .as_ref()
431                            .unwrap()
432                            .as_ptr() as *const c_void,
433                        image_source
434                            .buffer
435                            .as_ref()
436                            .unwrap()
437                            .len() as size_t,
438                    );
439                }
440                VipsValue::Source(value) => {
441                    g_value_init(
442                        gvalue_ptr,
443                        vips_source_get_type(),
444                    );
445                    g_value_set_object(
446                        gvalue_ptr,
447                        value.ctx as *mut c_void,
448                    );
449                }
450                VipsValue::Target(value) => {
451                    g_value_init(
452                        gvalue_ptr,
453                        vips_target_get_type(),
454                    );
455                    g_value_set_object(
456                        gvalue_ptr,
457                        value.ctx as *mut c_void,
458                    );
459                }
460                VipsValue::Interpolate(value) => {
461                    g_value_init(
462                        gvalue_ptr,
463                        vips_interpolate_get_type(),
464                    );
465                    g_value_set_object(
466                        gvalue_ptr,
467                        value.ctx as *mut c_void,
468                    );
469                }
470                _ => {}
471            }
472
473            set_property(
474                operation,
475                &pair.name,
476                gvalue_ptr,
477            )?;
478            g_value_unset(gvalue_ptr);
479        }
480    }
481
482    Ok(())
483}
484
485fn set_property(operation: *mut VipsOperation, name: &str, value: *mut GValue) -> Result<()> {
486    unsafe {
487        let object: *mut VipsObject = operation.cast();
488        let name = new_c_string(name)?;
489
490        let mut pspec: *mut GParamSpec = std::ptr::null_mut();
491        let mut argument_class: *mut VipsArgumentClass = std::ptr::null_mut();
492        let mut argument_instance: *mut VipsArgumentInstance = std::ptr::null_mut();
493        if vips_object_get_argument(
494            object,
495            name.as_ptr(),
496            &mut pspec,
497            &mut argument_class,
498            &mut argument_instance,
499        ) < 0
500        {
501            g_warning()?;
502            vips_error_clear();
503            return Ok(());
504        }
505
506        let is_param_spec_enum = g_type_check_instance_is_a(
507            pspec as *mut GTypeInstance,
508            get_g_type("GParamEnum")?,
509        ) != 0;
510
511        if is_param_spec_enum && (*value).g_type == get_g_type(G_TYPE_STRING)? {
512            let pspec_type = (*pspec).value_type;
513            let enum_value = vips_enum_from_nick(
514                (*object).nickname,
515                pspec_type,
516                g_value_get_string(value),
517            );
518            if enum_value < 0 {
519                g_warning()?;
520                vips_error_clear();
521                return Ok(());
522            }
523
524            let mut gvalue = MaybeUninit::<GValue>::zeroed();
525            let value2 = gvalue.as_mut_ptr();
526            g_value_init(
527                value2,
528                pspec_type,
529            );
530            g_value_set_enum(
531                value2,
532                enum_value,
533            );
534            g_object_set_property(
535                object.cast(),
536                name.as_ptr(),
537                value2,
538            );
539            g_value_unset(value2);
540        } else {
541            g_object_set_property(
542                object.cast(),
543                name.as_ptr(),
544                value,
545            );
546        }
547    }
548
549    Ok(())
550}
551
552enum VipsValue<'a> {
553    Bool(bool),
554    MutBool(&'a mut bool),
555    Int(i32),
556    MutInt(&'a mut i32),
557    Uint(u64),
558    Double(f64),
559    MutDouble(&'a mut f64),
560    Str(&'a str),
561    CStr(*const c_char),
562    Image(&'a crate::VipsImage),
563    MutImage(&'a mut crate::VipsImage),
564    IntArray(&'a [i32]),
565    DoubleArray(&'a [f64]),
566    MutDoubleArray(&'a mut Vec<f64>),
567    ImageArray(&'a [crate::VipsImage]),
568    Blob(&'a [u8]),
569    MutBlob(&'a mut Vec<u8>),
570    Target(&'a crate::VipsTarget),
571    Source(&'a crate::VipsSource),
572    Interpolate(&'a crate::interpolate::VipsInterpolate),
573}
574
575struct Pair<'a> {
576    input: bool,
577    name: String,
578    value: VipsValue<'a>,
579}
580
581impl<'a> Pair<'a> {
582    fn input(name: &str, value: VipsValue<'a>) -> Self {
583        Self {
584            input: true,
585            name: name.to_string(),
586            value,
587        }
588    }
589
590    fn output(name: &str, value: VipsValue<'a>) -> Self {
591        Self {
592            input: false,
593            name: name.to_string(),
594            value,
595        }
596    }
597}
598
599/// VOption, a list of name-value pairs
600#[derive(Default)]
601pub struct VOption<'a> {
602    options: Vec<Pair<'a>>,
603}
604
605impl<'a> VOption<'a> {
606    pub fn new() -> Self {
607        Self {
608            options: Vec::new(),
609        }
610    }
611}
612
613/// Set the value of a name-value pair of VOption
614pub trait Setter<'a, T> {
615    fn set(self, name: &str, value: T) -> VOption<'a>;
616    fn add(&mut self, name: &str, value: T);
617}
618
619// input bool
620impl<'a> Setter<'a, bool> for VOption<'a> {
621    fn set(mut self, name: &str, value: bool) -> VOption<'a> {
622        self.options
623            .push(
624                Pair::input(
625                    name,
626                    VipsValue::Bool(value),
627                ),
628            );
629        self
630    }
631    fn add(&mut self, name: &str, value: bool) {
632        self.options
633            .push(
634                Pair::input(
635                    name,
636                    VipsValue::Bool(value),
637                ),
638            );
639    }
640}
641
642// input i32
643impl<'a> Setter<'a, i32> for VOption<'a> {
644    fn set(mut self, name: &str, value: i32) -> VOption<'a> {
645        self.options
646            .push(
647                Pair::input(
648                    name,
649                    VipsValue::Int(value),
650                ),
651            );
652        self
653    }
654    fn add(&mut self, name: &str, value: i32) {
655        self.options
656            .push(
657                Pair::input(
658                    name,
659                    VipsValue::Int(value),
660                ),
661            );
662    }
663}
664
665// input u64
666impl<'a> Setter<'a, u64> for VOption<'a> {
667    fn set(mut self, name: &str, value: u64) -> VOption<'a> {
668        self.options
669            .push(
670                Pair::input(
671                    name,
672                    VipsValue::Uint(value),
673                ),
674            );
675        self
676    }
677    fn add(&mut self, name: &str, value: u64) {
678        self.options
679            .push(
680                Pair::input(
681                    name,
682                    VipsValue::Uint(value),
683                ),
684            );
685    }
686}
687
688// input f64
689impl<'a> Setter<'a, f64> for VOption<'a> {
690    fn set(mut self, name: &str, value: f64) -> VOption<'a> {
691        self.options
692            .push(
693                Pair::input(
694                    name,
695                    VipsValue::Double(value),
696                ),
697            );
698        self
699    }
700    fn add(&mut self, name: &str, value: f64) {
701        self.options
702            .push(
703                Pair::input(
704                    name,
705                    VipsValue::Double(value),
706                ),
707            );
708    }
709}
710
711// input &str
712impl<'a> Setter<'a, &'a str> for VOption<'a> {
713    fn set(mut self, name: &str, value: &'a str) -> VOption<'a> {
714        self.options
715            .push(
716                Pair::input(
717                    name,
718                    VipsValue::Str(value),
719                ),
720            );
721        self
722    }
723    fn add(&mut self, name: &str, value: &'a str) {
724        self.options
725            .push(
726                Pair::input(
727                    name,
728                    VipsValue::Str(value),
729                ),
730            );
731    }
732}
733
734impl<'a> Setter<'a, &'a String> for VOption<'a> {
735    fn set(mut self, name: &str, value: &'a String) -> VOption<'a> {
736        self.options
737            .push(
738                Pair::input(
739                    name,
740                    VipsValue::Str(value),
741                ),
742            );
743        self
744    }
745    fn add(&mut self, name: &str, value: &'a String) {
746        self.options
747            .push(
748                Pair::input(
749                    name,
750                    VipsValue::Str(value),
751                ),
752            );
753    }
754}
755
756// input c_char
757impl<'a> Setter<'a, *const c_char> for VOption<'a> {
758    fn set(mut self, name: &str, value: *const c_char) -> VOption<'a> {
759        self.options
760            .push(
761                Pair::input(
762                    name,
763                    VipsValue::CStr(value),
764                ),
765            );
766        self
767    }
768    fn add(&mut self, name: &str, value: *const c_char) {
769        self.options
770            .push(
771                Pair::input(
772                    name,
773                    VipsValue::CStr(value),
774                ),
775            );
776    }
777}
778
779// input VipsImage
780impl<'a> Setter<'a, &'a crate::VipsImage> for VOption<'a> {
781    fn set(mut self, name: &str, value: &'a crate::VipsImage) -> VOption<'a> {
782        self.options
783            .push(
784                Pair::input(
785                    name,
786                    VipsValue::Image(value),
787                ),
788            );
789        self
790    }
791    fn add(&mut self, name: &str, value: &'a crate::VipsImage) {
792        self.options
793            .push(
794                Pair::input(
795                    name,
796                    VipsValue::Image(value),
797                ),
798            );
799    }
800}
801
802// input &[i32]
803impl<'a> Setter<'a, &'a [i32]> for VOption<'a> {
804    fn set(mut self, name: &str, value: &'a [i32]) -> VOption<'a> {
805        self.options
806            .push(
807                Pair::input(
808                    name,
809                    VipsValue::IntArray(value),
810                ),
811            );
812        self
813    }
814    fn add(&mut self, name: &str, value: &'a [i32]) {
815        self.options
816            .push(
817                Pair::input(
818                    name,
819                    VipsValue::IntArray(value),
820                ),
821            );
822    }
823}
824
825impl<'a, const N: usize> Setter<'a, &'a [i32; N]> for VOption<'a> {
826    fn set(mut self, name: &str, value: &'a [i32; N]) -> VOption<'a> {
827        self.options
828            .push(
829                Pair::input(
830                    name,
831                    VipsValue::IntArray(value),
832                ),
833            );
834        self
835    }
836    fn add(&mut self, name: &str, value: &'a [i32; N]) {
837        self.options
838            .push(
839                Pair::input(
840                    name,
841                    VipsValue::IntArray(value),
842                ),
843            );
844    }
845}
846
847// input &[f64]
848impl<'a> Setter<'a, &'a [f64]> for VOption<'a> {
849    fn set(mut self, name: &str, value: &'a [f64]) -> VOption<'a> {
850        self.options
851            .push(
852                Pair::input(
853                    name,
854                    VipsValue::DoubleArray(value),
855                ),
856            );
857        self
858    }
859    fn add(&mut self, name: &str, value: &'a [f64]) {
860        self.options
861            .push(
862                Pair::input(
863                    name,
864                    VipsValue::DoubleArray(value),
865                ),
866            );
867    }
868}
869
870impl<'a, const N: usize> Setter<'a, &'a [f64; N]> for VOption<'a> {
871    fn set(mut self, name: &str, value: &'a [f64; N]) -> VOption<'a> {
872        self.options
873            .push(
874                Pair::input(
875                    name,
876                    VipsValue::DoubleArray(value),
877                ),
878            );
879        self
880    }
881    fn add(&mut self, name: &str, value: &'a [f64; N]) {
882        self.options
883            .push(
884                Pair::input(
885                    name,
886                    VipsValue::DoubleArray(value),
887                ),
888            );
889    }
890}
891
892// input &[VipsImage]
893impl<'a> Setter<'a, &'a [crate::VipsImage]> for VOption<'a> {
894    fn set(mut self, name: &str, value: &'a [crate::VipsImage]) -> VOption<'a> {
895        self.options
896            .push(
897                Pair::input(
898                    name,
899                    VipsValue::ImageArray(value),
900                ),
901            );
902        self
903    }
904    fn add(&mut self, name: &str, value: &'a [crate::VipsImage]) {
905        self.options
906            .push(
907                Pair::input(
908                    name,
909                    VipsValue::ImageArray(value),
910                ),
911            );
912    }
913}
914
915impl<'a, const N: usize> Setter<'a, &'a [crate::VipsImage; N]> for VOption<'a> {
916    fn set(mut self, name: &str, value: &'a [crate::VipsImage; N]) -> VOption<'a> {
917        self.options
918            .push(
919                Pair::input(
920                    name,
921                    VipsValue::ImageArray(value),
922                ),
923            );
924        self
925    }
926    fn add(&mut self, name: &str, value: &'a [crate::VipsImage; N]) {
927        self.options
928            .push(
929                Pair::input(
930                    name,
931                    VipsValue::ImageArray(value),
932                ),
933            );
934    }
935}
936
937// input blob
938impl<'a> Setter<'a, &'a [u8]> for VOption<'a> {
939    fn set(mut self, name: &str, value: &'a [u8]) -> VOption<'a> {
940        self.options
941            .push(
942                Pair::input(
943                    name,
944                    VipsValue::Blob(value),
945                ),
946            );
947        self
948    }
949    fn add(&mut self, name: &str, value: &'a [u8]) {
950        self.options
951            .push(
952                Pair::input(
953                    name,
954                    VipsValue::Blob(value),
955                ),
956            );
957    }
958}
959
960// input VipsTarget
961impl<'a> Setter<'a, &'a crate::VipsTarget> for VOption<'a> {
962    fn set(mut self, name: &str, value: &'a crate::VipsTarget) -> VOption<'a> {
963        self.options
964            .push(
965                Pair::input(
966                    name,
967                    VipsValue::Target(value),
968                ),
969            );
970        self
971    }
972    fn add(&mut self, name: &str, value: &'a crate::VipsTarget) {
973        self.options
974            .push(
975                Pair::input(
976                    name,
977                    VipsValue::Target(value),
978                ),
979            );
980    }
981}
982
983// input VipsSource
984impl<'a> Setter<'a, &'a crate::VipsSource> for VOption<'a> {
985    fn set(mut self, name: &str, value: &'a crate::VipsSource) -> VOption<'a> {
986        self.options
987            .push(
988                Pair::input(
989                    name,
990                    VipsValue::Source(value),
991                ),
992            );
993        self
994    }
995    fn add(&mut self, name: &str, value: &'a crate::VipsSource) {
996        self.options
997            .push(
998                Pair::input(
999                    name,
1000                    VipsValue::Source(value),
1001                ),
1002            );
1003    }
1004}
1005
1006// input VipsInterpolate
1007impl<'a> Setter<'a, &'a crate::interpolate::VipsInterpolate> for VOption<'a> {
1008    fn set(mut self, name: &str, value: &'a crate::interpolate::VipsInterpolate) -> VOption<'a> {
1009        self.options
1010            .push(
1011                Pair::input(
1012                    name,
1013                    VipsValue::Interpolate(value),
1014                ),
1015            );
1016        self
1017    }
1018    fn add(&mut self, name: &str, value: &'a crate::interpolate::VipsInterpolate) {
1019        self.options
1020            .push(
1021                Pair::input(
1022                    name,
1023                    VipsValue::Interpolate(value),
1024                ),
1025            );
1026    }
1027}
1028
1029// output bool
1030impl<'a> Setter<'a, &'a mut bool> for VOption<'a> {
1031    fn set(mut self, name: &str, value: &'a mut bool) -> VOption<'a> {
1032        self.options
1033            .push(
1034                Pair::output(
1035                    name,
1036                    VipsValue::MutBool(value),
1037                ),
1038            );
1039        self
1040    }
1041    fn add(&mut self, name: &str, value: &'a mut bool) {
1042        self.options
1043            .push(
1044                Pair::output(
1045                    name,
1046                    VipsValue::MutBool(value),
1047                ),
1048            );
1049    }
1050}
1051
1052// output i32
1053impl<'a> Setter<'a, &'a mut i32> for VOption<'a> {
1054    fn set(mut self, name: &str, value: &'a mut i32) -> VOption<'a> {
1055        self.options
1056            .push(
1057                Pair::output(
1058                    name,
1059                    VipsValue::MutInt(value),
1060                ),
1061            );
1062        self
1063    }
1064    fn add(&mut self, name: &str, value: &'a mut i32) {
1065        self.options
1066            .push(
1067                Pair::output(
1068                    name,
1069                    VipsValue::MutInt(value),
1070                ),
1071            );
1072    }
1073}
1074
1075// output f64
1076impl<'a> Setter<'a, &'a mut f64> for VOption<'a> {
1077    fn set(mut self, name: &str, value: &'a mut f64) -> VOption<'a> {
1078        self.options
1079            .push(
1080                Pair::output(
1081                    name,
1082                    VipsValue::MutDouble(value),
1083                ),
1084            );
1085        self
1086    }
1087    fn add(&mut self, name: &str, value: &'a mut f64) {
1088        self.options
1089            .push(
1090                Pair::output(
1091                    name,
1092                    VipsValue::MutDouble(value),
1093                ),
1094            );
1095    }
1096}
1097
1098// output VipsImage/mutable input VipsImage
1099impl<'a> Setter<'a, &'a mut crate::VipsImage> for VOption<'a> {
1100    fn set(mut self, name: &str, value: &'a mut crate::VipsImage) -> VOption<'a> {
1101        // If ctx is null, assume it is output image. Otherwise, it is mutable input image.
1102        let pair = if value
1103            .image
1104            .ctx
1105            .is_null()
1106        {
1107            Pair::output(
1108                name,
1109                VipsValue::MutImage(value),
1110            )
1111        } else {
1112            Pair::input(
1113                name,
1114                VipsValue::Image(value),
1115            )
1116        };
1117        self.options
1118            .push(pair);
1119
1120        self
1121    }
1122    fn add(&mut self, name: &str, value: &'a mut crate::VipsImage) {
1123        // If ctx is null, assume it is output image. Otherwise, it is mutable input image.
1124        let pair = if value
1125            .image
1126            .ctx
1127            .is_null()
1128        {
1129            Pair::output(
1130                name,
1131                VipsValue::MutImage(value),
1132            )
1133        } else {
1134            Pair::input(
1135                name,
1136                VipsValue::Image(value),
1137            )
1138        };
1139        self.options
1140            .push(pair);
1141    }
1142}
1143
1144// output Vec<f64>
1145impl<'a> Setter<'a, &'a mut Vec<f64>> for VOption<'a> {
1146    fn set(mut self, name: &str, value: &'a mut Vec<f64>) -> VOption<'a> {
1147        self.options
1148            .push(
1149                Pair::output(
1150                    name,
1151                    VipsValue::MutDoubleArray(value),
1152                ),
1153            );
1154        self
1155    }
1156    fn add(&mut self, name: &str, value: &'a mut Vec<f64>) {
1157        self.options
1158            .push(
1159                Pair::output(
1160                    name,
1161                    VipsValue::MutDoubleArray(value),
1162                ),
1163            );
1164    }
1165}
1166
1167// output Blob
1168impl<'a> Setter<'a, &'a mut Vec<u8>> for VOption<'a> {
1169    fn set(mut self, name: &str, value: &'a mut Vec<u8>) -> VOption<'a> {
1170        self.options
1171            .push(
1172                Pair::output(
1173                    name,
1174                    VipsValue::MutBlob(value),
1175                ),
1176            );
1177        self
1178    }
1179    fn add(&mut self, name: &str, value: &'a mut Vec<u8>) {
1180        self.options
1181            .push(
1182                Pair::output(
1183                    name,
1184                    VipsValue::MutBlob(value),
1185                ),
1186            );
1187    }
1188}