From 95c8a1915b84437a8df8427af3e74deb1ce2214a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 6 Oct 2017 16:00:54 +0100 Subject: [PATCH] all doneo seems to work! --- libvips/conversion/composite.cpp | 1043 ++++++++++++----------------- libvips/include/vips/conversion.h | 5 + 2 files changed, 428 insertions(+), 620 deletions(-) diff --git a/libvips/conversion/composite.cpp b/libvips/conversion/composite.cpp index 66e1a00b..19bc5085 100644 --- a/libvips/conversion/composite.cpp +++ b/libvips/conversion/composite.cpp @@ -59,6 +59,10 @@ */ #define MAX_BANDS (64) +/* Uncomment to disable vector path ... handy for debugging. +#undef HAVE_VECTOR_ARITH + */ + /** * VipsBlendMode: * VIPS_BLEND_MODE_CLEAR: @@ -100,391 +104,13 @@ * https://www.cairographics.org/operators/ */ -/* For each of the supported interpretations, the maximum value of each band. +/* We have a vector path with gcc's vector attr. */ -static int -vips_composite_prescale_max_band( VipsImage *image, double *scale ) -{ - double max_alpha; - int b; - - max_alpha = 255.0; - if( image->Type == VIPS_INTERPRETATION_GREY16 || - image->Type == VIPS_INTERPRETATION_RGB16 ) - max_alpha = 65535.0; - - for( b = 0; b < image->Bands; b++ ) - scale[b] = max_alpha; - - switch( image->Type ) { - case VIPS_INTERPRETATION_XYZ: - scale[0] = VIPS_D65_X0; - scale[1] = VIPS_D65_Y0; - scale[2] = VIPS_D65_Z0; - break; - - case VIPS_INTERPRETATION_LAB: - scale[0] = 100; - scale[1] = 128; - scale[2] = 128; - break; - - case VIPS_INTERPRETATION_LCH: - scale[0] = 100; - scale[1] = 128; - scale[2] = 360; - break; - - case VIPS_INTERPRETATION_CMC: - scale[0] = 100; - scale[1] = 128; - scale[2] = 360; - break; - - case VIPS_INTERPRETATION_scRGB: - scale[0] = 1; - scale[1] = 1; - scale[2] = 1; - break; - - case VIPS_INTERPRETATION_sRGB: - scale[0] = 255; - scale[1] = 255; - scale[2] = 255; - break; - - case VIPS_INTERPRETATION_HSV: - scale[0] = 255; - scale[1] = 255; - scale[2] = 255; - break; - - case VIPS_INTERPRETATION_RGB16: - scale[0] = 65535; - scale[1] = 65535; - scale[2] = 65535; - break; - - case VIPS_INTERPRETATION_GREY16: - scale[0] = 65535; - break; - - case VIPS_INTERPRETATION_YXY: - scale[0] = 100; - scale[1] = 1; - scale[2] = 1; - break; - - case VIPS_INTERPRETATION_B_W: - scale[0] = 256; - break; - - default: - return( -1 ); - } - - return( 0 ); -} - -#define LOOP_PRESCALE( IN, OUT ) { \ - IN *tp = (IN ) p; \ - OUT *tq = (OUT ) q; \ - \ - for( x = 0; x < sz; x++ ) \ - for( b = 0; b < bands; b++ ) \ - tq[b] = tp[b] * scale[b]; \ - \ - tq += bands; \ - tp += bands; \ - } \ -} - -static int -vips_composite_prescale_gen( VipsRegion *output_region, - void *seq, void *a, void *b, gboolean *stop ) -{ - VipsRegion *input_region = (VipsRegion *) seq; - VipsImage *in = (VipsImage *) a; - int bands = in->Bands; - double *scale = (double *) b; - VipsRect *r = &output_region->valid; - int sz = r->width * in->Bands; - - int x, b; - - if( vips_region_prepare( input_region, r ) ) - return( -1 ); - - VIPS_GATE_START( "vips_composite_prescale_gen: work" ); - - for( int y = 0; y < r->height; y++ ) { - VipsPel *p = VIPS_REGION_ADDR( input_region, - r->left, r->top + y ); - VipsPel *q = VIPS_REGION_ADDR( output_region, - r->left, r->top + y ); - - switch( input_region->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - LOOP_PRESCALE( unsigned char, float ); - break; - - case VIPS_FORMAT_CHAR: - LOOP_PRESCALE( signed char, float ); - break; - - case VIPS_FORMAT_USHORT: - LOOP_PRESCALE( unsigned short, float ); - break; - - case VIPS_FORMAT_SHORT: - LOOP_PRESCALE( signed short, float ); - break; - - case VIPS_FORMAT_UINT: - LOOP_PRESCALE( unsigned int, float ); - break; - - case VIPS_FORMAT_INT: - LOOP_PRESCALE( signed int, float ); - break; - - case VIPS_FORMAT_FLOAT: - LOOP_PRESCALE( float, float ); - break; - - case VIPS_FORMAT_DOUBLE: - LOOP_PRESCALE( double, double ); - break; - - default: - g_assert_not_reached(); - return( -1 ); - } - } - - VIPS_GATE_STOP( "vips_composite_prescale_gen: work" ); - - return( 0 ); -} - -#define LOOP_PRESCALE_PREMULTIPLY( IN, OUT ) { \ - IN *tp = (IN ) p; \ - OUT *tq = (OUT ) q; \ - \ - for( x = 0; x < sz; x++ ) \ - for( b = 0; b < bands; b++ ) \ - tq[b] = tp[b] * scale[b]; \ - \ - tq += bands; \ - tp += bands; \ - } \ -} - -static int -vips_composite_prescale_premultiply_gen( VipsRegion *output_region, - void *seq, void *a, void *b, gboolean *stop ) -{ - VipsRegion *input_region = (VipsRegion *) seq; - VipsImage *in = (VipsImage *) a; - int bands = in->Bands; - double *scale = (double *) b; - VipsRect *r = &output_region->valid; - int sz = r->width * in->Bands; - - int x, b; - - if( vips_region_prepare( input_region, r ) ) - return( -1 ); - - VIPS_GATE_START( "vips_composite_prescale_premultiply_gen: work" ); - - for( int y = 0; y < r->height; y++ ) { - VipsPel *p = VIPS_REGION_ADDR( input_region, - r->left, r->top + y ); - VipsPel *q = VIPS_REGION_ADDR( output_region, - r->left, r->top + y ); - - switch( input_region->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - LOOP_PRESCALE_PREMULTIPLY( unsigned char, float ); - break; - - case VIPS_FORMAT_CHAR: - LOOP_PRESCALE_PREMULTIPLY( signed char, float ); - break; - - case VIPS_FORMAT_USHORT: - LOOP_PRESCALE_PREMULTIPLY( unsigned short, float ); - break; - - case VIPS_FORMAT_SHORT: - LOOP_PRESCALE_PREMULTIPLY( signed short, float ); - break; - - case VIPS_FORMAT_UINT: - LOOP_PRESCALE_PREMULTIPLY( unsigned int, float ); - break; - - case VIPS_FORMAT_INT: - LOOP_PRESCALE_PREMULTIPLY( signed int, float ); - break; - - case VIPS_FORMAT_FLOAT: - LOOP_PRESCALE_PREMULTIPLY( float, float ); - break; - - case VIPS_FORMAT_DOUBLE: - LOOP_PRESCALE_PREMULTIPLY( double, double ); - break; - - default: - g_assert_not_reached(); - return( -1 ); - } - } - - VIPS_GATE_STOP( "vips_composite_prescale_premultiply_gen: work" ); - - return( 0 ); -} - -/* Prescale all bands to float 0 - 1, or double if the input is double. If - * premultiplied is FALSE, also premultiply all non-alpha channels by alpha. +#ifdef HAVE_VECTOR_ARITH +/* A vector of four floats. */ -static int -vips_composite_prescale( VipsImage *in, VipsImage **out, gboolean premultiplied ) -{ - double *scale; - - if( vips_check_noncomplex( "vips_composite_prescale", in ) ) - return( -1 ); - - *out = vips_image_new(); - - if( !(scale = VIPS_ARRAY( *out, double, in->Bands )) ) - return( -1 ); - if( vips_composite_prescale_max_band( in, scale ) ) { - vips_error( "vips_composite_prescale", - "%s", _( "unsupported prescale space" ) ); - return( -1 ); - } - - *out->BandFmt == in->BandFmt == VIPS_FORMAT_DOUBLE ? - VIPS_FORMAT_DOUBLE : VIPS_FORMAT_FLOAT; - if( vips_image_pipelinev( out, - VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) - return( -1 ); - if( vips_image_generate( out, - vips_start_one, vips_composite_prescale_gen, vips_stop_one, - in, scale ) ) - return( -1 ); - - return( 0 ); -} - -#define LOOP_UNPRESCALE( IN, OUT ) { \ - IN *tp = (IN ) p; \ - OUT *tq = (OUT ) q; \ - \ - for( x = 0; x < sz; x++ ) \ - for( b = 0; b < bands; b++ ) \ - tq[b] = tp[b] * scale[b]; \ - \ - tq += bands; \ - tp += bands; \ - } \ -} - -static int -vips_composite_unprescale_gen( VipsRegion *output_region, - void *seq, void *a, void *b, gboolean *stop ) -{ - VipsRegion *input_region = (VipsRegion *) seq; - VipsImage *in = (VipsImage *) a; - int bands = in->Bands; - double *scale = (double *) b; - VipsRect *r = &output_region->valid; - int sz = r->width * in->Bands; - - int x, b; - - if( vips_region_prepare( input_region, r ) ) - return( -1 ); - - VIPS_GATE_START( "vips_composite_unprescale_gen: work" ); - - for( int y = 0; y < r->height; y++ ) { - VipsPel *p = VIPS_REGION_ADDR( input_region, - r->left, r->top + y ); - VipsPel *q = VIPS_REGION_ADDR( output_region, - r->left, r->top + y ); - - switch( input_region->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - LOOP_UNPRESCALE( unsigned char, float ); break; - case VIPS_FORMAT_CHAR: - LOOP_UNPRESCALE( signed char, float ); break; - case VIPS_FORMAT_USHORT: - LOOP_UNPRESCALE( unsigned short, float ); break; - case VIPS_FORMAT_SHORT: - LOOP_UNPRESCALE( signed short, float ); break; - case VIPS_FORMAT_UINT: - LOOP_UNPRESCALE( unsigned int, float ); break; - case VIPS_FORMAT_INT: - LOOP_UNPRESCALE( signed int, float ); break; - case VIPS_FORMAT_FLOAT: - LOOP_UNPRESCALE( float, float ); break; - case VIPS_FORMAT_DOUBLE: - LOOP_UNPRESCALE( double, double ); break; - - default: - g_assert_not_reached(); - return( -1 ); - } - } - - VIPS_GATE_STOP( "vips_composite_unprescale_gen: work" ); - - return( 0 ); -} - -/* Undo vips_composite_prescale(). - */ -static int -vips_composite_unprescale( VipsImage *in, VipsImage **out, - gboolean premultiplied ) -{ - double *scale; - int i; - - if( vips_check_noncomplex( "vips_composite_unprescale", in ) ) - return( -1 ); - - *out = vips_image_new(); - - if( !(scale = VIPS_ARRAY( *out, double, in->Bands )) ) - return( -1 ); - if( vips_composite_prescale_max_band( in, scale ) ) { - vips_error( "vips_composite_unprescale", - "%s", _( "unsupported unprescale space" ) ); - return( -1 ); - } - for( i = 0; i < in->Bands; i++ ) - if( scale[i] != 0 ) - scale[i] = 1.0 / scale[i]; - - *out->BandFmt == in->BandFmt == VIPS_FORMAT_DOUBLE ? - VIPS_FORMAT_DOUBLE : VIPS_FORMAT_FLOAT; - if( vips_image_pipelinev( out, - VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) - return( -1 ); - if( vips_image_generate( out, - vips_start_one, vips_composite_unprescale_gen, vips_stop_one, - in, scale ) ) - return( -1 ); - - return( 0 ); -} +typedef float v4f __attribute__((vector_size(4 * sizeof(float)))); +#endif /*HAVE_VECTOR_ARITH*/ typedef struct _VipsComposite { VipsConversion parent_instance; @@ -511,10 +137,21 @@ typedef struct _VipsComposite { */ int n; - /* The number of bands we are blending. + /* 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*/ + } VipsComposite; typedef VipsConversionClass VipsCompositeClass; @@ -525,6 +162,92 @@ extern "C" { G_DEFINE_TYPE( VipsComposite, vips_composite, VIPS_TYPE_CONVERSION ); } +/* For each of the supported interpretations, the maximum value of each band. + */ +static int +vips_composite_max_band( VipsComposite *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] = 256; + break; + + default: + return( -1 ); + } + + return( 0 ); +} + /* Cairo naming conventions: * * aR alpha of result @@ -535,104 +258,22 @@ G_DEFINE_TYPE( VipsComposite, vips_composite, VIPS_TYPE_CONVERSION ); * xB colour band of source B */ -static double inline -vips_composite_alpha( VipsBlendMode mode, double aA, double aB ) -{ - double aR; - - switch( mode ) { - /* CLEAR and SOURCE are bounded operators and don't really make sense - * here, since we are always unbounded. Replace them with something - * similar that uses alpha. - */ - case VIPS_BLEND_MODE_CLEAR: - aR = 1 - aA; - break; - - case VIPS_BLEND_MODE_SOURCE: - aR = aA; - break; - - case VIPS_BLEND_MODE_OVER: - aR = aA + aB * (1.0 - aA); - break; - - case VIPS_BLEND_MODE_IN: - aR = aA * aB; - break; - - case VIPS_BLEND_MODE_OUT: - aR = aA * (1 - aB); - break; - - case VIPS_BLEND_MODE_ATOP: - aR = aB; - break; - - case VIPS_BLEND_MODE_DEST: - aR = aB; - break; - - case VIPS_BLEND_MODE_DEST_OVER: - aR = aB + aA * (1.0 - aB); - break; - - case VIPS_BLEND_MODE_DEST_IN: - aR = aA * aB; - break; - - case VIPS_BLEND_MODE_DEST_OUT: - aR = (1 - aA) * aB; - break; - - case VIPS_BLEND_MODE_DEST_ATOP: - aR = aA; - break; - - case VIPS_BLEND_MODE_XOR: - aR = aA + aB - 2 * aA * aB; - break; - - case VIPS_BLEND_MODE_ADD: - aR = VIPS_MIN( 1, aA + aB ); - break; - - case VIPS_BLEND_MODE_SATURATE: - aR = VIPS_MIN( 1, aA + aB ); - break; - - case VIPS_BLEND_MODE_MULTIPLY: - case VIPS_BLEND_MODE_SCREEN: - case VIPS_BLEND_MODE_OVERLAY: - case VIPS_BLEND_MODE_DARKEN: - case VIPS_BLEND_MODE_LIGHTEN: - case VIPS_BLEND_MODE_COLOUR_DODGE: - case VIPS_BLEND_MODE_COLOUR_BURN: - case VIPS_BLEND_MODE_HARD_LIGHT: - case VIPS_BLEND_MODE_SOFT_LIGHT: - case VIPS_BLEND_MODE_DIFFERENCE: - case VIPS_BLEND_MODE_EXCLUSION: - /* All the PDF modes have the same alpha function. - */ - aR = aA + aB * (1.0 - aA); - break; - - default: - aR = 0; - g_assert_not_reached(); - } - - return( aR ); -} - -/* A is the new pixel coming in, either float or double. B is the double pixel - * we are accumulating. Pixels are premultiplied. +/* 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_blend( VipsBlendMode mode, - double * restrict B, T * restrict A, int bands ) +vips_composite_blend( VipsComposite *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; @@ -640,108 +281,128 @@ vips_composite_blend( VipsBlendMode mode, double t2; double t3; double f[MAX_BANDS + 1]; - int b; + + /* Load and scale the pixel to 0 - 1. + */ + for( int b = 0; b <= bands; b++ ) + A[b] = p[b] / composite->max_band[b]; aA = A[bands]; aB = B[bands]; - aR = vips_composite_alpha( mode, aA, aB ); + + /* 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: - for( b = 0; b < bands; b++ ) + aR = 0; + for( int b = 0; b < bands; b++ ) B[b] = 0; break; case VIPS_BLEND_MODE_SOURCE: - for( b = 0; b < bands; b++ ) + 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( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) B[b] = A[b] + t1 * B[b]; break; case VIPS_BLEND_MODE_IN: - for( b = 0; b < bands; b++ ) + aR = aA * aB; + for( int b = 0; b < bands; b++ ) B[b] = A[b]; break; case VIPS_BLEND_MODE_OUT: - for( b = 0; b < bands; b++ ) + aR = aA * (1 - aB); + for( int b = 0; b < bands; b++ ) B[b] = A[b]; break; case VIPS_BLEND_MODE_ATOP: - if( aB == 0 ) - for( b = 0; b < bands; b++ ) - B[b] = A[b]; - else - for( b = 0; b < bands; b++ ) - B[b] = A[b] + (B[b] / aB) * (1 - aA); + 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( b = 0; b < bands; b++ ) + 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: - if( aB != 0 ) - for( b = 0; b < bands; b++ ) - B[b] = (A[b] / aB) * (1 - aB) + B[b]; + 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( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) B[b] = t1 * A[b] + t2 * B[b]; break; case VIPS_BLEND_MODE_ADD: - for( b = 0; b < bands; b++ ) + 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: - if( aA != 0 ) { - t1 = VIPS_MIN( aA, 1 - aB ); - for( b = 0; b < bands; b++ ) - B[b] = t1 * (A[b] / aA) + B[b]; - } + 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( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) f[b] = A[b] * B[b]; break; case VIPS_BLEND_MODE_SCREEN: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) f[b] = A[b] + B[b] - A[b] * B[b]; break; case VIPS_BLEND_MODE_OVERLAY: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) if( B[b] <= 0.5 ) f[b] = 2 * A[b] * B[b]; else @@ -749,17 +410,17 @@ vips_composite_blend( VipsBlendMode mode, break; case VIPS_BLEND_MODE_DARKEN: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) f[b] = VIPS_MIN( A[b], B[b] ); break; case VIPS_BLEND_MODE_LIGHTEN: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) f[b] = VIPS_MAX( A[b], B[b] ); break; case VIPS_BLEND_MODE_COLOUR_DODGE: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) if( A[b] < 1 ) f[b] = VIPS_MIN( 1, B[b] / (1 - A[b]) ); else @@ -767,7 +428,7 @@ vips_composite_blend( VipsBlendMode mode, break; case VIPS_BLEND_MODE_COLOUR_BURN: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) if( A[b] > 0 ) f[b] = 1 - VIPS_MIN( 1, (1 - B[b]) / A[b] ); @@ -776,7 +437,7 @@ vips_composite_blend( VipsBlendMode mode, break; case VIPS_BLEND_MODE_HARD_LIGHT: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) if( A[b] < 0.5 ) f[b] = 2 * A[b] * B[b]; else @@ -784,12 +445,11 @@ vips_composite_blend( VipsBlendMode mode, break; case VIPS_BLEND_MODE_SOFT_LIGHT: - for( b = 0; b < bands; b++ ) { + for( int b = 0; b < bands; b++ ) { 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] ); @@ -803,26 +463,25 @@ vips_composite_blend( VipsBlendMode mode, break; case VIPS_BLEND_MODE_DIFFERENCE: - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) f[b] = abs( B[b] - A[b] ); break; case VIPS_BLEND_MODE_EXCLUSION: - for( b = 0; b < bands; b++ ) + 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( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) B[b] = 0; } t1 = 1 - aB; t2 = 1 - aA; t3 = aA * aB; - - for( b = 0; b < bands; b++ ) + for( int b = 0; b < bands; b++ ) B[b] = t1 * A[b] + t2 * B[b] + t3 * f[b]; break; } @@ -833,19 +492,18 @@ vips_composite_blend( VipsBlendMode mode, /* 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)))); - -/* Special path for RGBA with float pixels. This is overwhelmingly the most +/* 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 float pixel coming + * B is the float pixel we are accumulating, A is the new pixel coming * in from memory. */ -static void inline -vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) +template +static void +vips_composite_blend3( VipsComposite *composite, + VipsBlendMode mode, v4f &B, T * restrict p ) { + v4f A; float aA; float aB; float aR; @@ -855,85 +513,108 @@ vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) v4f f; v4f g; - v4f A = *((v4f *) A_memory); + /* 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]; - aR = vips_composite_alpha( mode, aA, aB ); + + /* 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; - if( aB == 0 ) - B = A; - else - B = A + t1 * (B / aB); + 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 = t1 * A + B; + B = B + t1 * A; break; case VIPS_BLEND_MODE_DEST_IN: - B = B; + aR = aA * aB; + // B = B break; case VIPS_BLEND_MODE_DEST_OUT: - B = B; + aR = (1 - aA) * aB; + // B = B break; case VIPS_BLEND_MODE_DEST_ATOP: - t1 = 1 - aB; - if( aB != 0 ) - B = t1 * A + B; + 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 ); - if( aA != 0 ) - B = t1 * (A / aA) + B; + 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; @@ -945,7 +626,8 @@ vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) case VIPS_BLEND_MODE_OVERLAY: f = B <= 0.5 ? - 2 * A * B : 1 - 2 * (1 - A) * (1 - B); + 2 * A * B : + 1 - 2 * (1 - A) * (1 - B); break; case VIPS_BLEND_MODE_DARKEN: @@ -957,7 +639,9 @@ vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) break; case VIPS_BLEND_MODE_COLOUR_DODGE: - f = A < 1 ? VIPS_MIN( 1, B / (1 - A) ) : 1; + f = A < 1 ? + VIPS_MIN( 1, B / (1 - A) ) : + 1; break; case VIPS_BLEND_MODE_COLOUR_BURN: @@ -968,29 +652,27 @@ vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) case VIPS_BLEND_MODE_HARD_LIGHT: f = A < 0.5 ? - 2 * A * B : + 2 * A * B : 1 - 2 * (1 - A) * (1 - B); break; case VIPS_BLEND_MODE_SOFT_LIGHT: - /* sqrt does not work on vectors, you have to - * loop explicitly. + /* 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[b] = ((16 * B[b] - 12) * - B[b] + 4) * B[b]; - else if( B[b] >= 0 ) - g[b] = sqrt( B[b] ); - else - g[b] = 0; + 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[b]); + (g - B[b]); } break; @@ -1005,25 +687,27 @@ vips_composite_blend_3float( VipsBlendMode mode, v4f &B, float *A_memory ) default: g_assert_not_reached(); - B[0] = 0; - B[1] = 0; - B[2] = 0; - break; + for( int b = 0; b < 3; b++ ) + B[b] = 0; } t1 = 1 - aB; t2 = 1 - aA; t3 = aA * aB; B = t1 * A + t2 * B + t3 * f; + break; } B[3] = aR; } #endif /*HAVE_VECTOR_ARITH*/ -template -static void vips_combine_pixels( VipsComposite *composite, - VipsPel *q, VipsPel **p ) +/* 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( VipsComposite *composite, VipsPel *q, VipsPel **p ) { VipsBlendMode *m = (VipsBlendMode *) composite->mode->area.data; int n = composite->n; @@ -1032,35 +716,113 @@ static void vips_combine_pixels( VipsComposite *composite, 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[n - 1][b]; + B[b] = tp[n - 1][b] / composite->max_band[b]; + + aB = B[bands]; + if( !composite->premultiplied ) + for( int b = 0; b < bands; b++ ) + B[b] *= aB; for( int i = n - 2; i >= 0; i-- ) - vips_composite_blend( m[i], B, tp[i], bands ); + vips_composite_blend( composite, m[i], B, tp[i] ); - for( int b = 0; b <= bands; b++ ) - tq[b] = B[b]; + /* 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_pixels_3float( VipsComposite *composite, - VipsPel *q, VipsPel **p ) +vips_combine_pixels3( VipsComposite *composite, VipsPel *q, VipsPel **p ) { VipsBlendMode *m = (VipsBlendMode *) composite->mode->area.data; int n = composite->n; - float * restrict tq = (float * restrict) q; - float ** restrict tp = (float ** restrict) p; + T * restrict tq = (T * restrict) q; + T ** restrict tp = (T ** restrict) p; - v4f B, R; + v4f B; + float aB; - B = *((v4f *) tp[n - 1]); + B[0] = tp[n - 1][0]; + B[1] = tp[n - 1][1]; + B[2] = tp[n - 1][2]; + B[3] = tp[n - 1][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 = n - 2; i >= 0; i-- ) - vips_composite_blend_3float( m[i], B, tp[i] ); + vips_composite_blend3( composite, m[i], B, tp[i] ); - *((v4f *) tq) = B; + /* 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*/ @@ -1081,29 +843,82 @@ vips_composite_gen( VipsRegion *output_region, for( int y = 0; y < r->height; y++ ) { VipsPel *p[MAX_INPUT_IMAGES]; VipsPel *q; - int x, i; - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) p[i] = VIPS_REGION_ADDR( input_regions[i], r->left, r->top + y ); - p[i] = NULL; + p[composite->n] = NULL; q = VIPS_REGION_ADDR( output_region, r->left, r->top + y ); - for( x = 0; x < r->width; x++ ) { + for( int x = 0; x < r->width; x++ ) { switch( input_regions[0]->im->BandFmt ) { + case VIPS_FORMAT_UCHAR: +#ifdef HAVE_VECTOR_ARITH + if( composite->bands == 3 ) + vips_combine_pixels3 + + ( composite, q, p ); + else +#endif + vips_combine_pixels + + ( composite, q, p ); + break; + + case VIPS_FORMAT_CHAR: + vips_combine_pixels + + ( composite, q, p ); + break; + + case VIPS_FORMAT_USHORT: +#ifdef HAVE_VECTOR_ARITH + if( composite->bands == 3 ) + vips_combine_pixels3 + + ( composite, q, p ); + else +#endif + vips_combine_pixels + + ( composite, q, p ); + break; + + case VIPS_FORMAT_SHORT: + vips_combine_pixels + + ( composite, q, p ); + break; + + case VIPS_FORMAT_UINT: + vips_combine_pixels + + ( composite, q, p ); + break; + + case VIPS_FORMAT_INT: + vips_combine_pixels + + ( composite, q, p ); + break; + case VIPS_FORMAT_FLOAT: #ifdef HAVE_VECTOR_ARITH if( composite->bands == 3 ) - vips_combine_pixels_3float( composite, - q, p ); + vips_combine_pixels3 + + ( composite, q, p ); else -#endif /*HAVE_VECTOR_ARITH*/ - vips_combine_pixels( composite, - q, p ); +#endif + vips_combine_pixels + + ( composite, q, p ); break; case VIPS_FORMAT_DOUBLE: - vips_combine_pixels( composite, q, p ); + vips_combine_pixels + + ( composite, q, p ); break; default: @@ -1111,7 +926,7 @@ vips_composite_gen( VipsRegion *output_region, return( -1 ); } - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) p[i] += ps; q += ps; } @@ -1128,17 +943,13 @@ vips_composite_build( VipsObject *object ) VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object ); VipsConversion *conversion = VIPS_CONVERSION( object ); VipsComposite *composite = (VipsComposite *) object; - VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 ); - int i; VipsImage **in; VipsImage **decode; VipsImage **compositing; VipsImage **format; VipsImage **size; - VipsImage **prescale; VipsBlendMode *mode; - VipsImage *out; if( VIPS_OBJECT_CLASS( vips_composite_parent_class )->build( object ) ) return( -1 ); @@ -1156,7 +967,7 @@ vips_composite_build( VipsObject *object ) return( -1 ); } mode = (VipsBlendMode *) composite->mode->area.data; - for( i = 0; i < composite->n - 1; i++ ) { + for( int i = 0; i < composite->n - 1; i++ ) { if( mode[i] < 0 || mode[i] >= VIPS_BLEND_MODE_LAST ) { vips_error( klass->nickname, @@ -1169,7 +980,7 @@ vips_composite_build( VipsObject *object ) in = (VipsImage **) composite->in->area.data; decode = (VipsImage **) vips_object_local_array( object, composite->n ); - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) if( vips_image_decode( in[i], &decode[i] ) ) return( -1 ); in = decode; @@ -1177,7 +988,7 @@ vips_composite_build( VipsObject *object ) /* Are any of the images missing alpha? The first missing alpha is * given a solid 255 and becomes the background image, shortening n. */ - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) if( !vips_image_hasalpha( in[i] ) ) { VipsImage *x; double solid; @@ -1209,14 +1020,14 @@ vips_composite_build( VipsObject *object ) gboolean any_16; all_grey = TRUE; - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) if( in[i]->Bands > 2 ) { all_grey = FALSE; break; } any_16 = FALSE; - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) if( in[i]->Type == VIPS_INTERPRETATION_GREY16 || in[i]->Type == VIPS_INTERPRETATION_RGB16 ) { any_16 = TRUE; @@ -1234,7 +1045,7 @@ vips_composite_build( VipsObject *object ) compositing = (VipsImage **) vips_object_local_array( object, composite->n ); - for( i = 0; i < composite->n; i++ ) + for( int i = 0; i < composite->n; i++ ) if( vips_colourspace( in[i], &compositing[i], composite->compositing_space, NULL ) ) return( -1 ); @@ -1243,7 +1054,7 @@ vips_composite_build( VipsObject *object ) /* Check that they all now match in bands. This can fail for some * input combinations. */ - for( i = 1; i < composite->n; i++ ) + for( int i = 1; i < composite->n; i++ ) if( in[i]->Bands != in[0]->Bands ) { vips_error( klass->nickname, _( "image %d does not have %d bands" ), @@ -1259,15 +1070,19 @@ vips_composite_build( VipsObject *object ) composite->bands = in[0]->Bands - 1; - /* Prescale all bands to 0 - 1 range. + /* Set the max for each band now we know bands and compositing space. */ - prescale = (VipsImage **) - vips_object_local_array( object, composite->n ); - for( i = 0; i < composite->n; i++ ) - if( vips_composite_prescale( in[i], &prescale[i], - composite->premultiplied ) ) - return( -1 ); - in = prescale; + if( vips_composite_max_band( composite, composite->max_band ) ) { + vips_error( klass->nickname, + "%s", _( "unsupported compositing space" ) ); + return( -1 ); + } + +#ifdef HAVE_VECTOR_ARITH + 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 in size and format. We may have * mixed float and double, for example. @@ -1279,27 +1094,15 @@ vips_composite_build( VipsObject *object ) return( -1 ); in = size; - t[0] = vips_image_new(); - out = t[0]; - - if( vips_image_pipeline_array( out, + if( vips_image_pipeline_array( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, in ) ) return( -1 ); - if( vips_image_generate( out, + if( vips_image_generate( conversion->out, vips_start_many, vips_composite_gen, vips_stop_many, in, composite ) ) return( -1 ); - /* Scale all bands back to their full range again. - */ - if( vips_composite_unprescale( out, &t[1], composite->premultiplied ) ) - return( -1 ); - out = t[1]; - - if( vips_image_write( out, conversion->out ) ) - return( -1 ); - return( 0 ); } @@ -1376,9 +1179,9 @@ vips_compositev( VipsImage **in, VipsImage **out, int n, int *mode, va_list ap ) } /** - * vips_composite: + * vips_composite: (method) * @in: (array length=n) (transfer none): array of input images - * @out: output image + * @out: (out): output image * @n: number of input images * @mode: array of (@n - 1) #VipsBlendMode * @...: %NULL-terminated list of optional named arguments @@ -1413,7 +1216,7 @@ vips_compositev( VipsImage **in, VipsImage **out, int n, int *mode, va_list ap ) * 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. * - * Image are normally treated as unpremultiplied, so this oepration can be used + * Image are normally treated as unpremultiplied, so this operation can be used * directly on PNG images. If your images have been through vips_premultiply(), * set @premultiplied. * @@ -1435,10 +1238,10 @@ vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) } /** - * vips_composite2: + * vips_composite2: (method) * @in1: first input image * @in2: second input image - * @out: output image + * @out: (out): output image * @mode: composite with this blend mode * @...: %NULL-terminated list of optional named arguments * diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index dc70ef26..083fef5f 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -249,6 +249,11 @@ int vips_premultiply( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); 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, + VipsBlendMode mode1, ... ) + __attribute__((sentinel)); int vips_falsecolour( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel));