From 7b19ad8dc1b3e745b2053530d659148bf22033b4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 6 Dec 2018 18:28:55 +0000 Subject: [PATCH] composite subimage positioning almost done --x fails though? odd vips composite2 Gugg_coloured.jpg PNG_transparency.png x.jpg over --x 600 --y 100 --x causes the dice to clip, but not translate --- ChangeLog | 1 + libvips/conversion/composite-old.cpp | 1527 -------------------------- libvips/conversion/composite.cpp | 601 +++++----- libvips/conversion/insert.c | 2 +- libvips/include/vips/rect.h | 1 + libvips/iofuncs/rect.c | 19 + libvips/iofuncs/region.c | 9 +- 7 files changed, 327 insertions(+), 1833 deletions(-) delete mode 100644 libvips/conversion/composite-old.cpp diff --git a/ChangeLog b/ChangeLog index 108e7767..f923ea93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ - implement shrink-on-load for tiff pyramids [jcupitt] - added vips_image_set_blob_copy() [jcupitt] - don't stop composite on first non-transparent image [felixbuenemann] +- add vips_rect_overlapsrect() 21/11/18 started 8.7.2 - more info output for temp files to help diagnose problems diff --git a/libvips/conversion/composite-old.cpp b/libvips/conversion/composite-old.cpp deleted file mode 100644 index 1e66eb32..00000000 --- a/libvips/conversion/composite-old.cpp +++ /dev/null @@ -1,1527 +0,0 @@ -/* composite an array of images with PDF operators - * - * 25/9/17 - * - from bandjoin.c - * 30/11/17 - * - add composite2 class, to make a nice CLI interface - * 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 - * 27/11/18 - * - don't stop on first non-transparent image [felixbuenemann] - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* -#define VIPS_DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#if _MSC_VER -#include -#else -#include -#endif -#include - -#include -#include -#include - -#include "pconversion.h" - -/* Maximum number of image bands. - */ -#define MAX_BANDS (64) - -/* Uncomment to disable the vector path ... handy for debugging. -#undef HAVE_VECTOR_ARITH - */ - -/* We have a vector path with gcc's vector attr. - */ -#ifdef HAVE_VECTOR_ARITH -/* A vector of four floats. - */ -typedef float v4f __attribute__((vector_size(4 * sizeof(float)))); -#endif /*HAVE_VECTOR_ARITH*/ - -typedef struct _VipsCompositeBase { - VipsConversion parent_instance; - - /* The input images. - */ - VipsArrayImage *in; - - /* For N input images, 1 blend mode or N - 1 blend modes. - */ - VipsArrayInt *mode; - - /* Compositing space. This defaults to RGB, or B_W if we only have - * G and GA inputs. - */ - VipsInterpretation compositing_space; - - /* Set if the input images have already been premultiplied. - */ - gboolean premultiplied; - - /* 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). Set by - * subclasses. Can be NULL. - */ - int *x_offset; - int *y_offset; - - /* The number of non-alpha bands we are blending. - */ - int bands; - - /* The maximum value for each band, set from the image interpretation. - * This is used to scale each band to 0 - 1. - */ - double max_band[MAX_BANDS + 1]; - -#ifdef HAVE_VECTOR_ARITH - /* max_band as a vector, for the RGBA case. - */ - v4f max_band_vec; -#endif /*HAVE_VECTOR_ARITH*/ - -} VipsCompositeBase; - -typedef VipsConversionClass VipsCompositeBaseClass; - -/* We need C linkage for this. - */ -extern "C" { -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 ); -} - -/* Our sequence value. - */ -typedef struct { - VipsCompositeBase *composite; - - /* Set of input regions. - */ - VipsRegion **ir; - - /* For each input, an input pointer. - */ - VipsPel **p; - -} VipsCompositeSequence; - -static int -vips_composite_stop( void *vseq, void *a, void *b ) -{ - VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; - - if( seq->ir ) { - int i; - - for( i = 0; seq->ir[i]; i++ ) - VIPS_UNREF( seq->ir[i] ); - VIPS_FREE( seq->ir ); - } - - VIPS_FREE( seq->p ); - - VIPS_FREE( seq ); - - return( 0 ); -} - -static void * -vips_composite_start( VipsImage *out, void *a, void *b ) -{ - VipsImage **in = (VipsImage **) a; - VipsCompositeBase *composite = (VipsCompositeBase *) b; - - VipsCompositeSequence *seq; - int i, n; - - if( !(seq = VIPS_NEW( NULL, VipsCompositeSequence )) ) - return( NULL ); - - seq->composite = composite; - seq->ir = NULL; - seq->p = NULL; - - /* How many images? - */ - for( n = 0; in[n]; n++ ) - ; - - /* Allocate space for region array. - */ - if( !(seq->ir = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { - vips_composite_stop( seq, NULL, NULL ); - return( NULL ); - } - - /* Create a set of regions. - */ - for( i = 0; i < n; i++ ) - if( !(seq->ir[i] = vips_region_new( in[i] )) ) { - vips_composite_stop( seq, NULL, NULL ); - return( NULL ); - } - - seq->ir[n] = NULL; - - /* Input pointers. - */ - if( !(seq->p = VIPS_ARRAY( NULL, n + 1, VipsPel * )) ) { - vips_composite_stop( seq, NULL, NULL ); - return( NULL ); - } - - return( seq ); -} - -/* For each of the supported interpretations, the maximum value of each band. - */ -static int -vips_composite_base_max_band( VipsCompositeBase *composite, double *max_band ) -{ - double max_alpha; - int b; - - max_alpha = 255.0; - if( composite->compositing_space == VIPS_INTERPRETATION_GREY16 || - composite->compositing_space == VIPS_INTERPRETATION_RGB16 ) - max_alpha = 65535.0; - - for( b = 0; b <= composite->bands; b++ ) - max_band[b] = max_alpha; - - switch( composite->compositing_space ) { - case VIPS_INTERPRETATION_XYZ: - max_band[0] = VIPS_D65_X0; - max_band[1] = VIPS_D65_Y0; - max_band[2] = VIPS_D65_Z0; - break; - - case VIPS_INTERPRETATION_LAB: - max_band[0] = 100; - max_band[1] = 128; - max_band[2] = 128; - break; - - case VIPS_INTERPRETATION_LCH: - max_band[0] = 100; - max_band[1] = 128; - max_band[2] = 360; - break; - - case VIPS_INTERPRETATION_CMC: - max_band[0] = 100; - max_band[1] = 128; - max_band[2] = 360; - break; - - case VIPS_INTERPRETATION_scRGB: - max_band[0] = 1; - max_band[1] = 1; - max_band[2] = 1; - break; - - case VIPS_INTERPRETATION_sRGB: - max_band[0] = 255; - max_band[1] = 255; - max_band[2] = 255; - break; - - case VIPS_INTERPRETATION_HSV: - max_band[0] = 255; - max_band[1] = 255; - max_band[2] = 255; - break; - - case VIPS_INTERPRETATION_RGB16: - max_band[0] = 65535; - max_band[1] = 65535; - max_band[2] = 65535; - break; - - case VIPS_INTERPRETATION_GREY16: - max_band[0] = 65535; - break; - - case VIPS_INTERPRETATION_YXY: - max_band[0] = 100; - max_band[1] = 1; - max_band[2] = 1; - break; - - case VIPS_INTERPRETATION_B_W: - max_band[0] = 255; - break; - - default: - return( -1 ); - } - - return( 0 ); -} - -/* Cairo naming conventions: - * - * aR alpha of result - * aA alpha of source A (the new pixel) - * aB alpha of source B (the thing we accumulate) - * xR colour band of result - * xA colour band of source A - * xB colour band of source B - */ - -/* A is the new pixel coming in, of any non-complex type T. - * - * We must scale incoming pixels to 0 - 1 by dividing by the scale[] vector. - * - * If premultipled is not set, we premultiply incoming pixels before blending. - * - * B is the double pixel we are accumulating. - */ -template -static void -vips_composite_base_blend( VipsCompositeBase *composite, - VipsBlendMode mode, double * restrict B, T * restrict p ) -{ - const int bands = composite->bands; - - double A[MAX_BANDS + 1]; - double aA; - double aB; - double aR; - double t1; - double t2; - double t3; - double f[MAX_BANDS + 1]; - - /* Load and scale the pixel to 0 - 1. - */ - for( int b = 0; b <= bands; b++ ) - A[b] = p[b] / composite->max_band[b]; - /* Not necessary, but it stops a compiler warning. - */ - for( int b = bands + 1; b < MAX_BANDS + 1; b++ ) - A[b] = 0.0; - - aA = A[bands]; - aB = B[bands]; - - /* We may need to premultiply A. - */ - if( !composite->premultiplied ) - for( int b = 0; b < bands; b++ ) - A[b] *= aA; - - switch( mode ) { - case VIPS_BLEND_MODE_CLEAR: - aR = 0; - for( int b = 0; b < bands; b++ ) - B[b] = 0; - break; - - case VIPS_BLEND_MODE_SOURCE: - aR = aA; - for( int b = 0; b < bands; b++ ) - B[b] = A[b]; - break; - - case VIPS_BLEND_MODE_OVER: - aR = aA + aB * (1 - aA); - t1 = 1 - aA; - for( int b = 0; b < bands; b++ ) - B[b] = A[b] + t1 * B[b]; - break; - - case VIPS_BLEND_MODE_IN: - aR = aA * aB; - for( int b = 0; b < bands; b++ ) - B[b] = A[b]; - break; - - case VIPS_BLEND_MODE_OUT: - aR = aA * (1 - aB); - for( int b = 0; b < bands; b++ ) - B[b] = A[b]; - break; - - case VIPS_BLEND_MODE_ATOP: - aR = aB; - t1 = 1 - aA; - for( int b = 0; b < bands; b++ ) - B[b] = A[b] + t1 * B[b]; - break; - - case VIPS_BLEND_MODE_DEST: - aR = aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_OVER: - aR = aB + aA * (1 - aB); - t1 = 1 - aB; - for( int b = 0; b < bands; b++ ) - B[b] = B[b] + t1 * A[b]; - break; - - case VIPS_BLEND_MODE_DEST_IN: - aR = aA * aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_OUT: - aR = (1 - aA) * aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_ATOP: - aR = aA; - t1 = 1 - aA; - for( int b = 0; b < bands; b++ ) - B[b] = t1 * A[b] + B[b]; - break; - - case VIPS_BLEND_MODE_XOR: - aR = aA + aB - 2 * aA * aB; - t1 = 1 - aB; - t2 = 1 - aA; - for( int b = 0; b < bands; b++ ) - B[b] = t1 * A[b] + t2 * B[b]; - break; - - case VIPS_BLEND_MODE_ADD: - aR = VIPS_MIN( 1, aA + aB ); - for( int b = 0; b < bands; b++ ) - B[b] = A[b] + B[b]; - break; - - case VIPS_BLEND_MODE_SATURATE: - aR = VIPS_MIN( 1, aA + aB ); - t1 = VIPS_MIN( aA, 1 - aB ); - for( int b = 0; b < bands; b++ ) - B[b] = t1 * A[b] + B[b]; - break; - - default: - /* The PDF modes are a bit different. - */ - aR = aA + aB * (1 - aA); - - switch( mode ) { - case VIPS_BLEND_MODE_MULTIPLY: - for( int b = 0; b < bands; b++ ) - f[b] = A[b] * B[b]; - break; - - case VIPS_BLEND_MODE_SCREEN: - for( int b = 0; b < bands; b++ ) - f[b] = A[b] + B[b] - A[b] * B[b]; - break; - - case VIPS_BLEND_MODE_OVERLAY: - for( int b = 0; b < bands; b++ ) - if( B[b] <= 0.5 ) - f[b] = 2 * A[b] * B[b]; - else - f[b] = 1 - 2 * (1 - A[b]) * (1 - B[b]); - break; - - case VIPS_BLEND_MODE_DARKEN: - for( int b = 0; b < bands; b++ ) - f[b] = VIPS_MIN( A[b], B[b] ); - break; - - case VIPS_BLEND_MODE_LIGHTEN: - for( int b = 0; b < bands; b++ ) - f[b] = VIPS_MAX( A[b], B[b] ); - break; - - case VIPS_BLEND_MODE_COLOUR_DODGE: - for( int b = 0; b < bands; b++ ) - if( A[b] < 1 ) - f[b] = VIPS_MIN( 1, B[b] / (1 - A[b]) ); - else - f[b] = 1; - break; - - case VIPS_BLEND_MODE_COLOUR_BURN: - for( int b = 0; b < bands; b++ ) - if( A[b] > 0 ) - f[b] = 1 - VIPS_MIN( 1, - (1 - B[b]) / A[b] ); - else - f[b] = 0; - break; - - case VIPS_BLEND_MODE_HARD_LIGHT: - for( int b = 0; b < bands; b++ ) - if( A[b] < 0.5 ) - f[b] = 2 * A[b] * B[b]; - else - f[b] = 1 - 2 * (1 - A[b]) * (1 - B[b]); - break; - - case VIPS_BLEND_MODE_SOFT_LIGHT: - for( int b = 0; b < bands; b++ ) { - double g; - - if( B[b] <= 0.25 ) - g = ((16 * B[b] - 12) * B[b] + 4) * B[b]; - else - g = sqrt( B[b] ); - - if( A[b] <= 0.5 ) - f[b] = B[b] - (1 - 2 * A[b]) * - B[b] * (1 - B[b]); - else - f[b] = B[b] + (2 * A[b] - 1) * - (g - B[b]); - } - break; - - case VIPS_BLEND_MODE_DIFFERENCE: - for( int b = 0; b < bands; b++ ) - f[b] = abs( B[b] - A[b] ); - break; - - case VIPS_BLEND_MODE_EXCLUSION: - for( int b = 0; b < bands; b++ ) - f[b] = A[b] + B[b] - 2 * A[b] * B[b]; - break; - - default: - g_assert_not_reached(); - for( int b = 0; b < bands; b++ ) - B[b] = 0; - } - - t1 = 1 - aB; - t2 = 1 - aA; - t3 = aA * aB; - for( int b = 0; b < bands; b++ ) - B[b] = t1 * A[b] + t2 * B[b] + t3 * f[b]; - break; - } - - B[bands] = aR; -} - -/* We have a vector path with gcc's vector attr. - */ -#ifdef HAVE_VECTOR_ARITH -/* Special path for RGBA with non-double output. This is overwhelmingly the most - * common case, and vectorises easily. - * - * B is the float pixel we are accumulating, A is the new pixel coming - * in from memory. - */ -template -static void -vips_composite_base_blend3( VipsCompositeBase *composite, - VipsBlendMode mode, v4f &B, T * restrict p ) -{ - v4f A; - float aA; - float aB; - float aR; - float t1; - float t2; - float t3; - v4f f; - v4f g; - - /* Load and scale the pixel to 0 - 1. - */ - A[0] = p[0]; - A[1] = p[1]; - A[2] = p[2]; - A[3] = p[3]; - - A /= composite->max_band_vec; - - aA = A[3]; - aB = B[3]; - - /* We may need to premultiply A. - */ - if( !composite->premultiplied ) - A *= aA; - - switch( mode ) { - case VIPS_BLEND_MODE_CLEAR: - aR = 0; - B[0] = 0; - B[1] = 0; - B[2] = 0; - break; - - case VIPS_BLEND_MODE_SOURCE: - aR = aA; - B = A; - break; - - case VIPS_BLEND_MODE_OVER: - aR = aA + aB * (1 - aA); - t1 = 1 - aA; - B = A + t1 * B; - break; - - case VIPS_BLEND_MODE_IN: - aR = aA * aB; - B = A; - break; - - case VIPS_BLEND_MODE_OUT: - aR = aA * (1 - aB); - B = A; - break; - - case VIPS_BLEND_MODE_ATOP: - aR = aB; - t1 = 1 - aA; - B = A + t1 * B; - break; - - case VIPS_BLEND_MODE_DEST: - aR = aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_OVER: - aR = aB + aA * (1 - aB); - t1 = 1 - aB; - B = B + t1 * A; - break; - - case VIPS_BLEND_MODE_DEST_IN: - aR = aA * aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_OUT: - aR = (1 - aA) * aB; - // B = B - break; - - case VIPS_BLEND_MODE_DEST_ATOP: - aR = aA; - t1 = 1 - aA; - B = t1 * A + B; - break; - - case VIPS_BLEND_MODE_XOR: - aR = aA + aB - 2 * aA * aB; - t1 = 1 - aB; - t2 = 1 - aA; - B = t1 * A + t2 * B; - break; - - case VIPS_BLEND_MODE_ADD: - aR = VIPS_MIN( 1, aA + aB ); - B = A + B; - break; - - case VIPS_BLEND_MODE_SATURATE: - aR = VIPS_MIN( 1, aA + aB ); - t1 = VIPS_MIN( aA, 1 - aB ); - B = t1 * A + B; - break; - - default: - /* The PDF modes are a bit different. - */ - aR = aA + aB * (1 - aA); - - switch( mode ) { - case VIPS_BLEND_MODE_MULTIPLY: - f = A * B; - break; - - case VIPS_BLEND_MODE_SCREEN: - f = A + B - A * B; - break; - - case VIPS_BLEND_MODE_OVERLAY: - f = B <= 0.5 ? - 2 * A * B : - 1 - 2 * (1 - A) * (1 - B); - break; - - case VIPS_BLEND_MODE_DARKEN: - f = VIPS_MIN( A, B ); - break; - - case VIPS_BLEND_MODE_LIGHTEN: - f = VIPS_MAX( A, B ); - break; - - case VIPS_BLEND_MODE_COLOUR_DODGE: - f = A < 1 ? - VIPS_MIN( 1, B / (1 - A) ) : - 1; - break; - - case VIPS_BLEND_MODE_COLOUR_BURN: - f = A > 0 ? - 1 - VIPS_MIN( 1, (1 - B) / A ) : - 0; - break; - - case VIPS_BLEND_MODE_HARD_LIGHT: - f = A < 0.5 ? - 2 * A * B : - 1 - 2 * (1 - A) * (1 - B); - break; - - case VIPS_BLEND_MODE_SOFT_LIGHT: - /* You can't sqrt a vector, so we must loop. - */ - for( int b = 0; b < 3; b++ ) { - double g; - - if( B[b] <= 0.25 ) - g = ((16 * B[b] - 12) * B[b] + 4) * B[b]; - else - g = sqrt( B[b] ); - - if( A[b] <= 0.5 ) - f[b] = B[b] - (1 - 2 * A[b]) * - B[b] * (1 - B[b]); - else - f[b] = B[b] + (2 * A[b] - 1) * - (g - B[b]); - } - break; - - case VIPS_BLEND_MODE_DIFFERENCE: - g = B - A; - f = g > 0 ? g : -1 * g; - break; - - case VIPS_BLEND_MODE_EXCLUSION: - f = A + B - 2 * A * B; - break; - - default: - g_assert_not_reached(); - - /* Stop compiler warnings. - */ - for( int b = 0; b < 3; b++ ) - B[b] = 0; - f = A; - } - - t1 = 1 - aB; - t2 = 1 - aA; - t3 = aA * aB; - B = t1 * A + t2 * B + t3 * f; - break; - } - - B[3] = aR; -} -#endif /*HAVE_VECTOR_ARITH*/ - -/* min_T and max_T are the numeric range for this type. 0, 0 means no limit, - * for example float. - */ -template -static void -vips_combine_pixels( VipsCompositeBase *composite, VipsPel *q, VipsPel **p ) -{ - VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; - int n_mode = composite->mode->area.n; - int n = composite->in->area.n; - int bands = composite->bands; - T * restrict tq = (T * restrict) q; - T ** restrict tp = (T ** restrict) p; - - double B[MAX_BANDS + 1]; - double aB; - - /* Load and scale the base pixel to 0 - 1. - */ - for( int b = 0; b <= bands; b++ ) - B[b] = tp[0][b] / composite->max_band[b]; - - aB = B[bands]; - if( !composite->premultiplied ) - for( int b = 0; b < bands; b++ ) - B[b] *= aB; - - for( int i = 1; i < n; i++ ) { - VipsBlendMode m = n_mode == 1 ? mode[0] : mode[i - 1]; - - vips_composite_base_blend( composite, m, B, tp[i] ); - } - - /* Unpremultiply, if necessary. - */ - if( !composite->premultiplied ) { - double aR = B[bands]; - - if( aR == 0 ) - for( int b = 0; b < bands; b++ ) - B[b] = 0; - else - for( int b = 0; b < bands; b++ ) - B[b] = B[b] / aR; - } - - /* Write back as a full range pixel, clipping to range. - */ - for( int b = 0; b <= bands; b++ ) { - double v; - - v = B[b] * composite->max_band[b]; - if( min_T != 0 || - max_T != 0 ) { - v = VIPS_CLIP( min_T, v, max_T ); - } - - tq[b] = v; - } -} - -#ifdef HAVE_VECTOR_ARITH -/* Three band (four with alpha) vecvtior case. Non-double output. min_T and - * max_T are the numeric range for this type. 0, 0 means no limit, - * for example float. - */ -template -static void -vips_combine_pixels3( VipsCompositeBase *composite, VipsPel *q, VipsPel **p ) -{ - VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; - int n_mode = composite->mode->area.n; - int n = composite->in->area.n; - T * restrict tq = (T * restrict) q; - T ** restrict tp = (T ** restrict) p; - - v4f B; - float aB; - - B[0] = tp[0][0]; - B[1] = tp[0][1]; - B[2] = tp[0][2]; - B[3] = tp[0][3]; - - /* Scale the base pixel to 0 - 1. - */ - B /= composite->max_band_vec; - aB = B[3]; - - if( !composite->premultiplied ) { - B *= aB; - B[3] = aB; - } - - for( int i = 1; i < n; i++ ) { - VipsBlendMode m = n_mode == 1 ? mode[0] : mode[i - 1]; - - vips_composite_base_blend3( composite, m, B, tp[i] ); - } - - /* Unpremultiply, if necessary. - */ - if( !composite->premultiplied ) { - float aR = B[3]; - - if( aR == 0 ) - for( int b = 0; b < 3; b++ ) - B[b] = 0; - else { - B /= aR; - B[3] = aR; - } - } - - /* Write back as a full range pixel, clipping to range. - */ - B *= composite->max_band_vec; - if( min_T != 0 || - max_T != 0 ) { - float low = min_T; - float high = max_T; - - B = VIPS_CLIP( low, B, high ); - } - - tq[0] = B[0]; - tq[1] = B[1]; - tq[2] = B[2]; - tq[3] = B[3]; -} -#endif /*HAVE_VECTOR_ARITH*/ - -static int -vips_composite_base_gen( VipsRegion *output_region, - void *vseq, void *a, void *b, gboolean *stop ) -{ - VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; - VipsCompositeBase *composite = (VipsCompositeBase *) b; - VipsRect *r = &output_region->valid; - int ps = VIPS_IMAGE_SIZEOF_PEL( output_region->im ); - int n = composite->in->area.n; - - if( vips_reorder_prepare_many( output_region->im, seq->ir, r ) ) - return( -1 ); - - VIPS_GATE_START( "vips_composite_base_gen: work" ); - - for( int y = 0; y < r->height; y++ ) { - VipsPel *q; - - for( int i = 0; i < n; i++ ) - seq->p[i] = VIPS_REGION_ADDR( seq->ir[i], - r->left, r->top + y ); - seq->p[n] = NULL; - q = VIPS_REGION_ADDR( output_region, r->left, r->top + y ); - - for( int x = 0; x < r->width; x++ ) { - switch( seq->ir[0]->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: -#ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p ); - else -#endif - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_CHAR: - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_USHORT: -#ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p ); - else -#endif - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_SHORT: - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_UINT: - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_INT: - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_FLOAT: -#ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p ); - else -#endif - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - case VIPS_FORMAT_DOUBLE: - vips_combine_pixels - - ( composite, q, seq->p ); - break; - - default: - g_assert_not_reached(); - return( -1 ); - } - - for( int i = 0; i < n; i++ ) - seq->p[i] += ps; - q += ps; - } - } - - VIPS_GATE_STOP( "vips_composite_base_gen: work" ); - - return( 0 ); -} - -static int -vips_composite_base_build( VipsObject *object ) -{ - VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object ); - VipsConversion *conversion = VIPS_CONVERSION( object ); - VipsCompositeBase *composite = (VipsCompositeBase *) object; - - int n; - VipsBlendMode *mode; - VipsImage **in; - VipsImage **decode; - VipsImage **compositing; - VipsImage **format; - VipsImage **size; - - if( VIPS_OBJECT_CLASS( vips_composite_base_parent_class )-> - build( object ) ) - return( -1 ); - - n = composite->in->area.n; - - if( n <= 0 ) { - vips_error( klass->nickname, "%s", _( "no input images" ) ); - return( -1 ); - } - if( composite->mode->area.n != n - 1 && - composite->mode->area.n != 1 ) { - vips_error( klass->nickname, _( "must be 1 or %d blend modes" ), - n - 1 ); - return( -1 ); - } - mode = (VipsBlendMode *) composite->mode->area.data; - for( int i = 0; i < composite->mode->area.n; i++ ) { - if( mode[i] < 0 || - mode[i] >= VIPS_BLEND_MODE_LAST ) { - vips_error( klass->nickname, - _( "blend mode index %d (%d) invalid" ), - i, mode[i] ); - return( -1 ); - } - } - - in = (VipsImage **) composite->in->area.data; - - decode = (VipsImage **) vips_object_local_array( object, n ); - for( int i = 0; i < n; i++ ) - if( vips_image_decode( in[i], &decode[i] ) ) - return( -1 ); - in = decode; - - /* Add a solid alpha to any images missing one. - */ - for( int i = n - 1; i >= 0; i-- ) - if( !vips_image_hasalpha( in[i] ) ) { - VipsImage *x; - - if( vips_addalpha( in[i], &x, (void *) NULL ) ) - return( -1 ); - g_object_unref( in[i] ); - in[i] = x; - } - - /* Transform to compositing space. It defaults to sRGB or B_W, usually - * 8 bit, but 16 bit if any inputs are 16 bit. - */ - if( !vips_object_argument_isset( object, "compositing_space" ) ) { - gboolean all_grey; - gboolean any_16; - - all_grey = TRUE; - for( int i = 0; i < n; i++ ) - if( in[i]->Bands > 2 ) { - all_grey = FALSE; - break; - } - - any_16 = FALSE; - for( int i = 0; i < n; i++ ) - if( in[i]->Type == VIPS_INTERPRETATION_GREY16 || - in[i]->Type == VIPS_INTERPRETATION_RGB16 ) { - any_16 = TRUE; - break; - } - - composite->compositing_space = any_16 ? - (all_grey ? - VIPS_INTERPRETATION_GREY16 : - VIPS_INTERPRETATION_RGB16) : - (all_grey ? - VIPS_INTERPRETATION_B_W : - VIPS_INTERPRETATION_sRGB); - } - - compositing = (VipsImage **) - vips_object_local_array( object, n ); - for( int i = 0; i < n; i++ ) - if( vips_colourspace( in[i], &compositing[i], - composite->compositing_space, (void *) NULL ) ) - return( -1 ); - in = compositing; - - /* Check that they all now match in bands. This can fail for some - * input combinations. - */ - for( int i = 1; i < n; i++ ) - if( in[i]->Bands != in[0]->Bands ) { - vips_error( klass->nickname, - "%s", _( "images do not have same " - "numbers of bands" ) ); - return( -1 ); - } - - if( in[0]->Bands > MAX_BANDS ) { - vips_error( klass->nickname, - "%s", _( "too many input bands" ) ); - return( -1 ); - } - - composite->bands = in[0]->Bands - 1; - - /* Set the max for each band now we know bands and compositing space. - */ - if( vips_composite_base_max_band( composite, composite->max_band ) ) { - vips_error( klass->nickname, - "%s", _( "unsupported compositing space" ) ); - return( -1 ); - } - -#ifdef HAVE_VECTOR_ARITH - /* We need a float version for the vector path. - */ - if( composite->bands == 3 ) - for( int b = 0; b <= 3; b++ ) - composite->max_band_vec[b] = composite->max_band[b]; -#endif /*HAVE_VECTOR_ARITH*/ - - /* Transform the input images to match format. We may have - * mixed float and double, for example. - */ - format = (VipsImage **) vips_object_local_array( object, n ); - if( vips__formatalike_vec( in, format, 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, n ); - - /* The zero image does not move. - */ - g_object_ref( in[0] ); - position[0] = in[0]; - - for( int i = 1; i < n; i++ ) - if( vips_embed( in[i], &position[i], - composite->x_offset[i - 1], - composite->y_offset[i - 1], - width, height, (void *) 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, n ); - if( vips__sizealike_vec( in, size, n ) ) - return( -1 ); - in = size; - - if( vips_image_pipeline_array( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, in ) ) - return( -1 ); - - if( vips_image_generate( conversion->out, - vips_composite_start, - vips_composite_base_gen, - vips_composite_stop, - 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; - - /* For N input images, N - 1 x coordinates. - */ - VipsArrayInt *x; - - /* For N input images, N - 1 y coordinates. - */ - VipsArrayInt *y; - -} VipsComposite; - -typedef VipsCompositeBaseClass VipsCompositeClass; - -/* We need C linkage for this. - */ -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 ) -{ - 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 = "composite"; - vobject_class->description = - _( "blend an array of images with an array of blend modes" ); - vobject_class->build = vips_composite_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - - VIPS_ARG_BOXED( klass, "in", 0, - _( "Inputs" ), - _( "Array of input images" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - 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( 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 -vips_composite_init( VipsComposite *composite ) -{ -} - -static int -vips_compositev( VipsImage **in, VipsImage **out, int n, int *mode, va_list ap ) -{ - VipsArrayImage *image_array; - VipsArrayInt *mode_array; - int result; - - image_array = vips_array_image_new( in, n ); - mode_array = vips_array_int_new( mode, n - 1 ); - result = vips_call_split( "composite", ap, - image_array, out, mode_array ); - vips_area_unref( VIPS_AREA( image_array ) ); - vips_area_unref( VIPS_AREA( mode_array ) ); - - return( result ); -} - -/* See conversion.c for the doc comment. - */ - -int -vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) -{ - va_list ap; - int result; - - va_start( ap, mode ); - result = vips_compositev( in, out, n, mode, ap ); - va_end( ap ); - - return( result ); -} - -typedef struct _VipsComposite2 { - VipsCompositeBase parent_instance; - - VipsImage *base; - VipsImage *overlay; - VipsBlendMode mode; - int x; - int y; - -} 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 ); - } - - base->x_offset = &composite2->x; - base->y_offset = &composite2->y; - - 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 ); - - 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 -vips_composite2_init( VipsComposite2 *composite2 ) -{ -} - -/* See conversion.c for the doc comment. - */ - -int -vips_composite2( VipsImage *base, VipsImage *overlay, VipsImage **out, - VipsBlendMode mode, ... ) -{ - va_list ap; - int result; - - va_start( ap, mode ); - result = vips_call_split( "composite2", ap, base, overlay, out, mode ); - va_end( ap ); - - return( result ); -} - diff --git a/libvips/conversion/composite.cpp b/libvips/conversion/composite.cpp index 4bcd923e..9dbf2f6c 100644 --- a/libvips/conversion/composite.cpp +++ b/libvips/conversion/composite.cpp @@ -11,7 +11,8 @@ * - x/y params let you position images * 27/11/18 * - don't stop on first non-transparent image [felixbuenemann] - * - composite in scanlines, not pixels [jcupitt] + * 6/12/18 + * - do our own subimage positioning */ /* @@ -108,6 +109,11 @@ typedef struct _VipsCompositeBase { int *x_offset; int *y_offset; + /* A rect for the position of each input image. For each output region, + * we composite the set of input images which intersect that area. + */ + VipsRect *subimages; + /* The number of non-alpha bands we are blending. */ int bands; @@ -147,6 +153,7 @@ vips_composite_base_dispose( GObject *gobject ) vips_area_unref( (VipsArea *) composite->mode ); composite->mode = NULL; } + VIPS_FREE( composite->subimages ); G_OBJECT_CLASS( vips_composite_base_parent_class )->dispose( gobject ); } @@ -156,21 +163,30 @@ vips_composite_base_dispose( GObject *gobject ) typedef struct { VipsCompositeBase *composite; - /* Set of input regions. + /* Full set of input regions, each made on the corresponding input + * image. */ - VipsRegion **ir; + VipsRegion **input_regions; - /* For each input, an input pointer. + /* We then vips_region_prepare_to() to one of this set of regions, + * each defined on the base image. + */ + VipsRegion **composite_regions; + + /* Number of input regions which intersect this request rect. + */ + int n; + + /* For each of @n above (inputs which intersect this request), the + * index of the input image we need. We can use this index to get the + * position, input region and composite region. + */ + int *enabled; + + /* For each enabled image, an input pointer. */ VipsPel **p; - /* We composite images a scanline at a time, working up the image - * stack. Keep intermediate pixels in this scanline buffer. - * - * This will be float for the vector path, double otherwise. - */ - double *scanline; - } VipsCompositeSequence; static int @@ -178,16 +194,20 @@ vips_composite_stop( void *vseq, void *a, void *b ) { VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; - if( seq->ir ) { - int i; - - for( i = 0; seq->ir[i]; i++ ) - VIPS_UNREF( seq->ir[i] ); - VIPS_FREE( seq->ir ); + if( seq->input_regions ) { + for( int i = 0; seq->input_regions[i]; i++ ) + VIPS_UNREF( seq->input_regions[i] ); + VIPS_FREE( seq->input_regions ); } + if( seq->composite_regions ) { + for( int i = 0; seq->composite_regions[i]; i++ ) + VIPS_UNREF( seq->composite_regions[i] ); + VIPS_FREE( seq->composite_regions ); + } + + VIPS_FREE( seq->enabled ); VIPS_FREE( seq->p ); - VIPS_FREE( seq->scanline ); VIPS_FREE( seq ); @@ -207,7 +227,8 @@ vips_composite_start( VipsImage *out, void *a, void *b ) return( NULL ); seq->composite = composite; - seq->ir = NULL; + seq->input_regions = NULL; + seq->enabled = NULL; seq->p = NULL; /* How many images? @@ -217,36 +238,42 @@ vips_composite_start( VipsImage *out, void *a, void *b ) /* Allocate space for region array. */ - if( !(seq->ir = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { + if( !(seq->input_regions = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { + vips_composite_stop( seq, NULL, NULL ); + return( NULL ); + } + for( i = 0; i < n + 1; i++ ) + seq->input_regions[i] = NULL; + + if( !(seq->composite_regions = + VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { + vips_composite_stop( seq, NULL, NULL ); + return( NULL ); + } + for( i = 0; i < n + 1; i++ ) + seq->composite_regions[i] = NULL; + + seq->enabled = VIPS_ARRAY( NULL, n, int ); + seq->p = VIPS_ARRAY( NULL, n, VipsPel * ); + if( !seq->enabled || + !seq->p ) { vips_composite_stop( seq, NULL, NULL ); return( NULL ); } /* Create a set of regions. */ - for( i = 0; i < n; i++ ) - if( !(seq->ir[i] = vips_region_new( in[i] )) ) { + for( i = 0; i < n; i++ ) { + seq->input_regions[i] = vips_region_new( in[i] ); + seq->composite_regions[i] = vips_region_new( in[0] ); + + if( !seq->input_regions[i] || + !seq->composite_regions[i] ) { vips_composite_stop( seq, NULL, NULL ); return( NULL ); } + } - seq->ir[n] = NULL; - - /* Input pointers. - */ - if( !(seq->p = VIPS_ARRAY( NULL, n + 1, VipsPel * )) ) { - vips_composite_stop( seq, NULL, NULL ); - return( NULL ); - } - - /* Scanline buffer. - */ - if( !(seq->scanline = - VIPS_ARRAY( NULL, in[0]->Xsize * in[0]->Bands, double )) ) { - vips_composite_stop( seq, NULL, NULL ); - return( NULL ); - } - return( seq ); } @@ -346,13 +373,13 @@ vips_composite_base_max_band( VipsCompositeBase *composite, double *max_band ) * xB colour band of source B */ -/* A is the new scaline coming in, of any non-complex type T. +/* A is the new pixel coming in, of any non-complex type T. * * We must scale incoming pixels to 0 - 1 by dividing by the scale[] vector. * * If premultipled is not set, we premultiply incoming pixels before blending. * - * B is the double scanline we are accumulating. + * B is the double pixel we are accumulating. */ template static void @@ -541,8 +568,7 @@ vips_composite_base_blend( VipsCompositeBase *composite, double g; if( B[b] <= 0.25 ) - g = ((16 * B[b] - 12) * - B[b] + 4) * B[b]; + g = ((16 * B[b] - 12) * B[b] + 4) * B[b]; else g = sqrt( B[b] ); @@ -804,204 +830,132 @@ vips_composite_base_blend3( VipsCompositeBase *composite, */ template static void -vips_combine_pixels( VipsCompositeBase *composite, - VipsPel *q, VipsPel **p, double *scanline, int width ) +vips_combine_pixels( VipsCompositeSequence *seq, VipsPel *q ) { + VipsCompositeBase *composite = seq->composite; VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; int n_mode = composite->mode->area.n; - int n = composite->in->area.n; + int n = seq->n; int bands = composite->bands; T * restrict tq = (T * restrict) q; - T ** restrict tp = (T ** restrict) p; + T ** restrict tp = (T ** restrict) seq->p; - double *B1; - T * restrict tp1; - T * restrict tq1; + double B[MAX_BANDS + 1]; + double aB; - /* Load and scale the base scanline to 0 - 1. + /* Load and scale the base pixel to 0 - 1. */ - B1 = scanline; - tp1 = tp[0]; - for( int x = 0; x < width; x++ ) { - for( int b = 0; b <= bands; b++ ) - B1[b] = tp1[b] / composite->max_band[b]; + for( int b = 0; b <= bands; b++ ) + B[b] = tp[0][b] / composite->max_band[b]; - if( !composite->premultiplied ) { - double aB = B1[bands]; - - for( int b = 0; b < bands; b++ ) - B1[b] *= aB; - } - - B1 += bands + 1; - tp1 += bands + 1; - } + aB = B[bands]; + if( !composite->premultiplied ) + for( int b = 0; b < bands; b++ ) + B[b] *= aB; for( int i = 1; i < n; i++ ) { VipsBlendMode m = n_mode == 1 ? mode[0] : mode[i - 1]; - B1 = scanline; - tp1 = tp[i]; - for( int x = 0; x < width; x++ ) { - vips_composite_base_blend( composite, m, B1, tp1 ); - - B1 += bands + 1; - tp1 += bands + 1; - } + vips_composite_base_blend( composite, m, B, tp[i] ); } - /* Write back as a full range pixel, clipping to range. Unpremultiply, - * if necessary. + /* Unpremultiply, if necessary. */ - B1 = scanline; - tq1 = tq; - for( int x = 0; x < width; x++ ) { - if( !composite->premultiplied ) { - double aR = B1[bands]; + if( !composite->premultiplied ) { + double aR = B[bands]; - if( aR == 0 ) - for( int b = 0; b < bands; b++ ) - B1[b] = 0; - else - for( int b = 0; b < bands; b++ ) - B1[b] = B1[b] / aR; + if( aR == 0 ) + for( int b = 0; b < bands; b++ ) + B[b] = 0; + else + for( int b = 0; b < bands; b++ ) + B[b] = B[b] / aR; + } + + /* Write back as a full range pixel, clipping to range. + */ + for( int b = 0; b <= bands; b++ ) { + double v; + + v = B[b] * composite->max_band[b]; + if( min_T != 0 || + max_T != 0 ) { + v = VIPS_CLIP( min_T, v, max_T ); } - for( int b = 0; b <= bands; b++ ) { - double v; - - v = B1[b] * composite->max_band[b]; - if( min_T != 0 || - max_T != 0 ) { - v = VIPS_CLIP( min_T, v, max_T ); - } - - tq1[b] = v; - } - - B1 += bands + 1; - tq1 += bands + 1; + tq[b] = v; } } #ifdef HAVE_VECTOR_ARITH -/* Three band (four with alpha) vector case. Non-double output. min_T and +/* Three band (four with alpha) vecvtior case. Non-double output. min_T and * max_T are the numeric range for this type. 0, 0 means no limit, * for example float. */ template static void -vips_combine_pixels3( VipsCompositeBase *composite, - VipsPel *q, VipsPel **p, double *scanline, int width ) +vips_combine_pixels3( VipsCompositeSequence *seq, VipsPel *q ) { + VipsCompositeBase *composite = seq->composite; VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; int n_mode = composite->mode->area.n; - int n = composite->in->area.n; + int n = seq->n; T * restrict tq = (T * restrict) q; - T ** restrict tp = (T ** restrict) p; + T ** restrict tp = (T ** restrict) seq->p; - float *B1; - T * restrict tp1; - T * restrict tq1; + v4f B; + float aB; - printf( "on vector path\n" ); + B[0] = tp[0][0]; + B[1] = tp[0][1]; + B[2] = tp[0][2]; + B[3] = tp[0][3]; - B1 = (float *) scanline; - tp1 = tp[0]; - for( int x = 0; x < width; x++ ) { - v4f B; - float aB; + /* Scale the base pixel to 0 - 1. + */ + B /= composite->max_band_vec; + aB = B[3]; - B[0] = tp1[0]; - B[1] = tp1[1]; - B[2] = tp1[2]; - B[3] = tp1[3]; - - /* Scale the base pixel to 0 - 1. - */ - B /= composite->max_band_vec; - aB = B[3]; - - if( !composite->premultiplied ) { - B *= aB; - B[3] = aB; - } - - B1[0] = B[0]; - B1[1] = B[1]; - B1[2] = B[2]; - B1[3] = B[3]; - - B1 += 4; - tp1 += 4; + if( !composite->premultiplied ) { + B *= aB; + B[3] = aB; } for( int i = 1; i < n; i++ ) { VipsBlendMode m = n_mode == 1 ? mode[0] : mode[i - 1]; - B1 = (float *) scanline; - tp1 = tp[i]; - for( int x = 0; x < width; x++ ) { - v4f B; + vips_composite_base_blend3( composite, m, B, tp[i] ); + } - B[0] = B1[0]; - B[1] = B1[1]; - B[2] = B1[2]; - B[3] = B1[3]; + /* Unpremultiply, if necessary. + */ + if( !composite->premultiplied ) { + float aR = B[3]; - vips_composite_base_blend3( composite, m, B, tp1 ); - - B1[0] = B[0]; - B1[1] = B[1]; - B1[2] = B[2]; - B1[3] = B[3]; - - B1 += 4; - tp1 += 4; + if( aR == 0 ) + for( int b = 0; b < 3; b++ ) + B[b] = 0; + else { + B /= aR; + B[3] = aR; } } - B1 = (float *) scanline; - tq1 = tq; - for( int x = 0; x < width; x++ ) { - v4f B; + /* Write back as a full range pixel, clipping to range. + */ + B *= composite->max_band_vec; + if( min_T != 0 || + max_T != 0 ) { + float low = min_T; + float high = max_T; - B[0] = B1[0]; - B[1] = B1[1]; - B[2] = B1[2]; - B[3] = B1[3]; - - /* Unpremultiply, if necessary. - */ - if( !composite->premultiplied ) { - float aR = B[3]; - - if( aR == 0 ) - for( int b = 0; b < 3; b++ ) - B[b] = 0; - else { - B /= aR; - B[3] = aR; - } - } - - B *= composite->max_band_vec; - if( min_T != 0 || - max_T != 0 ) { - float low = min_T; - float high = max_T; - - B = VIPS_CLIP( low, B, high ); - } - - tq1[0] = B[0]; - tq1[1] = B[1]; - tq1[2] = B[2]; - tq1[3] = B[3]; - - B1 += 4; - tq1 += 4; + B = VIPS_CLIP( low, B, high ); } + + tq[0] = B[0]; + tq[1] = B[1]; + tq[2] = B[2]; + tq[3] = B[3]; } #endif /*HAVE_VECTOR_ARITH*/ @@ -1012,106 +966,156 @@ vips_composite_base_gen( VipsRegion *output_region, VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; VipsCompositeBase *composite = (VipsCompositeBase *) b; VipsRect *r = &output_region->valid; + int ps = VIPS_IMAGE_SIZEOF_PEL( output_region->im ); int n = composite->in->area.n; - if( vips_reorder_prepare_many( output_region->im, seq->ir, r ) ) - return( -1 ); + /* Find the subset of our input images which intersect this region. + */ + seq->n = 0; + for( int i = 0; i < n; i++ ) + if( vips_rect_overlapsrect( r, &composite->subimages[i] ) ) { + seq->enabled[seq->n] = i; + seq->n += 1; + } + + /* FIXME ... just one? prepare directly to output and return. + */ + + /* FIXME ... can use vips_rect_overlapsrect() elsewhere? + */ + + /* Prepare the appropriate parts into our set of composite + * regions. + */ + for( int i = 0; i < seq->n; i++ ) { + int j = seq->enabled[i]; + + VipsRect request; + + /* Translate the request to the input image space, clipping as + * required. + */ + request = *r; + vips_rect_intersectrect( &request, &composite->subimages[j], + &request ); + request.left -= composite->subimages[j].left; + request.top -= composite->subimages[j].top; + + /* Set the composite region up to be a bit of memory at the + * right position. + */ + if( vips_region_buffer( seq->composite_regions[j], r ) ) + return( -1 ); + + /* If the request is smaller than the target region, there + * will be some gaps. We must make sure these are zero. + */ + if( request.width < r->width || + request.height < r->height ) + vips_region_black( seq->composite_regions[j] ); + + /* And render the right part of the input image to the + * composite region. + */ + if( vips_region_prepare_to( seq->input_regions[j], + seq->composite_regions[j], &request, + r->left, r->top ) ) + return( -1 ); + } VIPS_GATE_START( "vips_composite_base_gen: work" ); for( int y = 0; y < r->height; y++ ) { VipsPel *q; - for( int i = 0; i < n; i++ ) - seq->p[i] = VIPS_REGION_ADDR( seq->ir[i], + for( int i = 0; i < seq->n; i++ ) { + int j = seq->enabled[i]; + + seq->p[i] = VIPS_REGION_ADDR( seq->composite_regions[j], r->left, r->top + y ); + } seq->p[n] = NULL; q = VIPS_REGION_ADDR( output_region, r->left, r->top + y ); - switch( seq->ir[0]->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: + for( int x = 0; x < r->width; x++ ) { + switch( seq->input_regions[0]->im->BandFmt ) { + case VIPS_FORMAT_UCHAR: #ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p, seq->scanline, - r->width ); - else + if( composite->bands == 3 ) + vips_combine_pixels3 + + ( seq, q ); + else #endif + vips_combine_pixels + + ( seq, q ); + break; + + case VIPS_FORMAT_CHAR: vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + + ( seq, q ); + break; - case VIPS_FORMAT_CHAR: - vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; - - case VIPS_FORMAT_USHORT: + case VIPS_FORMAT_USHORT: #ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p, seq->scanline, - r->width ); - else + if( composite->bands == 3 ) + vips_combine_pixels3 + + ( seq, q ); + else #endif + vips_combine_pixels + + ( seq, q ); + break; + + case VIPS_FORMAT_SHORT: vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + + ( seq, q ); + break; - case VIPS_FORMAT_SHORT: - vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + case VIPS_FORMAT_UINT: + vips_combine_pixels + + ( seq, q ); + break; - case VIPS_FORMAT_UINT: - vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + case VIPS_FORMAT_INT: + vips_combine_pixels + + ( seq, q ); + break; - case VIPS_FORMAT_INT: - vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; - - case VIPS_FORMAT_FLOAT: + case VIPS_FORMAT_FLOAT: #ifdef HAVE_VECTOR_ARITH - if( composite->bands == 3 ) - vips_combine_pixels3 - - ( composite, q, seq->p, seq->scanline, - r->width ); - else + if( composite->bands == 3 ) + vips_combine_pixels3 + + ( seq, q ); + else #endif + vips_combine_pixels + + ( seq, q ); + break; + + case VIPS_FORMAT_DOUBLE: vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + + ( seq, q ); + break; - case VIPS_FORMAT_DOUBLE: - vips_combine_pixels - - ( composite, q, seq->p, seq->scanline, - r->width ); - break; + default: + g_assert_not_reached(); + return( -1 ); + } - default: - g_assert_not_reached(); - return( -1 ); + for( int i = 0; i < seq->n; i++ ) + seq->p[i] += ps; + q += ps; } } @@ -1133,7 +1137,6 @@ vips_composite_base_build( VipsObject *object ) VipsImage **decode; VipsImage **compositing; VipsImage **format; - VipsImage **size; if( VIPS_OBJECT_CLASS( vips_composite_base_parent_class )-> build( object ) ) @@ -1164,6 +1167,31 @@ vips_composite_base_build( VipsObject *object ) in = (VipsImage **) composite->in->area.data; + /* Make a set of rects for the positions of the input images. Image 0 + * (the background) is always at (0, 0). + */ + if( !(composite->subimages = + VIPS_ARRAY( NULL, n, VipsRect )) ) + return( -1 ); + for( int i = 0; i < n; i++ ) { + composite->subimages[i].left = 0; + composite->subimages[i].top = 0; + composite->subimages[i].width = in[i]->Xsize; + composite->subimages[i].height = in[i]->Ysize; + } + + /* Position all images, if x/y is set. Image 0 + * (the background) is always at (0, 0). + */ + if( composite->x_offset && + composite->y_offset ) + for( int i = 1; i < n; i++ ) { + composite->subimages[i].left = + composite->x_offset[i - 1]; + composite->subimages[i].top = + composite->y_offset[i - 1]; + } + decode = (VipsImage **) vips_object_local_array( object, n ); for( int i = 0; i < n; i++ ) if( vips_image_decode( in[i], &decode[i] ) ) @@ -1256,7 +1284,7 @@ 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 format. We may have + /* Transform the input images to match in format. We may have * mixed float and double, for example. */ format = (VipsImage **) vips_object_local_array( object, n ); @@ -1264,42 +1292,15 @@ vips_composite_base_build( VipsObject *object ) 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, n ); - - /* The zero image does not move. - */ - g_object_ref( in[0] ); - position[0] = in[0]; - - for( int i = 1; i < n; i++ ) - if( vips_embed( in[i], &position[i], - composite->x_offset[i - 1], - composite->y_offset[i - 1], - width, height, (void *) 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, n ); - if( vips__sizealike_vec( in, size, n ) ) - return( -1 ); - in = size; - if( vips_image_pipeline_array( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, in ) ) return( -1 ); + /* The output image is always the size of the base image. + */ + conversion->out->Xsize = in[0]->Xsize; + conversion->out->Ysize = in[0]->Ysize; + if( vips_image_generate( conversion->out, vips_composite_start, vips_composite_base_gen, diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index 01636c51..ce45ccdf 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -181,7 +181,7 @@ vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) return( vips__insert_just_one( or, ir[1], insert->rsub.left, insert->rsub.top ) ); - + /* Does it fall entirely inside the main, and not at all inside the * sub? */ diff --git a/libvips/include/vips/rect.h b/libvips/include/vips/rect.h index 108011e8..2c32de4e 100644 --- a/libvips/include/vips/rect.h +++ b/libvips/include/vips/rect.h @@ -52,6 +52,7 @@ gboolean vips_rect_isempty( const VipsRect *r ); gboolean vips_rect_includespoint( const VipsRect *r, int x, int y ); gboolean vips_rect_includesrect( const VipsRect *r1, const VipsRect *r2 ); gboolean vips_rect_equalsrect( const VipsRect *r1, const VipsRect *r2 ); +gboolean vips_rect_overlapsrect( const VipsRect *r1, const VipsRect *r2 ); void vips_rect_marginadjust( VipsRect *r, int n ); void vips_rect_intersectrect( const VipsRect *r1, const VipsRect *r2, VipsRect *out ); diff --git a/libvips/iofuncs/rect.c b/libvips/iofuncs/rect.c index d77e024d..8e5dd5ac 100644 --- a/libvips/iofuncs/rect.c +++ b/libvips/iofuncs/rect.c @@ -132,6 +132,25 @@ vips_rect_equalsrect( const VipsRect *r1, const VipsRect *r2 ) r1->width == r2->width && r1->height == r2->height ); } +/** + * vips_rect_overlapsrect: (method) + * @r1: first rectangle + * @r2: second rectangle + * + * Do @r1 and @r2 have a non-empty intersection? + * + * Returns: %TRUE if @r2 and @r1 overlap. + */ +gboolean +vips_rect_overlapsrect( const VipsRect *r1, const VipsRect *r2 ) +{ + VipsRect intersection; + + vips_rect_intersectrect( r1, r2, &intersection ); + + return( !vips_rect_isempty( &intersection ) ); +} + /** * vips_rect_marginadjust: (method) * @r: rectangle to adjust diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index bce6d775..42f84c0c 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -1735,13 +1735,12 @@ vips_region_prepare_to_generate( VipsRegion *reg, * @y: postion of @r in @dest * * Like vips_region_prepare(): fill @reg with the pixels in area @r. - * Unlike vips_region_prepare(), rather than writing the result to @reg, the pixels are - * written into @dest - * at offset @x, @y. + * + * Unlike vips_region_prepare(), rather than writing the result to @reg, the + * pixels are written into @dest at offset @x, @y. * * Also unlike vips_region_prepare(), @dest is not set up for writing for - * you with - * vips_region_buffer(). You can + * you with vips_region_buffer(). You can * point @dest at anything, and pixels really will be written there. * This makes vips_region_prepare_to() useful for making the ends of * pipelines.