From 03dac2a2df920be186c63e5e8565b2443dbb169e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 8 Jan 2021 14:19:32 +0000 Subject: [PATCH] detect bad profiles during profile load and add some extra dbg to print profile properties see https://github.com/libvips/libvips/issues/1956 --- libvips/colour/icc_transform.c | 149 ++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 39 deletions(-) diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index 1caa8a58..ecb1b43c 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -71,6 +71,10 @@ These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ + +/* +#define DEBUG + */ #ifdef HAVE_CONFIG_H #include @@ -81,7 +85,6 @@ #include #include -#include /* Has to be before VIPS to avoid nameclashes. */ @@ -417,20 +420,6 @@ typedef VipsIccClass VipsIccImportClass; G_DEFINE_TYPE( VipsIccImport, vips_icc_import, VIPS_TYPE_ICC ); -static void -vips_check_intent( const char *domain, - cmsHPROFILE profile, VipsIntent intent, int direction ) -{ - if( profile && - !cmsIsIntentSupported( profile, intent, direction ) ) - g_warning( _( "%s: intent %d (%s) not supported by " - "%s profile; falling back to default intent" ), - domain, - intent, vips_enum_nick( VIPS_TYPE_INTENT, intent ), - direction == LCMS_USED_AS_INPUT ? - _( "input" ) : _( "output" ) ); -} - static int vips_icc_profile_needs_bands( cmsHPROFILE profile ) { @@ -607,10 +596,76 @@ vips_icc_get_profile_image( VipsImage *image ) return( vips_blob_new( NULL, data, size ) ); } -/* Load a profile from a blob and check compatibility. +#ifdef DEBUG +static void +vips_icc_print_profile( const char *name, cmsHPROFILE profile ) +{ + static const cmsInfoType info_types[] = { + cmsInfoDescription, + cmsInfoManufacturer, + cmsInfoModel, + cmsInfoCopyright + }; + static const char *info_names[] = { + "description", + "manufacturer", + "model", + "copyright" + }; + + int i; + cmsUInt32Number n_bytes; + cmsUInt32Number n_intents; + cmsUInt32Number *intent_codes; + char **intent_descriptions; + + printf( "icc profile %s: %p\n", name, profile ); + for( i = 0; i < VIPS_NUMBER( info_types ); i++ ) { + if( (n_bytes = cmsGetProfileInfoASCII( profile, + info_types[i], "en", "US", + NULL, 0 )) ) { + char *buffer; + + buffer = VIPS_ARRAY( NULL, n_bytes, char ); + (void) cmsGetProfileInfoASCII( profile, + info_types[i], "en", "US", + buffer, n_bytes ); + printf( "%s: %s\n", info_names[i], buffer ); + g_free( buffer ); + } + } + + printf( "profile class: %#x\n", cmsGetDeviceClass( profile ) ); + printf( "PCS: %#x\n", cmsGetPCS( profile ) ); + + printf( "matrix shaper: %d\n", cmsIsMatrixShaper( profile ) ); + printf( "version: %g\n", cmsGetProfileVersion( profile ) ); + + n_intents = cmsGetSupportedIntents( 0, NULL, NULL ); + printf( "n_intents = %u\n", n_intents ); + intent_codes = VIPS_ARRAY( NULL, n_intents, cmsUInt32Number ); + intent_descriptions = VIPS_ARRAY( NULL, n_intents, char * ); + (void) cmsGetSupportedIntents( n_intents, + intent_codes, intent_descriptions ); + for( i = 0; i < n_intents; i++ ) { + printf( " %#x: %s, in CLUT = %d, out CLUT = %d\n", + intent_codes[i], intent_descriptions[i], + cmsIsCLUT( profile, + intent_codes[i], LCMS_USED_AS_INPUT ), + cmsIsCLUT( profile, + intent_codes[i], LCMS_USED_AS_OUTPUT ) ); + } + g_free( intent_codes ); + g_free( intent_descriptions ); +} +#endif /*DEBUG*/ + +/* Load a profile from a blob and check compatibility with image, intent and + * direction. */ static cmsHPROFILE -vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image ) +vips_icc_load_profile_blob( VipsBlob *blob, + VipsImage *image, VipsIntent intent, int direction ) { const void *data; size_t size; @@ -622,6 +677,10 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image ) return( NULL ); } +#ifdef DEBUG + vips_icc_print_profile( "from blob", profile ); +#endif /*DEBUG*/ + if( image && vips_image_expected_bands( image ) != vips_icc_profile_needs_bands( profile ) ) { @@ -629,6 +688,7 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image ) g_warning( "%s", _( "profile incompatible with image" ) ); return( NULL ); } + if( image && vips_image_expected_sig( image ) != cmsGetColorSpace( profile ) ) { @@ -638,6 +698,15 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image ) return( NULL ); } + if( !cmsIsIntentSupported( profile, intent, direction ) ) { + VIPS_FREEF( cmsCloseProfile, profile ); + g_warning( _( "%s profile does not support %s intent" ), + direction == LCMS_USED_AS_INPUT ? + _( "input" ) : _( "output" ), + vips_enum_nick( VIPS_TYPE_INTENT, intent ) ); + return( NULL ); + } + return( profile ); } @@ -646,12 +715,14 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image ) * unref the blob if it's useless. */ static cmsHPROFILE -vips_icc_verify_blob( VipsBlob **blob, VipsImage *image ) +vips_icc_verify_blob( VipsBlob **blob, + VipsImage *image, VipsIntent intent, int direction ) { if( *blob ) { cmsHPROFILE profile; - if( !(profile = vips_icc_load_profile_blob( *blob, image )) ) { + if( !(profile = vips_icc_load_profile_blob( *blob, + image, intent, direction )) ) { vips_area_unref( (VipsArea *) *blob ); *blob = NULL; } @@ -689,7 +760,8 @@ vips_icc_import_build( VipsObject *object ) !import->input_profile_filename) ) { icc->in_blob = vips_icc_get_profile_image( code->in ); icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, code->in ); + vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT ); } if( code->in && @@ -699,7 +771,8 @@ vips_icc_import_build( VipsObject *object ) &icc->in_blob, NULL ) ) return( -1 ); icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, code->in ); + vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT ); used_fallback = TRUE; } @@ -708,9 +781,6 @@ vips_icc_import_build( VipsObject *object ) return( -1 ); } - vips_check_intent( class->nickname, - icc->in_profile, icc->intent, LCMS_USED_AS_INPUT ); - if( icc->pcs == VIPS_PCS_LAB ) { cmsCIExyY white; cmsWhitePointFromTemp( &white, 6500 ); @@ -896,16 +966,12 @@ vips_icc_export_build( VipsObject *object ) } if( icc->out_blob && - !(icc->out_profile = - vips_icc_load_profile_blob( icc->out_blob, NULL )) ) { + !(icc->out_profile = vips_icc_load_profile_blob( icc->out_blob, + NULL, icc->intent, LCMS_USED_AS_OUTPUT )) ) { vips_error( class->nickname, "%s", _( "no output profile" ) ); return( -1 ); } - if( icc->out_profile ) - vips_check_intent( class->nickname, - icc->out_profile, icc->intent, LCMS_USED_AS_OUTPUT ); - if( VIPS_OBJECT_CLASS( vips_icc_export_parent_class )->build( object ) ) return( -1 ); @@ -1093,7 +1159,8 @@ vips_icc_transform_build( VipsObject *object ) !transform->input_profile_filename) ) { icc->in_blob = vips_icc_get_profile_image( code->in ); icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, code->in ); + vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT ); } if( code->in && @@ -1103,7 +1170,8 @@ vips_icc_transform_build( VipsObject *object ) &icc->in_blob, NULL ) ) return( -1 ); icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, code->in ); + vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT ); } if( !icc->in_profile ) { @@ -1124,19 +1192,14 @@ vips_icc_transform_build( VipsObject *object ) } if( icc->out_blob ) - icc->out_profile = - vips_icc_load_profile_blob( icc->out_blob, NULL ); + icc->out_profile = vips_icc_load_profile_blob( icc->out_blob, + NULL, icc->intent, LCMS_USED_AS_OUTPUT ); if( !icc->out_profile ) { vips_error( class->nickname, "%s", _( "no output profile" ) ); return( -1 ); } - vips_check_intent( class->nickname, - icc->in_profile, icc->intent, LCMS_USED_AS_INPUT ); - vips_check_intent( class->nickname, - icc->out_profile, icc->intent, LCMS_USED_AS_OUTPUT ); - if( VIPS_OBJECT_CLASS( vips_icc_transform_parent_class )-> build( object ) ) return( -1 ); @@ -1234,6 +1297,10 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename ) if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) ) return( -1 ); +#ifdef DEBUG + vips_icc_print_profile( profile_filename, profile ); +#endif /*DEBUG*/ + if( !(media = cmsReadTag( profile, cmsSigMediaWhitePointTag )) ) { vips_error( "vips_icc_ac2rc", "%s", _( "unable to get media white point" ) ); @@ -1293,6 +1360,10 @@ vips_icc_is_compatible_profile( VipsImage *image, */ return( FALSE ); +#ifdef DEBUG + vips_icc_print_profile( "from memory", profile ); +#endif /*DEBUG*/ + if( vips_image_expected_bands( image ) != vips_icc_profile_needs_bands( profile ) ) { VIPS_FREEF( cmsCloseProfile, profile );