diff --git a/libvips/conversion/composite.cpp b/libvips/conversion/composite.cpp index 174a59ce..675680db 100644 --- a/libvips/conversion/composite.cpp +++ b/libvips/conversion/composite.cpp @@ -2,6 +2,8 @@ * * 25/9/17 * - from bandjoin.c + * 30/11/17 + * - add composite2 class, to make a nice CLI interface */ /* @@ -91,7 +93,7 @@ * VIPS_BLEND_MODE_DIFFERENCE: * VIPS_BLEND_MODE_EXCLUSION: * - * The various Porter-Duff and PDF blend modes. See vips_composite(), + * The various Porter-Duff and PDF blend modes. See vips_composite_base(), * for example. * * The Cairo docs have a nice explanation of all the blend modes: @@ -107,7 +109,7 @@ typedef float v4f __attribute__((vector_size(4 * sizeof(float)))); #endif /*HAVE_VECTOR_ARITH*/ -typedef struct _VipsComposite { +typedef struct _VipsCompositeBase { VipsConversion parent_instance; /* The input images. @@ -147,20 +149,38 @@ typedef struct _VipsComposite { v4f max_band_vec; #endif /*HAVE_VECTOR_ARITH*/ -} VipsComposite; +} VipsCompositeBase; -typedef VipsConversionClass VipsCompositeClass; +typedef VipsConversionClass VipsCompositeBaseClass; /* We need C linkage for this. */ extern "C" { -G_DEFINE_TYPE( VipsComposite, vips_composite, VIPS_TYPE_CONVERSION ); +G_DEFINE_ABSTRACT_TYPE( VipsCompositeBase, vips_composite_base, + VIPS_TYPE_CONVERSION ); +} + +static void +vips_composite_base_dispose( GObject *gobject ) +{ + VipsCompositeBase *composite = (VipsCompositeBase *) gobject; + + if( composite->in ) { + vips_area_unref( (VipsArea *) composite->in ); + composite->in = NULL; + } + if( composite->mode ) { + vips_area_unref( (VipsArea *) composite->mode ); + composite->mode = NULL; + } + + G_OBJECT_CLASS( vips_composite_base_parent_class )->dispose( gobject ); } /* For each of the supported interpretations, the maximum value of each band. */ static int -vips_composite_max_band( VipsComposite *composite, double *max_band ) +vips_composite_base_max_band( VipsCompositeBase *composite, double *max_band ) { double max_alpha; int b; @@ -263,7 +283,7 @@ vips_composite_max_band( VipsComposite *composite, double *max_band ) */ template static void -vips_composite_blend( VipsComposite *composite, +vips_composite_base_blend( VipsCompositeBase *composite, VipsBlendMode mode, double * restrict B, T * restrict p ) { const int bands = composite->bands; @@ -495,7 +515,7 @@ vips_composite_blend( VipsComposite *composite, */ template static void -vips_composite_blend3( VipsComposite *composite, +vips_composite_base_blend3( VipsCompositeBase *composite, VipsBlendMode mode, v4f &B, T * restrict p ) { v4f A; @@ -702,7 +722,7 @@ vips_composite_blend3( VipsComposite *composite, */ template static void -vips_combine_pixels( VipsComposite *composite, VipsPel *q, VipsPel **p ) +vips_combine_pixels( VipsCompositeBase *composite, VipsPel *q, VipsPel **p ) { VipsBlendMode *m = (VipsBlendMode *) composite->mode->area.data; int n = composite->n; @@ -724,7 +744,7 @@ vips_combine_pixels( VipsComposite *composite, VipsPel *q, VipsPel **p ) B[b] *= aB; for( int i = 1; i < n; i++ ) - vips_composite_blend( composite, m[i - 1], B, tp[i] ); + vips_composite_base_blend( composite, m[i - 1], B, tp[i] ); /* Unpremultiply, if necessary. */ @@ -761,7 +781,7 @@ vips_combine_pixels( VipsComposite *composite, VipsPel *q, VipsPel **p ) */ template static void -vips_combine_pixels3( VipsComposite *composite, VipsPel *q, VipsPel **p ) +vips_combine_pixels3( VipsCompositeBase *composite, VipsPel *q, VipsPel **p ) { VipsBlendMode *m = (VipsBlendMode *) composite->mode->area.data; int n = composite->n; @@ -787,7 +807,7 @@ vips_combine_pixels3( VipsComposite *composite, VipsPel *q, VipsPel **p ) } for( int i = 1; i < n; i++ ) - vips_composite_blend3( composite, m[i - 1], B, tp[i] ); + vips_composite_base_blend3( composite, m[i - 1], B, tp[i] ); /* Unpremultiply, if necessary. */ @@ -822,18 +842,18 @@ vips_combine_pixels3( VipsComposite *composite, VipsPel *q, VipsPel **p ) #endif /*HAVE_VECTOR_ARITH*/ static int -vips_composite_gen( VipsRegion *output_region, +vips_composite_base_gen( VipsRegion *output_region, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion **input_regions = (VipsRegion **) seq; - VipsComposite *composite = (VipsComposite *) b; + VipsCompositeBase *composite = (VipsCompositeBase *) b; VipsRect *r = &output_region->valid; int ps = VIPS_IMAGE_SIZEOF_PEL( output_region->im ); if( vips_reorder_prepare_many( output_region->im, input_regions, r ) ) return( -1 ); - VIPS_GATE_START( "vips_composite_gen: work" ); + VIPS_GATE_START( "vips_composite_base_gen: work" ); for( int y = 0; y < r->height; y++ ) { VipsPel *p[MAX_INPUT_IMAGES]; @@ -927,17 +947,17 @@ vips_composite_gen( VipsRegion *output_region, } } - VIPS_GATE_STOP( "vips_composite_gen: work" ); + VIPS_GATE_STOP( "vips_composite_base_gen: work" ); return( 0 ); } static int -vips_composite_build( VipsObject *object ) +vips_composite_base_build( VipsObject *object ) { VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object ); VipsConversion *conversion = VIPS_CONVERSION( object ); - VipsComposite *composite = (VipsComposite *) object; + VipsCompositeBase *composite = (VipsCompositeBase *) object; VipsImage **in; VipsImage **decode; @@ -946,7 +966,8 @@ vips_composite_build( VipsObject *object ) VipsImage **size; VipsBlendMode *mode; - if( VIPS_OBJECT_CLASS( vips_composite_parent_class )->build( object ) ) + if( VIPS_OBJECT_CLASS( vips_composite_base_parent_class )-> + build( object ) ) return( -1 ); composite->n = composite->in->area.n; @@ -1064,7 +1085,7 @@ vips_composite_build( VipsObject *object ) /* Set the max for each band now we know bands and compositing space. */ - if( vips_composite_max_band( composite, composite->max_band ) ) { + if( vips_composite_base_max_band( composite, composite->max_band ) ) { vips_error( klass->nickname, "%s", _( "unsupported compositing space" ) ); return( -1 ); @@ -1093,13 +1114,67 @@ vips_composite_build( VipsObject *object ) return( -1 ); if( vips_image_generate( conversion->out, - vips_start_many, vips_composite_gen, vips_stop_many, + vips_start_many, vips_composite_base_gen, vips_stop_many, in, composite ) ) return( -1 ); return( 0 ); } +static void +vips_composite_base_class_init( VipsCompositeBaseClass *klass ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( klass ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( klass ); + + VIPS_DEBUG_MSG( "vips_composite_base_class_init\n" ); + + gobject_class->dispose = vips_composite_base_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "composite_base"; + vobject_class->description = _( "blend images together" ); + vobject_class->build = vips_composite_base_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_ENUM( klass, "compositing_space", 10, + _( "Compositing space" ), + _( "Composite images in this colour space" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompositeBase, compositing_space ), + VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB ); + + VIPS_ARG_BOOL( klass, "premultiplied", 11, + _( "Premultiplied" ), + _( "Images have premultiplied alpha" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompositeBase, premultiplied ), + FALSE ); + +} + +static void +vips_composite_base_init( VipsCompositeBase *composite ) +{ + composite->compositing_space = VIPS_INTERPRETATION_sRGB; +} + +typedef struct _VipsComposite { + VipsCompositeBase parent_instance; + +} VipsComposite; + +typedef VipsCompositeBaseClass VipsCompositeClass; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsComposite, vips_composite, vips_composite_base_get_type() ); +} + static void vips_composite_class_init( VipsCompositeClass *klass ) { @@ -1115,7 +1190,7 @@ vips_composite_class_init( VipsCompositeClass *klass ) vobject_class->nickname = "composite"; vobject_class->description = _( "blend an array of images with an array of blend modes" ); - vobject_class->build = vips_composite_build; + vobject_class->build = vips_composite_base_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL; @@ -1123,36 +1198,21 @@ vips_composite_class_init( VipsCompositeClass *klass ) _( "Inputs" ), _( "Array of input images" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsComposite, in ), + G_STRUCT_OFFSET( VipsCompositeBase, in ), VIPS_TYPE_ARRAY_IMAGE ); VIPS_ARG_BOXED( klass, "mode", 3, _( "Blend modes" ), _( "Array of VipsBlendMode to join with" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsComposite, mode ), + G_STRUCT_OFFSET( VipsCompositeBase, mode ), VIPS_TYPE_ARRAY_INT ); - VIPS_ARG_ENUM( klass, "compositing_space", 10, - _( "Compositing space" ), - _( "Composite images in this colour space" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsComposite, compositing_space ), - VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB ); - - VIPS_ARG_BOOL( klass, "premultiplied", 11, - _( "Premultiplied" ), - _( "Images have premultiplied alpha" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsComposite, premultiplied ), - FALSE ); - } static void vips_composite_init( VipsComposite *composite ) { - composite->compositing_space = VIPS_INTERPRETATION_sRGB; } static int @@ -1232,6 +1292,94 @@ vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) return( result ); } +typedef struct _VipsComposite2 { + VipsCompositeBase parent_instance; + + VipsImage *base; + VipsImage *overlay; + VipsBlendMode mode; + +} VipsComposite2; + +typedef VipsCompositeBaseClass VipsComposite2Class; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsComposite2, vips_composite2, vips_composite_base_get_type() ); +} + +static int +vips_composite2_build( VipsObject *object ) +{ + VipsCompositeBase *base = (VipsCompositeBase *) object; + VipsComposite2 *composite2 = (VipsComposite2 *) object; + + if( composite2->overlay && + composite2->base ) { + VipsImage *in[3]; + int mode[1]; + + in[0] = composite2->base; + in[1] = composite2->overlay; + in[2] = NULL; + base->in = vips_array_image_new( in, 2 ); + + mode[0] = (int) composite2->mode; + base->mode = vips_array_int_new( mode, 1 ); + } + + if( VIPS_OBJECT_CLASS( vips_composite2_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_composite2_class_init( VipsCompositeClass *klass ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( klass ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( klass ); + + VIPS_DEBUG_MSG( "vips_composite_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "composite2"; + vobject_class->description = + _( "blend a pair of images with a blend mode" ); + vobject_class->build = vips_composite2_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( klass, "base", 0, + _( "Base" ), + _( "Base image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsComposite2, base ) ); + + VIPS_ARG_IMAGE( klass, "overlay", 1, + _( "Overlay" ), + _( "Overlay image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsComposite2, overlay ) ); + + VIPS_ARG_ENUM( klass, "mode", 3, + _( "Blend mode" ), + _( "VipsBlendMode to join with" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsComposite2, mode ), + VIPS_TYPE_BLEND_MODE, VIPS_BLEND_MODE_OVER ); + +} + +static void +vips_composite2_init( VipsComposite2 *composite2 ) +{ +} + /** * vips_composite2: (method) * @base: first input image @@ -1240,7 +1388,7 @@ vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) * @mode: composite with this blend mode * @...: %NULL-terminated list of optional named arguments * - * Composite @overlay on top of @base with @mode. See vips_composite(). + * Composite @overlay on top of @base with @mode. See vips_composite_base(). * * Returns: 0 on success, -1 on error */ @@ -1250,16 +1398,11 @@ vips_composite2( VipsImage *base, VipsImage *overlay, VipsImage **out, { va_list ap; int result; - VipsImage *imagev[2]; - int modev[1]; - - imagev[0] = base; - imagev[1] = overlay; - modev[0] = mode; va_start( ap, mode ); - result = vips_compositev( imagev, out, 2, modev, ap ); + result = vips_call_split( "composite2", ap, base, overlay, out, mode ); va_end( ap ); return( result ); } + diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 893b4088..d0798508 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -291,6 +291,7 @@ vips_conversion_operation_init( void ) extern GType vips_falsecolour_get_type( void ); extern GType vips_gamma_get_type( void ); extern GType vips_composite_get_type( void ); + extern GType vips_composite2_get_type( void ); vips_copy_get_type(); vips_tile_cache_get_type(); @@ -340,4 +341,5 @@ vips_conversion_operation_init( void ) vips_falsecolour_get_type(); vips_gamma_get_type(); vips_composite_get_type(); + vips_composite2_get_type(); } diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index edd3a81e..a7db19c7 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -269,7 +269,7 @@ int vips_unpremultiply( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) __attribute__((sentinel)); -int vips_composite2( VipsImage *in1, VipsImage *in2, VipsImage **out, +int vips_composite2( VipsImage *base, VipsImage *overlay, VipsImage **out, VipsBlendMode mode1, ... ) __attribute__((sentinel));