From aa45c81464a899511c07f5f0db808543090f2c9f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 8 Oct 2014 11:55:24 +0100 Subject: [PATCH] fix extra band handling in colour backport of new colour.c from 7.41 --- ChangeLog | 3 + configure.ac | 4 +- libvips/colour/Lab2LabQ.c | 2 +- libvips/colour/Lab2LabS.c | 2 +- libvips/colour/LabS2Lab.c | 2 +- libvips/colour/LabS2LabQ.c | 2 +- libvips/colour/colour.c | 279 +++++++++++++-------------------- libvips/colour/float2rad.c | 2 +- libvips/colour/icc_transform.c | 18 ++- libvips/colour/pcolour.h | 6 +- libvips/colour/sRGB2scRGB.c | 2 +- libvips/colour/scRGB2sRGB.c | 2 +- 12 files changed, 135 insertions(+), 189 deletions(-) diff --git a/ChangeLog b/ChangeLog index 083c3213..9246a308 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +8/10/14 started 7.40.10 +- rework extra band handling for colour functions + 8/9/14 started 7.40.10 - icc_import and icc_transform checks the input profile for compatibility with the image, thanks James diff --git a/configure.ac b/configure.ac index 1fe07266..1ab7c907 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [7.40.10], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [7.40.11], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -18,7 +18,7 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [7]) m4_define([vips_minor_version], [40]) -m4_define([vips_micro_version], [10]) +m4_define([vips_micro_version], [11]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) diff --git a/libvips/colour/Lab2LabQ.c b/libvips/colour/Lab2LabQ.c index 28a8b50d..a7563645 100644 --- a/libvips/colour/Lab2LabQ.c +++ b/libvips/colour/Lab2LabQ.c @@ -149,11 +149,11 @@ vips_Lab2LabQ_init( VipsLab2LabQ *Lab2LabQ ) colour->coding = VIPS_CODING_LABQ; colour->interpretation = VIPS_INTERPRETATION_LABQ; colour->format = VIPS_FORMAT_UCHAR; + colour->input_bands = 3; colour->bands = 4; code->input_coding = VIPS_CODING_NONE; code->input_format = VIPS_FORMAT_FLOAT; - code->input_bands = 3; } /** diff --git a/libvips/colour/Lab2LabS.c b/libvips/colour/Lab2LabS.c index 1c0cde11..95713c85 100644 --- a/libvips/colour/Lab2LabS.c +++ b/libvips/colour/Lab2LabS.c @@ -90,11 +90,11 @@ vips_Lab2LabS_init( VipsLab2LabS *Lab2LabS ) colour->interpretation = VIPS_INTERPRETATION_LABS; colour->format = VIPS_FORMAT_SHORT; + colour->input_bands = 3; colour->bands = 3; code->input_coding = VIPS_CODING_NONE; code->input_format = VIPS_FORMAT_FLOAT; - code->input_bands = 3; } /** diff --git a/libvips/colour/LabS2Lab.c b/libvips/colour/LabS2Lab.c index 4e6b8de2..a0d2c5bb 100644 --- a/libvips/colour/LabS2Lab.c +++ b/libvips/colour/LabS2Lab.c @@ -89,10 +89,10 @@ vips_LabS2Lab_init( VipsLabS2Lab *LabS2Lab ) colour->interpretation = VIPS_INTERPRETATION_LAB; colour->format = VIPS_FORMAT_FLOAT; colour->bands = 3; + colour->input_bands = 3; code->input_coding = VIPS_CODING_NONE; code->input_format = VIPS_FORMAT_SHORT; - code->input_bands = 3; } /** diff --git a/libvips/colour/LabS2LabQ.c b/libvips/colour/LabS2LabQ.c index 1af3e8cc..5f896a88 100644 --- a/libvips/colour/LabS2LabQ.c +++ b/libvips/colour/LabS2LabQ.c @@ -137,11 +137,11 @@ vips_LabS2LabQ_init( VipsLabS2LabQ *LabS2LabQ ) colour->coding = VIPS_CODING_LABQ; colour->interpretation = VIPS_INTERPRETATION_LABQ; colour->format = VIPS_FORMAT_UCHAR; + colour->input_bands = 3; colour->bands = 4; code->input_coding = VIPS_CODING_NONE; code->input_format = VIPS_FORMAT_SHORT; - code->input_bands = 3; } /** diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index 0ea7e6ee..8d085ddc 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -165,6 +165,12 @@ * This is a highly uniform colour space, much better than CIELAB for * expressing small differences. * + * The CMC colourspace is described in "Uniform Colour Space Based on the + * CMC(l:c) Colour-difference Formula", M R Luo and B Rigg, Journal of the + * Society of Dyers and Colourists, vol 102, 1986. Distances in this + * colourspace approximate, within 10% or so, differences in the CMC(l:c) + * colour difference formula. + * * You can calculate metrics like CMC(2:1) by scaling the spaces before * finding differences. * @@ -294,6 +300,10 @@ vips_colour_build( VipsObject *object ) VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsColour *colour = VIPS_COLOUR( object ); + VipsImage **in; + VipsImage **extra_bands; + VipsImage *out; + int i; #ifdef DEBUG @@ -306,8 +316,6 @@ vips_colour_build( VipsObject *object ) build( object ) ) return( -1 ); - g_object_set( colour, "out", vips_image_new(), NULL ); - if( colour->n > MAX_INPUT_IMAGES ) { vips_error( class->nickname, "%s", _( "too many input images" ) ); @@ -317,28 +325,102 @@ vips_colour_build( VipsObject *object ) if( vips_image_pio_input( colour->in[i] ) ) return( -1 ); - /* colour->in[] must be NULL-terminated, we use it as an arg to + /* colour->in[] must be NULL-terminated, we can use it as an arg to * vips_start_many(). */ g_assert( !colour->in[colour->n] ); - if( vips_image_pipeline_array( colour->out, - VIPS_DEMAND_STYLE_THINSTRIP, colour->in ) ) + in = colour->in; + extra_bands = (VipsImage **) + vips_object_local_array( object, colour->n ); + + /* If there are more than @input_bands bands, we detach and reattach + * after processing. + */ + if( colour->input_bands > 0 ) { + VipsImage **new_in = (VipsImage **) + vips_object_local_array( object, colour->n ); + + for( i = 0; i < colour->n; i++ ) { + if( vips_check_bands_atleast( class->nickname, + in[i], colour->input_bands ) ) + return( -1 ); + + if( vips_extract_band( in[i], &new_in[i], 0, + "n", colour->input_bands, + NULL ) ) + return( -1 ); + + if( in[i]->Bands > colour->input_bands ) + if( vips_extract_band( in[i], &extra_bands[i], + colour->input_bands, + "n", in[i]->Bands - + colour->input_bands, + NULL ) ) + return( -1 ); + } + + in = new_in; + } + + out = vips_image_new(); + if( vips_image_pipeline_array( out, + VIPS_DEMAND_STYLE_THINSTRIP, in ) ) { + g_object_unref( out ); return( -1 ); - colour->out->Coding = colour->coding; - colour->out->Type = colour->interpretation; - colour->out->BandFmt = colour->format; - colour->out->Bands = colour->bands; + } + out->Coding = colour->coding; + out->Type = colour->interpretation; + out->BandFmt = colour->format; + out->Bands = colour->bands; if( colour->profile_filename ) - if( vips_colour_attach_profile( colour->out, - colour->profile_filename ) ) + if( vips_colour_attach_profile( out, + colour->profile_filename ) ) { + g_object_unref( out ); return( -1 ); + } - if( vips_image_generate( colour->out, + if( vips_image_generate( out, vips_start_many, vips_colour_gen, vips_stop_many, - colour->in, colour ) ) + in, colour ) ) { + g_object_unref( out ); return( -1 ); + } + + /* Reattach higher bands, if necessary. If we have more than one input + * image, just use the first extra bands. + */ + for( i = 0; i < colour->n; i++ ) + if( extra_bands[i] ) { + VipsImage *t1, *t2; + + /* We can't just reattach the extra bands: they might + * be float (for example) and we might be trying to + * make a short image. Cast extra to match the body of + * the image. + */ + + if( vips_cast( extra_bands[i], &t1, out->BandFmt, + NULL ) ) { + g_object_unref( out ); + return( -1 ); + } + + if( vips_bandjoin2( out, t1, &t2, + NULL ) ) { + g_object_unref( t1 ); + g_object_unref( out ); + return( -1 ); + } + g_object_unref( out ); + g_object_unref( t1 ); + out = t2; + + break; + } + + g_object_set( colour, "out", out, NULL ); return( 0 ); } @@ -373,6 +455,7 @@ vips_colour_init( VipsColour *colour ) colour->interpretation = VIPS_INTERPRETATION_sRGB; colour->format = VIPS_FORMAT_UCHAR; colour->bands = 3; + colour->input_bands = -1; } G_DEFINE_ABSTRACT_TYPE( VipsColourSpace, vips_colour_space, VIPS_TYPE_COLOUR ); @@ -382,62 +465,24 @@ vips_colour_space_build( VipsObject *object ) { VipsColour *colour = VIPS_COLOUR( object ); VipsColourSpace *space = VIPS_COLOUR_SPACE( object ); - - VipsImage **t; - VipsImage *in; - VipsImage *extra; - - t = (VipsImage **) vips_object_local_array( object, 4 ); - - in = space->in; - extra = NULL; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); /* We only process float. */ - if( vips_cast_float( in, &t[0], NULL ) ) + if( vips_cast_float( space->in, &t[0], NULL ) ) return( -1 ); - in = t[0]; - /* If there are more than n bands, process just the first three and - * reattach the rest after. This lets us handle RGBA etc. + /* We always do 3 bands -> 3 bands. */ - if( in->Bands > 3 ) { - if( vips_extract_band( in, &t[1], 0, "n", 3, NULL ) || - vips_extract_band( in, &t[2], 3, - "n", in->Bands - 3, NULL ) ) - return( -1 ); - - in = t[1]; - extra = t[2]; - } - else if( vips_check_bands_atleast( - VIPS_OBJECT_GET_CLASS( object )->nickname, in, 3 ) ) - return( -1 ); + colour->input_bands = 3; colour->n = 1; - colour->in = (VipsImage **) vips_object_local_array( object, 2 ); - colour->in[0] = in; - colour->in[1] = NULL; - if( colour->in[0] ) - g_object_ref( colour->in[0] ); + colour->in = t; if( VIPS_OBJECT_CLASS( vips_colour_space_parent_class )-> build( object ) ) return( -1 ); - /* Reattach higher bands, if necessary. - */ - if( extra ) { - VipsImage *x; - - if( vips_bandjoin2( colour->out, extra, &x, NULL ) ) - return( -1 ); - - VIPS_UNREF( colour->out ); - - colour->out = x; - } - return( 0 ); } @@ -486,11 +531,8 @@ vips_colour_code_build( VipsObject *object ) VipsImage **t = (VipsImage **) vips_object_local_array( object, 6 ); VipsImage *in; - VipsImage *extra; - in = code->in; - extra = NULL; /* If this is a LABQ and the coder wants uncoded, unpack. */ @@ -507,30 +549,6 @@ vips_colour_code_build( VipsObject *object ) in, code->input_coding ) ) return( -1 ); - /* Extra band processing. don't do automatic detach/reattach if either - * input or output will be coded. - */ - if( in && - code->input_coding == VIPS_CODING_NONE && - colour->coding == VIPS_CODING_NONE && - code->input_bands > 0 ) { - if( in->Bands > code->input_bands ) { - if( vips_extract_band( in, &t[1], 0, - "n", code->input_bands, NULL ) ) - return( -1 ); - if( vips_extract_band( in, &t[2], code->input_bands, - "n", in->Bands - code->input_bands, - NULL ) ) - return( -1 ); - in = t[1]; - extra = t[2]; - } - else if( vips_check_bands_atleast( - VIPS_OBJECT_CLASS( class )->nickname, - in, code->input_bands ) ) - return( -1 ); - } - if( in && code->input_coding == VIPS_CODING_NONE && code->input_format != VIPS_FORMAT_NOTSET ) { @@ -549,33 +567,14 @@ vips_colour_code_build( VipsObject *object ) } colour->n = 1; - colour->in = (VipsImage **) vips_object_local_array( object, 2 ); + colour->in = VIPS_ARRAY( object, 2, VipsImage * ); colour->in[0] = in; colour->in[1] = NULL; - if( colour->in[0] ) - g_object_ref( colour->in[0] ); - if( VIPS_OBJECT_CLASS( vips_colour_space_parent_class )-> + if( VIPS_OBJECT_CLASS( vips_colour_code_parent_class )-> build( object ) ) return( -1 ); - /* Reattach higher bands, if necessary. - * - * Our processing on the first three bands may have changed the image - * format. For example, converting LAB to LABS will make a short - * image. We need to force the extra bands to match this new type. - */ - if( extra ) { - VipsImage *x; - - if( vips_cast( extra, &t[5], colour->out->BandFmt, NULL ) || - vips_bandjoin2( colour->out, t[5], &x, NULL ) ) - return( -1 ); - - VIPS_UNREF( colour->out ); - colour->out = x; - } - return( 0 ); } @@ -613,19 +612,15 @@ vips_colour_difference_build( VipsObject *object ) { VipsColour *colour = VIPS_COLOUR( object ); VipsColourDifference *difference = VIPS_COLOUR_DIFFERENCE( object ); - VipsColourDifferenceClass *class = - VIPS_COLOUR_DIFFERENCE_GET_CLASS( object ); VipsImage **t; VipsImage *left; VipsImage *right; - VipsImage *extra; t = (VipsImage **) vips_object_local_array( object, 12 ); left = difference->left; right = difference->right; - extra = NULL; if( left ) { if( vips_image_decode( left, &t[0] ) ) @@ -639,52 +634,9 @@ vips_colour_difference_build( VipsObject *object ) right = t[1]; } - /* Detach and reattach extra bands, if any. If both left and right - * have extra bands, give up. + /* Detach and reattach any extra bands. */ - if( left && - left->Bands > 3 && - right && - right->Bands > 3 ) { - vips_error( VIPS_OBJECT_CLASS( class )->nickname, - "%s", "both images have extra bands" ); - return( -1 ); - } - - if( left && - left->Bands > 3 ) { - if( vips_extract_band( left, &t[2], 0, - "n", 3, - NULL ) ) - return( -1 ); - if( vips_extract_band( left, &t[3], 3, - "n", left->Bands - 3, - NULL ) ) - return( -1 ); - left = t[2]; - extra = t[3]; - } - - if( right && - right->Bands > 3 ) { - if( vips_extract_band( right, &t[4], 0, - "n", 3, - NULL ) ) - return( -1 ); - if( vips_extract_band( right, &t[5], 3, - "n", right->Bands - 3, - NULL ) ) - return( -1 ); - right = t[4]; - extra = t[5]; - } - - if( vips_check_bands_atleast( VIPS_OBJECT_CLASS( class )->nickname, - left, 3 ) ) - return( -1 ); - if( vips_check_bands_atleast( VIPS_OBJECT_CLASS( class )->nickname, - right, 3 ) ) - return( -1 ); + colour->input_bands = 3; if( vips_colourspace( left, &t[6], difference->interpretation, NULL ) ) return( -1 ); @@ -708,32 +660,15 @@ vips_colour_difference_build( VipsObject *object ) right = t[11]; colour->n = 2; - colour->in = (VipsImage **) vips_object_local_array( object, 3 ); + colour->in = VIPS_ARRAY( object, 3, VipsImage * ); colour->in[0] = left; colour->in[1] = right; colour->in[2] = NULL; - if( colour->in[0] ) - g_object_ref( colour->in[0] ); - if( colour->in[1] ) - g_object_ref( colour->in[1] ); - if( VIPS_OBJECT_CLASS( vips_colour_space_parent_class )-> + if( VIPS_OBJECT_CLASS( vips_colour_difference_parent_class )-> build( object ) ) return( -1 ); - /* Reattach higher bands, if necessary. - */ - if( extra ) { - VipsImage *x; - - if( vips_bandjoin2( colour->out, extra, &x, NULL ) ) - return( -1 ); - - VIPS_UNREF( colour->out ); - - colour->out = x; - } - return( 0 ); } diff --git a/libvips/colour/float2rad.c b/libvips/colour/float2rad.c index ebac3fff..fecaa669 100644 --- a/libvips/colour/float2rad.c +++ b/libvips/colour/float2rad.c @@ -212,11 +212,11 @@ vips_float2rad_init( VipsFloat2rad *float2rad ) colour->coding = VIPS_CODING_RAD; colour->interpretation = VIPS_INTERPRETATION_scRGB; colour->format = VIPS_FORMAT_UCHAR; + colour->input_bands = 3; colour->bands = 4; code->input_coding = VIPS_CODING_NONE; code->input_format = VIPS_FORMAT_FLOAT; - code->input_bands = 3; } /** diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index 5181b58c..1d902176 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -241,7 +241,7 @@ vips_icc_build( VipsObject *object ) code->in ) { switch( cmsGetColorSpace( icc->in_profile ) ) { case cmsSigRgbData: - code->input_bands = 3; + colour->input_bands = 3; code->input_format = code->in->BandFmt == VIPS_FORMAT_USHORT ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; @@ -252,7 +252,7 @@ vips_icc_build( VipsObject *object ) #ifdef HAVE_LCMS2 case cmsSigGrayData: - code->input_bands = 1; + colour->input_bands = 1; code->input_format = code->in->BandFmt == VIPS_FORMAT_USHORT ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; @@ -263,7 +263,7 @@ vips_icc_build( VipsObject *object ) #endif /*HAVE_LCMS2*/ case cmsSigCmykData: - code->input_bands = 4; + colour->input_bands = 4; code->input_format = code->in->BandFmt == VIPS_FORMAT_USHORT ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; @@ -273,7 +273,7 @@ vips_icc_build( VipsObject *object ) break; case cmsSigLabData: - code->input_bands = 3; + colour->input_bands = 3; code->input_format = VIPS_FORMAT_FLOAT; code->input_interpretation = VIPS_INTERPRETATION_LAB; @@ -281,7 +281,7 @@ vips_icc_build( VipsObject *object ) break; case cmsSigXYZData: - code->input_bands = 3; + colour->input_bands = 3; code->input_format = VIPS_FORMAT_FLOAT; icc->in_icc_format = TYPE_XYZ_16; break; @@ -506,7 +506,9 @@ vips_icc_load_profile_image( const char *domain, VipsImage *image ) return( NULL ); } - if( image->Bands != vips_icc_profile_needs_bands( profile ) ) { + /* We allow extra bands for eg. alpha. + */ + if( image->Bands < vips_icc_profile_needs_bands( profile ) ) { VIPS_FREEF( cmsCloseProfile, profile ); vips_warn( domain, "%s", _( "embedded profile incompatible with image" ) ); @@ -528,7 +530,9 @@ vips_icc_load_profile_file( const char *domain, return( NULL ); } - if( image->Bands != vips_icc_profile_needs_bands( profile ) ) { + /* We allow extra bands for eg. alpha. + */ + if( image->Bands < vips_icc_profile_needs_bands( profile ) ) { VIPS_FREEF( cmsCloseProfile, profile ); vips_warn( domain, _( "profile \"%s\" incompatible with image" ), diff --git a/libvips/colour/pcolour.h b/libvips/colour/pcolour.h index 7bfc2ad9..4d9f4f6c 100644 --- a/libvips/colour/pcolour.h +++ b/libvips/colour/pcolour.h @@ -65,6 +65,11 @@ typedef struct _VipsColour { VipsImage **in; int n; + /* If this is >0, only process this many bands from the input. Extra + * bands are removed and reattached after processing. + */ + int input_bands; + VipsImage *out; /* Set fields on ->out from these. @@ -149,7 +154,6 @@ typedef struct _VipsColourCode { */ VipsCoding input_coding; VipsBandFormat input_format; - int input_bands; VipsInterpretation input_interpretation; } VipsColourCode; diff --git a/libvips/colour/sRGB2scRGB.c b/libvips/colour/sRGB2scRGB.c index e254428e..c8b21aad 100644 --- a/libvips/colour/sRGB2scRGB.c +++ b/libvips/colour/sRGB2scRGB.c @@ -163,10 +163,10 @@ vips_sRGB2scRGB_init( VipssRGB2scRGB *sRGB2scRGB ) colour->coding = VIPS_CODING_NONE; colour->interpretation = VIPS_INTERPRETATION_scRGB; colour->format = VIPS_FORMAT_FLOAT; + colour->input_bands = 3; colour->bands = 3; code->input_coding = VIPS_CODING_NONE; - code->input_bands = 3; /* The default. This can get changed above ^^ if we see a * 16-bit input. diff --git a/libvips/colour/scRGB2sRGB.c b/libvips/colour/scRGB2sRGB.c index 2db8a058..0ae1f8f6 100644 --- a/libvips/colour/scRGB2sRGB.c +++ b/libvips/colour/scRGB2sRGB.c @@ -208,10 +208,10 @@ vips_scRGB2sRGB_init( VipsscRGB2sRGB *scRGB2sRGB ) colour->coding = VIPS_CODING_NONE; colour->interpretation = VIPS_INTERPRETATION_sRGB; colour->format = VIPS_FORMAT_UCHAR; + colour->input_bands = 3; colour->bands = 3; code->input_coding = VIPS_CODING_NONE; - code->input_bands = 3; code->input_format = VIPS_FORMAT_FLOAT; scRGB2sRGB->depth = 8;