From b1827128eace23b164b289165ef39d5b38d068c1 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 29 Sep 2014 11:04:53 +0100 Subject: [PATCH] icc checks input profile compat check the input profile colourspaces matches the image ... for example, if a CMYK image has an embedded RGB profile, fall back to the specified external profile see: https://github.com/jcupitt/libvips/issues/183 --- ChangeLog | 4 + configure.ac | 6 +- libvips/colour/icc_transform.c | 147 ++++++++++++++++++++++----------- 3 files changed, 108 insertions(+), 49 deletions(-) diff --git a/ChangeLog b/ChangeLog index b789165f..aa88247d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +8/9/14 started 7.40.10 +- icc_import and icc_transform checks the input profile for compatibility + with the image, thanks James + 8/9/14 started 7.40.9 - support jfif resunit "none" - support GRAY as an input and output ICC space diff --git a/configure.ac b/configure.ac index ef43a7f8..1fe07266 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.9], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [7.40.10], [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], [9]) +m4_define([vips_micro_version], [10]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -38,7 +38,7 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes not backwards compatible?: reset age to 0 LIBRARY_CURRENT=38 -LIBRARY_REVISION=6 +LIBRARY_REVISION=7 LIBRARY_AGE=0 # patched into include/vips/version.h diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index 6203f65a..5181b58c 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -28,6 +28,9 @@ * - support XYZ as an alternative PCS * 10/9/14 * - support GRAY as an input and output space + * 29/9/14 + * - check input profiles for compatibility with the input image, thanks + * James */ /* @@ -456,6 +459,86 @@ vips_check_intent( const char *domain, _( "input" ) : _( "output" ) ); } +static int +vips_icc_profile_needs_bands( cmsHPROFILE profile ) +{ + int needs_bands; + + switch( cmsGetColorSpace( profile ) ) { +#ifdef HAVE_LCMS2 + case cmsSigGrayData: + needs_bands = 1; + break; +#endif /*HAVE_LCMS2*/ + + case cmsSigRgbData: + case cmsSigLabData: + case cmsSigXYZData: + needs_bands = 3; + break; + + case cmsSigCmykData: + needs_bands = 4; + break; + + default: + needs_bands = -1; + break; + } + + return( needs_bands ); +} + +static cmsHPROFILE +vips_icc_load_profile_image( const char *domain, VipsImage *image ) +{ + void *data; + size_t data_length; + cmsHPROFILE profile; + + if( !vips_image_get_typeof( image, VIPS_META_ICC_NAME ) ) + return( NULL ); + + if( vips_image_get_blob( image, VIPS_META_ICC_NAME, + &data, &data_length ) || + !(profile = cmsOpenProfileFromMem( data, data_length )) ) { + vips_warn( domain, "%s", _( "corrupt embedded profile" ) ); + return( NULL ); + } + + if( image->Bands != vips_icc_profile_needs_bands( profile ) ) { + VIPS_FREEF( cmsCloseProfile, profile ); + vips_warn( domain, + "%s", _( "embedded profile incompatible with image" ) ); + return( NULL ); + } + + return( profile ); +} + +static cmsHPROFILE +vips_icc_load_profile_file( const char *domain, + VipsImage *image, const char *filename ) +{ + cmsHPROFILE profile; + + if( !(profile = cmsOpenProfileFromFile( filename, "r" )) ) { + vips_error( domain, + _( "unable to open profile \"%s\"" ), filename ); + return( NULL ); + } + + if( image->Bands != vips_icc_profile_needs_bands( profile ) ) { + VIPS_FREEF( cmsCloseProfile, profile ); + vips_warn( domain, + _( "profile \"%s\" incompatible with image" ), + filename ); + return( NULL ); + } + + return( profile ); +} + static int vips_icc_import_build( VipsObject *object ) { @@ -477,30 +560,16 @@ vips_icc_import_build( VipsObject *object ) if( code->in && (import->embedded || - !import->input_profile_filename) && - vips_image_get_typeof( code->in, VIPS_META_ICC_NAME ) ) { - void *data; - size_t data_length; + !import->input_profile_filename) ) + icc->in_profile = vips_icc_load_profile_image( class->nickname, + code->in ); - if( vips_image_get_blob( code->in, VIPS_META_ICC_NAME, - &data, &data_length ) || - !(icc->in_profile = cmsOpenProfileFromMem( - data, data_length )) ) { - vips_error( class->nickname, - "%s", _( "unable to load embedded profile" ) ); - return( -1 ); - } - } - else if( import->input_profile_filename ) { - if( !(icc->in_profile = cmsOpenProfileFromFile( - import->input_profile_filename, "r" )) ) { - vips_error( class->nickname, - _( "unable to open profile \"%s\"" ), - import->input_profile_filename ); - return( -1 ); - } - } - else { + if( !icc->in_profile && + import->input_profile_filename ) + icc->in_profile = vips_icc_load_profile_file( class->nickname, + code->in, import->input_profile_filename ); + + if( !icc->in_profile ) { vips_error( class->nickname, "%s", _( "no input profile" ) ); return( -1 ); } @@ -902,30 +971,16 @@ vips_icc_transform_build( VipsObject *object ) if( code->in && (transform->embedded || - !transform->input_profile_filename) && - vips_image_get_typeof( code->in, VIPS_META_ICC_NAME ) ) { - void *data; - size_t data_length; + !transform->input_profile_filename) ) + icc->in_profile = vips_icc_load_profile_image( class->nickname, + code->in ); - if( vips_image_get_blob( code->in, VIPS_META_ICC_NAME, - &data, &data_length ) || - !(icc->in_profile = cmsOpenProfileFromMem( - data, data_length )) ) { - vips_error( class->nickname, - "%s", _( "unable to load embedded profile" ) ); - return( -1 ); - } - } - else if( transform->input_profile_filename ) { - if( !(icc->in_profile = cmsOpenProfileFromFile( - transform->input_profile_filename, "r" )) ) { - vips_error( class->nickname, - _( "unable to open profile \"%s\"" ), - transform->input_profile_filename ); - return( -1 ); - } - } - else { + if( !icc->in_profile && + transform->input_profile_filename ) + icc->in_profile = vips_icc_load_profile_file( class->nickname, + code->in, transform->input_profile_filename ); + + if( !icc->in_profile ) { vips_error( class->nickname, "%s", _( "no input profile" ) ); return( -1 ); }