From cc29a13cc7b4f900c01dd4007eaf164b7fef4618 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 11 Jun 2018 17:04:29 +0100 Subject: [PATCH] revise composite position code slightly - move x/y into composite and out of composite base, have separate x/y int params for composite2 - upsize later for a small speed improvement - doc comment - note in changelog see https://github.com/jcupitt/libvips/pull/934 --- ChangeLog | 1 + libvips/conversion/composite.cpp | 191 ++++++++++++++++++++----------- libvips/conversion/conversion.c | 12 +- 3 files changed, 133 insertions(+), 71 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8dd061f9..f333703a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,7 @@ - pdfload has a option for background - vips7 C++ interface defaults off - make members, getters and operators "const" in cpp API +- composite has params for x/y position of sub-images [medakk] 12/3/18 started 8.6.4 - better fitting of fonts with overhanging edges [AdriĆ ] diff --git a/libvips/conversion/composite.cpp b/libvips/conversion/composite.cpp index 7dfa922d..59e4687d 100644 --- a/libvips/conversion/composite.cpp +++ b/libvips/conversion/composite.cpp @@ -7,6 +7,8 @@ * 30/1/18 * - remove number of images limit * - allow one mode ... reused for all joins + * 11/8/18 [medakk] + * - x/y params let you position images */ /* @@ -87,14 +89,6 @@ typedef struct _VipsCompositeBase { */ VipsArrayInt *mode; - /* For N input images, N - 1 x coordinates. - */ - VipsArrayInt *x; - - /* For N input images, N - 1 y coordinates. - */ - VipsArrayInt *y; - /* Compositing space. This defaults to RGB, or B_W if we only have * G and GA inputs. */ @@ -118,6 +112,12 @@ typedef struct _VipsCompositeBase { */ double max_band[MAX_BANDS + 1]; + /* The x and y positions for each image in the stack. There are n - 1 + * of these, since image 0 is always positioned at (0, 0). + */ + int *x_offset; + int *y_offset; + #ifdef HAVE_VECTOR_ARITH /* max_band as a vector, for the RGBA case. */ @@ -148,14 +148,6 @@ vips_composite_base_dispose( GObject *gobject ) vips_area_unref( (VipsArea *) composite->mode ); composite->mode = NULL; } - if( composite->x ) { - vips_area_unref( (VipsArea *) composite->x ); - composite->x = NULL; - } - if( composite->y ) { - vips_area_unref( (VipsArea *) composite->y ); - composite->y = NULL; - } G_OBJECT_CLASS( vips_composite_base_parent_class )->dispose( gobject ); } @@ -1074,44 +1066,8 @@ vips_composite_base_build( VipsObject *object ) } } - if( vips_object_argument_isset( object, "x" ) ) { - if( composite->x->area.n != composite->n - 1 ) { - vips_error( klass->nickname, _( "must be %d x coordinates" ), - composite->n - 1 ); - return( -1 ); - } - } - - if( vips_object_argument_isset( object, "y" ) ) { - if( composite->y->area.n != composite->n - 1 ) { - vips_error( klass->nickname, _( "must be %d y coordinates" ), - composite->n - 1 ); - return( -1 ); - } - } - in = (VipsImage **) composite->in->area.data; - if( vips_object_argument_isset( object, "x" ) && vips_object_argument_isset( object, "y" ) ) { - int width, height; - - width = vips_image_get_width( in[0] ); - height = vips_image_get_height( in[0] ); - - int *x_offsets = (int *) composite->x->area.data; - int *y_offsets = (int *) composite->y->area.data; - - for( int i = 1; i < composite->n; i++ ) { - VipsImage *e; - - if( vips_embed( in[i], &e, x_offsets[i - 1], y_offsets[i - 1], width, height, NULL ) ) - return( -1 ); - - g_object_unref( in[i] ); - in[i] = e; - } - } - decode = (VipsImage **) vips_object_local_array( object, composite->n ); for( int i = 0; i < composite->n; i++ ) if( vips_image_decode( in[i], &decode[i] ) ) @@ -1210,13 +1166,43 @@ vips_composite_base_build( VipsObject *object ) composite->max_band_vec[b] = composite->max_band[b]; #endif /*HAVE_VECTOR_ARITH*/ - /* Transform the input images to match in size and format. We may have + /* Transform the input images to match format. We may have * mixed float and double, for example. */ format = (VipsImage **) vips_object_local_array( object, composite->n ); + if( vips__formatalike_vec( in, format, composite->n ) ) + return( -1 ); + in = format; + + /* Position all images, if x/y is set. + */ + if( composite->x_offset && + composite->y_offset ) { + int width = vips_image_get_width( in[0] ); + int height = vips_image_get_height( in[0] ); + VipsImage **position = (VipsImage **) + vips_object_local_array( object, composite->n ); + + /* The zero image does not move. + */ + g_object_ref( in[0] ); + position[0] = in[0]; + + for( int i = 1; i < composite->n; i++ ) + if( vips_embed( in[i], &position[i], + composite->x_offset[i - 1], + composite->y_offset[i - 1], + width, height, NULL ) ) + return( -1 ); + + in = position; + } + + /* Transform the input images to match in size. They can be mismatched + * if there was no supplied x/y. + */ size = (VipsImage **) vips_object_local_array( object, composite->n ); - if( vips__formatalike_vec( in, format, composite->n ) || - vips__sizealike_vec( format, size, composite->n ) ) + if( vips__sizealike_vec( in, size, composite->n ) ) return( -1 ); in = size; @@ -1267,20 +1253,6 @@ vips_composite_base_class_init( VipsCompositeBaseClass *klass ) G_STRUCT_OFFSET( VipsCompositeBase, premultiplied ), FALSE ); - VIPS_ARG_BOXED( klass, "x", 12, - _( "x coordinates" ), - _( "Array of x coordinates to join at" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsCompositeBase, x ), - VIPS_TYPE_ARRAY_INT ); - - VIPS_ARG_BOXED( klass, "y", 13, - _( "y coordinates" ), - _( "Array of y coordinates to join at" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsCompositeBase, y ), - VIPS_TYPE_ARRAY_INT ); - } static void @@ -1292,6 +1264,14 @@ vips_composite_base_init( VipsCompositeBase *composite ) typedef struct _VipsComposite { VipsCompositeBase parent_instance; + /* For N input images, N - 1 x coordinates. + */ + VipsArrayInt *x; + + /* For N input images, N - 1 y coordinates. + */ + VipsArrayInt *y; + } VipsComposite; typedef VipsCompositeBaseClass VipsCompositeClass; @@ -1302,6 +1282,44 @@ extern "C" { G_DEFINE_TYPE( VipsComposite, vips_composite, vips_composite_base_get_type() ); } +static int +vips_composite_build( VipsObject *object ) +{ + VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object ); + VipsCompositeBase *base = (VipsCompositeBase *) object; + VipsComposite *composite = (VipsComposite *) object; + + int n; + + n = 0; + if( vips_object_argument_isset( object, "in" ) ) + n = base->in->area.n; + + if( vips_object_argument_isset( object, "x" ) ) { + if( composite->x->area.n != n - 1 ) { + vips_error( klass->nickname, + _( "must be %d x coordinates" ), n - 1 ); + return( -1 ); + } + base->x_offset = (int *) composite->x->area.data; + } + + if( vips_object_argument_isset( object, "y" ) ) { + if( composite->y->area.n != n - 1 ) { + vips_error( klass->nickname, + _( "must be %d y coordinates" ), n - 1 ); + return( -1 ); + } + base->y_offset = (int *) composite->y->area.data; + } + + if( VIPS_OBJECT_CLASS( vips_composite_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + static void vips_composite_class_init( VipsCompositeClass *klass ) { @@ -1317,7 +1335,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_base_build; + vobject_class->build = vips_composite_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL; @@ -1335,6 +1353,20 @@ vips_composite_class_init( VipsCompositeClass *klass ) G_STRUCT_OFFSET( VipsCompositeBase, mode ), VIPS_TYPE_ARRAY_INT ); + VIPS_ARG_BOXED( klass, "x", 4, + _( "x coordinates" ), + _( "Array of x coordinates to join at" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsComposite, x ), + VIPS_TYPE_ARRAY_INT ); + + VIPS_ARG_BOXED( klass, "y", 5, + _( "y coordinates" ), + _( "Array of y coordinates to join at" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsComposite, y ), + VIPS_TYPE_ARRAY_INT ); + } static void @@ -1381,6 +1413,8 @@ typedef struct _VipsComposite2 { VipsImage *base; VipsImage *overlay; VipsBlendMode mode; + int x; + int y; } VipsComposite2; @@ -1412,6 +1446,9 @@ vips_composite2_build( VipsObject *object ) base->mode = vips_array_int_new( mode, 1 ); } + base->x_offset = &composite2->x; + base->y_offset = &composite2->y; + if( VIPS_OBJECT_CLASS( vips_composite2_parent_class )->build( object ) ) return( -1 ); @@ -1456,6 +1493,20 @@ vips_composite2_class_init( VipsCompositeClass *klass ) G_STRUCT_OFFSET( VipsComposite2, mode ), VIPS_TYPE_BLEND_MODE, VIPS_BLEND_MODE_OVER ); + VIPS_ARG_INT( klass, "x", 4, + _( "x" ), + _( "x position of overlay" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsComposite2, x ), + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); + + VIPS_ARG_INT( klass, "y", 5, + _( "y" ), + _( "y position of overlay" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsComposite2, y ), + -VIPS_MAX_COORD, VIPS_MAX_COORD, 0 ); + } static void diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index b6cdd9fc..66c51547 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -81,6 +81,8 @@ * * * @compositing_space: #VipsInterpretation to composite in * * @premultiplied: %gboolean, images are already premultiplied + * * @x: #VipsArrayInt, position of subimages + * * @y: #VipsArrayInt, position of subimages * * Composite an array of images together. * @@ -106,7 +108,8 @@ * added to any input missing an alpha. * * The images do not need to match in size or format. They will be expanded to - * the smallest common size and format in the usual way. + * the smallest common size and format in the usual way. Images are positioned + * using the @x and @y parameters, if set. * * Image are normally treated as unpremultiplied, so this operation can be used * directly on PNG images. If your images have been through vips_premultiply(), @@ -125,6 +128,13 @@ * @mode: composite with this blend mode * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * * @compositing_space: #VipsInterpretation to composite in + * * @premultiplied: %gboolean, images are already premultiplied + * * @x: %gint, position of overlay + * * @y: %gint, position of overlay + * * Composite @overlay on top of @base with @mode. See vips_composite(). * * Returns: 0 on success, -1 on error