From 03dac2a2df920be186c63e5e8565b2443dbb169e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 8 Jan 2021 14:19:32 +0000 Subject: [PATCH 1/3] 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 ); From a046f260954df0a93d9f20c63ff97f40bf5f9505 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Apr 2021 17:44:25 +0100 Subject: [PATCH 2/3] better falling back on profile load This patch makes the icc operations fall back to the built-in profiles more gracefully. --- libvips/colour/icc_transform.c | 428 ++++++++++++++++----------------- libvips/colour/profile_load.c | 5 +- 2 files changed, 213 insertions(+), 220 deletions(-) diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index ecb1b43c..9d6e4cb8 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -163,7 +163,7 @@ typedef struct _VipsIcc { cmsUInt32Number in_icc_format; cmsUInt32Number out_icc_format; cmsHTRANSFORM trans; - + gboolean non_standard_input_profile; } VipsIcc; typedef VipsColourCodeClass VipsIccClass; @@ -369,84 +369,85 @@ vips_icc_build( VipsObject *object ) return( 0 ); } -static void -vips_icc_class_init( VipsIccClass *class ) +/* Get from an image. + */ +static VipsBlob * +vips_icc_get_profile_image( VipsImage *image ) { - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *object_class = (VipsObjectClass *) class; + const void *data; + size_t size; - gobject_class->dispose = vips_icc_dispose; - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; + if( !vips_image_get_typeof( image, VIPS_META_ICC_NAME ) ) + return( NULL ); + if( vips_image_get_blob( image, VIPS_META_ICC_NAME, &data, &size ) ) + return( NULL ); - object_class->nickname = "icc"; - object_class->description = _( "transform using ICC profiles" ); - object_class->build = vips_icc_build; - - VIPS_ARG_ENUM( class, "intent", 6, - _( "Intent" ), - _( "Rendering intent" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsIcc, intent ), - VIPS_TYPE_INTENT, VIPS_INTENT_RELATIVE ); - - VIPS_ARG_ENUM( class, "pcs", 6, - _( "PCS" ), - _( "Set Profile Connection Space" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsIcc, pcs ), - VIPS_TYPE_PCS, VIPS_PCS_LAB ); - - cmsSetLogErrorHandler( icc_error ); + return( vips_blob_new( NULL, data, size ) ); } +#ifdef DEBUG static void -vips_icc_init( VipsIcc *icc ) +vips_icc_print_profile( const char *name, cmsHPROFILE profile ) { - icc->intent = VIPS_INTENT_RELATIVE; - icc->pcs = VIPS_PCS_LAB; - icc->depth = 8; -} + static const cmsInfoType info_types[] = { + cmsInfoDescription, + cmsInfoManufacturer, + cmsInfoModel, + cmsInfoCopyright + }; + static const char *info_names[] = { + "description", + "manufacturer", + "model", + "copyright" + }; -typedef struct _VipsIccImport { - VipsIcc parent_instance; + 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; - gboolean embedded; - char *input_profile_filename; - -} VipsIccImport; - -typedef VipsIccClass VipsIccImportClass; - -G_DEFINE_TYPE( VipsIccImport, vips_icc_import, VIPS_TYPE_ICC ); - -static int -vips_icc_profile_needs_bands( cmsHPROFILE profile ) -{ - int needs_bands; - - switch( cmsGetColorSpace( profile ) ) { - case cmsSigGrayData: - needs_bands = 1; - break; - - case cmsSigRgbData: - case cmsSigLabData: - case cmsSigXYZData: - needs_bands = 3; - break; - - case cmsSigCmykData: - needs_bands = 4; - break; - - default: - needs_bands = -1; - break; + 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 ); + } } - return( needs_bands ); + 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*/ /* How many bands we expect to see from an image after preprocessing by our * parent classes. This is a bit fragile :-( @@ -499,6 +500,34 @@ vips_image_expected_bands( VipsImage *image ) return( expected_bands ); } +static int +vips_icc_profile_needs_bands( cmsHPROFILE profile ) +{ + int needs_bands; + + switch( cmsGetColorSpace( profile ) ) { + case cmsSigGrayData: + needs_bands = 1; + break; + + case cmsSigRgbData: + case cmsSigLabData: + case cmsSigXYZData: + needs_bands = 3; + break; + + case cmsSigCmykData: + needs_bands = 4; + break; + + default: + needs_bands = -1; + break; + } + + return( needs_bands ); +} + /* What cmsColorSpaceSignature do we expect this image to be (roughly) after * preprocessing. Again, fragile :( see the FIXME above. */ @@ -580,86 +609,6 @@ vips_image_expected_sig( VipsImage *image ) return( expected_sig ); } -/* Get from an image. - */ -static VipsBlob * -vips_icc_get_profile_image( VipsImage *image ) -{ - const void *data; - size_t size; - - if( !vips_image_get_typeof( image, VIPS_META_ICC_NAME ) ) - return( NULL ); - if( vips_image_get_blob( image, VIPS_META_ICC_NAME, &data, &size ) ) - return( NULL ); - - return( vips_blob_new( NULL, data, size ) ); -} - -#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. */ @@ -733,47 +682,63 @@ vips_icc_verify_blob( VipsBlob **blob, return( NULL ); } +/* Try to set the inport profile. We read the input profile like this: + * + * embedded filename action + * 0 0 image + * 1 0 image + * 0 1 file + * 1 1 image, then fall back to file + * + * If averything fails, we fall back to our built-in profiles, either + * srgb or cmyk, depending on the input image. + * + * We set attach_input_profile if we used a non-emdedded profile. The profile + * in in_blob will need to be attached to the output image in some way. + */ static int -vips_icc_import_build( VipsObject *object ) +vips_icc_set_import( VipsIcc *icc, + gboolean embedded, const char *input_profile_filename ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsColour *colour = (VipsColour *) object; - VipsColourCode *code = (VipsColourCode *) object; - VipsIcc *icc = (VipsIcc *) object; - VipsIccImport *import = (VipsIccImport *) object; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( icc ); + VipsColourCode *code = (VipsColourCode *) icc; - gboolean used_fallback; + icc->non_standard_input_profile = FALSE; - /* We read the input profile like this: - * - * embedded filename action - * 0 0 image - * 1 0 image - * 0 1 file - * 1 1 image, then fall back to file + /* Try embedded profile. */ - - used_fallback = FALSE; - if( code->in && - (import->embedded || - !import->input_profile_filename) ) { + (embedded || !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, icc->intent, LCMS_USED_AS_INPUT ); + icc->in_profile = vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT ); } + /* Try profile from filename. + */ if( code->in && !icc->in_blob && - import->input_profile_filename ) { - if( vips_profile_load( import->input_profile_filename, - &icc->in_blob, NULL ) ) - return( -1 ); - icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, - code->in, icc->intent, LCMS_USED_AS_INPUT ); - used_fallback = TRUE; + input_profile_filename ) { + if( + !vips_profile_load( input_profile_filename, + &icc->in_blob, NULL ) && + (icc->in_profile = vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT )) ) + icc->non_standard_input_profile = TRUE; + } + + /* Try built-in profile. + */ + if( code->in && + !icc->in_profile ) { + const char *name = code->in->Type == VIPS_INTERPRETATION_CMYK ? + "cmyk" : "srgb"; + + if( + !vips_profile_load( name, &icc->in_blob, NULL ) && + (icc->in_profile = vips_icc_verify_blob( &icc->in_blob, + code->in, icc->intent, LCMS_USED_AS_INPUT )) ) + icc->non_standard_input_profile = TRUE; } if( !icc->in_profile ) { @@ -781,6 +746,71 @@ vips_icc_import_build( VipsObject *object ) return( -1 ); } + return( 0 ); +} + +static void +vips_icc_class_init( VipsIccClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->dispose = vips_icc_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "icc"; + object_class->description = _( "transform using ICC profiles" ); + object_class->build = vips_icc_build; + + VIPS_ARG_ENUM( class, "intent", 6, + _( "Intent" ), + _( "Rendering intent" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsIcc, intent ), + VIPS_TYPE_INTENT, VIPS_INTENT_RELATIVE ); + + VIPS_ARG_ENUM( class, "pcs", 6, + _( "PCS" ), + _( "Set Profile Connection Space" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsIcc, pcs ), + VIPS_TYPE_PCS, VIPS_PCS_LAB ); + + cmsSetLogErrorHandler( icc_error ); +} + +static void +vips_icc_init( VipsIcc *icc ) +{ + icc->intent = VIPS_INTENT_RELATIVE; + icc->pcs = VIPS_PCS_LAB; + icc->depth = 8; +} + +typedef struct _VipsIccImport { + VipsIcc parent_instance; + + gboolean embedded; + char *input_profile_filename; + +} VipsIccImport; + +typedef VipsIccClass VipsIccImportClass; + +G_DEFINE_TYPE( VipsIccImport, vips_icc_import, VIPS_TYPE_ICC ); + +static int +vips_icc_import_build( VipsObject *object ) +{ + VipsColour *colour = (VipsColour *) object; + VipsIcc *icc = (VipsIcc *) object; + VipsIccImport *import = (VipsIccImport *) object; + + if( vips_icc_set_import( icc, + import->embedded, import->input_profile_filename ) ) + return( -1 ); + if( icc->pcs == VIPS_PCS_LAB ) { cmsCIExyY white; cmsWhitePointFromTemp( &white, 6500 ); @@ -799,7 +829,7 @@ vips_icc_import_build( VipsObject *object ) * In the same way, we don't remove the embedded input profile on * import. */ - if( used_fallback && + if( icc->non_standard_input_profile && icc->in_blob ) { const void *data; size_t size; @@ -1139,50 +1169,12 @@ vips_icc_transform_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsColour *colour = (VipsColour *) object; - VipsColourCode *code = (VipsColourCode *) object; VipsIcc *icc = (VipsIcc *) object; VipsIccTransform *transform = (VipsIccTransform *) object; - /* We read the input profile like this: - * - * embedded filename action - * 0 0 image - * 1 0 image - * 0 1 file - * 1 1 image, then fall back to file - * - * see also import_build. - */ - - if( code->in && - (transform->embedded || - !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, icc->intent, LCMS_USED_AS_INPUT ); - } - - if( code->in && - !icc->in_blob && - transform->input_profile_filename ) { - if( vips_profile_load( transform->input_profile_filename, - &icc->in_blob, NULL ) ) - return( -1 ); - icc->in_profile = - vips_icc_verify_blob( &icc->in_blob, - code->in, icc->intent, LCMS_USED_AS_INPUT ); - } - - if( !icc->in_profile ) { - vips_error( class->nickname, "%s", _( "no input profile" ) ); + if( vips_icc_set_import( icc, + transform->embedded, transform->input_profile_filename ) ) return( -1 ); - } - - if( !icc->in_profile ) { - vips_error( class->nickname, "%s", _( "no input profile" ) ); - return( -1 ); - } if( transform->output_profile_filename ) { if( vips_profile_load( transform->output_profile_filename, diff --git a/libvips/colour/profile_load.c b/libvips/colour/profile_load.c index 3105ef1c..9bce2a6c 100644 --- a/libvips/colour/profile_load.c +++ b/libvips/colour/profile_load.c @@ -88,7 +88,8 @@ vips_profile_fallback_get( const char *name, size_t *length ) return( data ); } else { g_free( data ); - g_warning( "fallback profile decompression failed" ); + g_warning( "fallback profile " + "decompression failed" ); } } @@ -182,7 +183,7 @@ vips_profile_load_init( VipsProfileLoad *load ) * case. * * - @name can be the name of one of the ICC profiles embedded in libvips. - * These names can be at least `"cmyk"` and `"srgb"`. + * These names can be at least `"cmyk"`, `"p3"` and `"srgb"`. * * - @name can be the full path to a file. * From 4abcbc7a2ee68f32d5381079e341f63c66cf55ca Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Apr 2021 18:23:27 +0100 Subject: [PATCH 3/3] try to improve the error message --- libvips/colour/icc_transform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index 9d6e4cb8..f934b41c 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -611,6 +611,8 @@ vips_image_expected_sig( VipsImage *image ) /* Load a profile from a blob and check compatibility with image, intent and * direction. + * + * Don't set any errors since this is used to test compatibility. */ static cmsHPROFILE vips_icc_load_profile_blob( VipsBlob *blob, @@ -742,7 +744,8 @@ vips_icc_set_import( VipsIcc *icc, } if( !icc->in_profile ) { - vips_error( class->nickname, "%s", _( "no input profile" ) ); + vips_error( class->nickname, "%s", _( "unable to load or " + "find any compatible input profile" ) ); return( -1 ); }