Merge pull request #2133 from libvips/improve-detection-of-bad-profiles
Improve detection of bad profiles
This commit is contained in:
commit
5c9287f8d8
@ -74,6 +74,10 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define DEBUG
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
@ -83,7 +87,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/* Has to be before VIPS to avoid nameclashes.
|
/* Has to be before VIPS to avoid nameclashes.
|
||||||
*/
|
*/
|
||||||
@ -163,7 +166,7 @@ typedef struct _VipsIcc {
|
|||||||
cmsUInt32Number in_icc_format;
|
cmsUInt32Number in_icc_format;
|
||||||
cmsUInt32Number out_icc_format;
|
cmsUInt32Number out_icc_format;
|
||||||
cmsHTRANSFORM trans;
|
cmsHTRANSFORM trans;
|
||||||
|
gboolean non_standard_input_profile;
|
||||||
} VipsIcc;
|
} VipsIcc;
|
||||||
|
|
||||||
typedef VipsColourCodeClass VipsIccClass;
|
typedef VipsColourCodeClass VipsIccClass;
|
||||||
@ -376,105 +379,85 @@ vips_icc_build( VipsObject *object )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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
|
static void
|
||||||
vips_icc_class_init( VipsIccClass *class )
|
vips_icc_print_profile( const char *name, cmsHPROFILE profile )
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
static const cmsInfoType info_types[] = {
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
cmsInfoDescription,
|
||||||
|
cmsInfoManufacturer,
|
||||||
|
cmsInfoModel,
|
||||||
|
cmsInfoCopyright
|
||||||
|
};
|
||||||
|
static const char *info_names[] = {
|
||||||
|
"description",
|
||||||
|
"manufacturer",
|
||||||
|
"model",
|
||||||
|
"copyright"
|
||||||
|
};
|
||||||
|
|
||||||
gobject_class->dispose = vips_icc_dispose;
|
int i;
|
||||||
gobject_class->set_property = vips_object_set_property;
|
cmsUInt32Number n_bytes;
|
||||||
gobject_class->get_property = vips_object_get_property;
|
cmsUInt32Number n_intents;
|
||||||
|
cmsUInt32Number *intent_codes;
|
||||||
|
char **intent_descriptions;
|
||||||
|
|
||||||
object_class->nickname = "icc";
|
printf( "icc profile %s: %p\n", name, profile );
|
||||||
object_class->description = _( "transform using ICC profiles" );
|
for( i = 0; i < VIPS_NUMBER( info_types ); i++ ) {
|
||||||
object_class->build = vips_icc_build;
|
if( (n_bytes = cmsGetProfileInfoASCII( profile,
|
||||||
|
info_types[i], "en", "US",
|
||||||
|
NULL, 0 )) ) {
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
VIPS_ARG_ENUM( class, "intent", 6,
|
buffer = VIPS_ARRAY( NULL, n_bytes, char );
|
||||||
_( "Intent" ),
|
(void) cmsGetProfileInfoASCII( profile,
|
||||||
_( "Rendering intent" ),
|
info_types[i], "en", "US",
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
buffer, n_bytes );
|
||||||
G_STRUCT_OFFSET( VipsIcc, intent ),
|
printf( "%s: %s\n", info_names[i], buffer );
|
||||||
VIPS_TYPE_INTENT, VIPS_INTENT_RELATIVE );
|
g_free( buffer );
|
||||||
|
}
|
||||||
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 );
|
|
||||||
|
|
||||||
VIPS_ARG_BOOL( class, "black_point_compensation", 7,
|
|
||||||
_( "Black point compensation" ),
|
|
||||||
_( "Enable black point compensation" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsIcc, black_point_compensation ),
|
|
||||||
FALSE );
|
|
||||||
|
|
||||||
cmsSetLogErrorHandler( icc_error );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
printf( "profile class: %#x\n", cmsGetDeviceClass( profile ) );
|
||||||
vips_icc_init( VipsIcc *icc )
|
printf( "PCS: %#x\n", cmsGetPCS( profile ) );
|
||||||
{
|
|
||||||
icc->intent = VIPS_INTENT_RELATIVE;
|
printf( "matrix shaper: %d\n", cmsIsMatrixShaper( profile ) );
|
||||||
icc->pcs = VIPS_PCS_LAB;
|
printf( "version: %g\n", cmsGetProfileVersion( profile ) );
|
||||||
icc->depth = 8;
|
|
||||||
|
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 );
|
||||||
typedef struct _VipsIccImport {
|
g_free( intent_descriptions );
|
||||||
VipsIcc parent_instance;
|
|
||||||
|
|
||||||
gboolean embedded;
|
|
||||||
char *input_profile_filename;
|
|
||||||
|
|
||||||
} VipsIccImport;
|
|
||||||
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
/* How many bands we expect to see from an image after preprocessing by our
|
/* How many bands we expect to see from an image after preprocessing by our
|
||||||
* parent classes. This is a bit fragile :-(
|
* parent classes. This is a bit fragile :-(
|
||||||
@ -527,6 +510,34 @@ vips_image_expected_bands( VipsImage *image )
|
|||||||
return( expected_bands );
|
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
|
/* What cmsColorSpaceSignature do we expect this image to be (roughly) after
|
||||||
* preprocessing. Again, fragile :( see the FIXME above.
|
* preprocessing. Again, fragile :( see the FIXME above.
|
||||||
*/
|
*/
|
||||||
@ -608,26 +619,14 @@ vips_image_expected_sig( VipsImage *image )
|
|||||||
return( expected_sig );
|
return( expected_sig );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get from an image.
|
/* Load a profile from a blob and check compatibility with image, intent and
|
||||||
*/
|
* direction.
|
||||||
static VipsBlob *
|
*
|
||||||
vips_icc_get_profile_image( VipsImage *image )
|
* Don't set any errors since this is used to test compatibility.
|
||||||
{
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load a profile from a blob and check compatibility.
|
|
||||||
*/
|
*/
|
||||||
static cmsHPROFILE
|
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;
|
const void *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -639,6 +638,10 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image )
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_icc_print_profile( "from blob", profile );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
if( image &&
|
if( image &&
|
||||||
vips_image_expected_bands( image ) !=
|
vips_image_expected_bands( image ) !=
|
||||||
vips_icc_profile_needs_bands( profile ) ) {
|
vips_icc_profile_needs_bands( profile ) ) {
|
||||||
@ -646,6 +649,7 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image )
|
|||||||
g_warning( "%s", _( "profile incompatible with image" ) );
|
g_warning( "%s", _( "profile incompatible with image" ) );
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( image &&
|
if( image &&
|
||||||
vips_image_expected_sig( image ) !=
|
vips_image_expected_sig( image ) !=
|
||||||
cmsGetColorSpace( profile ) ) {
|
cmsGetColorSpace( profile ) ) {
|
||||||
@ -655,6 +659,15 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image )
|
|||||||
return( NULL );
|
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 );
|
return( profile );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,12 +676,14 @@ vips_icc_load_profile_blob( VipsBlob *blob, VipsImage *image )
|
|||||||
* unref the blob if it's useless.
|
* unref the blob if it's useless.
|
||||||
*/
|
*/
|
||||||
static cmsHPROFILE
|
static cmsHPROFILE
|
||||||
vips_icc_verify_blob( VipsBlob **blob, VipsImage *image )
|
vips_icc_verify_blob( VipsBlob **blob,
|
||||||
|
VipsImage *image, VipsIntent intent, int direction )
|
||||||
{
|
{
|
||||||
if( *blob ) {
|
if( *blob ) {
|
||||||
cmsHPROFILE profile;
|
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 );
|
vips_area_unref( (VipsArea *) *blob );
|
||||||
*blob = NULL;
|
*blob = NULL;
|
||||||
}
|
}
|
||||||
@ -679,54 +694,142 @@ vips_icc_verify_blob( VipsBlob **blob, VipsImage *image )
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
/* Try to set the inport profile. We read the input profile like this:
|
||||||
vips_icc_import_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
|
||||||
VipsColour *colour = (VipsColour *) object;
|
|
||||||
VipsColourCode *code = (VipsColourCode *) object;
|
|
||||||
VipsIcc *icc = (VipsIcc *) object;
|
|
||||||
VipsIccImport *import = (VipsIccImport *) object;
|
|
||||||
|
|
||||||
gboolean used_fallback;
|
|
||||||
|
|
||||||
/* We read the input profile like this:
|
|
||||||
*
|
*
|
||||||
* embedded filename action
|
* embedded filename action
|
||||||
* 0 0 image
|
* 0 0 image
|
||||||
* 1 0 image
|
* 1 0 image
|
||||||
* 0 1 file
|
* 0 1 file
|
||||||
* 1 1 image, then fall back to 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_set_import( VipsIcc *icc,
|
||||||
|
gboolean embedded, const char *input_profile_filename )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( icc );
|
||||||
|
VipsColourCode *code = (VipsColourCode *) icc;
|
||||||
|
|
||||||
used_fallback = FALSE;
|
icc->non_standard_input_profile = FALSE;
|
||||||
|
|
||||||
|
/* Try embedded profile.
|
||||||
|
*/
|
||||||
if( code->in &&
|
if( code->in &&
|
||||||
(import->embedded ||
|
(embedded || !input_profile_filename) ) {
|
||||||
!import->input_profile_filename) ) {
|
|
||||||
icc->in_blob = vips_icc_get_profile_image( code->in );
|
icc->in_blob = vips_icc_get_profile_image( code->in );
|
||||||
icc->in_profile =
|
icc->in_profile = vips_icc_verify_blob( &icc->in_blob,
|
||||||
vips_icc_verify_blob( &icc->in_blob, code->in );
|
code->in, icc->intent, LCMS_USED_AS_INPUT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try profile from filename.
|
||||||
|
*/
|
||||||
if( code->in &&
|
if( code->in &&
|
||||||
!icc->in_blob &&
|
!icc->in_blob &&
|
||||||
import->input_profile_filename ) {
|
input_profile_filename ) {
|
||||||
if( vips_profile_load( import->input_profile_filename,
|
if(
|
||||||
&icc->in_blob, NULL ) )
|
!vips_profile_load( input_profile_filename,
|
||||||
return( -1 );
|
&icc->in_blob, NULL ) &&
|
||||||
icc->in_profile =
|
(icc->in_profile = vips_icc_verify_blob( &icc->in_blob,
|
||||||
vips_icc_verify_blob( &icc->in_blob, code->in );
|
code->in, icc->intent, LCMS_USED_AS_INPUT )) )
|
||||||
used_fallback = TRUE;
|
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 ) {
|
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 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
vips_check_intent( class->nickname,
|
return( 0 );
|
||||||
icc->in_profile, icc->intent, LCMS_USED_AS_INPUT );
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOOL( class, "black_point_compensation", 7,
|
||||||
|
_( "Black point compensation" ),
|
||||||
|
_( "Enable black point compensation" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsIcc, black_point_compensation ),
|
||||||
|
FALSE );
|
||||||
|
|
||||||
|
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 ) {
|
if( icc->pcs == VIPS_PCS_LAB ) {
|
||||||
cmsCIExyY white;
|
cmsCIExyY white;
|
||||||
@ -746,7 +849,7 @@ vips_icc_import_build( VipsObject *object )
|
|||||||
* In the same way, we don't remove the embedded input profile on
|
* In the same way, we don't remove the embedded input profile on
|
||||||
* import.
|
* import.
|
||||||
*/
|
*/
|
||||||
if( used_fallback &&
|
if( icc->non_standard_input_profile &&
|
||||||
icc->in_blob ) {
|
icc->in_blob ) {
|
||||||
const void *data;
|
const void *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -913,16 +1016,12 @@ vips_icc_export_build( VipsObject *object )
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( icc->out_blob &&
|
if( icc->out_blob &&
|
||||||
!(icc->out_profile =
|
!(icc->out_profile = vips_icc_load_profile_blob( icc->out_blob,
|
||||||
vips_icc_load_profile_blob( icc->out_blob, NULL )) ) {
|
NULL, icc->intent, LCMS_USED_AS_OUTPUT )) ) {
|
||||||
vips_error( class->nickname, "%s", _( "no output profile" ) );
|
vips_error( class->nickname, "%s", _( "no output profile" ) );
|
||||||
return( -1 );
|
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 ) )
|
if( VIPS_OBJECT_CLASS( vips_icc_export_parent_class )->build( object ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
@ -1090,48 +1189,12 @@ vips_icc_transform_build( VipsObject *object )
|
|||||||
{
|
{
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||||
VipsColour *colour = (VipsColour *) object;
|
VipsColour *colour = (VipsColour *) object;
|
||||||
VipsColourCode *code = (VipsColourCode *) object;
|
|
||||||
VipsIcc *icc = (VipsIcc *) object;
|
VipsIcc *icc = (VipsIcc *) object;
|
||||||
VipsIccTransform *transform = (VipsIccTransform *) object;
|
VipsIccTransform *transform = (VipsIccTransform *) object;
|
||||||
|
|
||||||
/* We read the input profile like this:
|
if( vips_icc_set_import( icc,
|
||||||
*
|
transform->embedded, transform->input_profile_filename ) )
|
||||||
* 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( code->in &&
|
|
||||||
!icc->in_blob &&
|
|
||||||
transform->input_profile_filename ) {
|
|
||||||
if( vips_profile_load( transform->input_profile_filename,
|
|
||||||
&icc->in_blob, NULL ) )
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
icc->in_profile =
|
|
||||||
vips_icc_verify_blob( &icc->in_blob, code->in );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !icc->in_profile ) {
|
|
||||||
vips_error( class->nickname, "%s", _( "no input profile" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !icc->in_profile ) {
|
|
||||||
vips_error( class->nickname, "%s", _( "no input profile" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( transform->output_profile_filename ) {
|
if( transform->output_profile_filename ) {
|
||||||
if( vips_profile_load( transform->output_profile_filename,
|
if( vips_profile_load( transform->output_profile_filename,
|
||||||
@ -1141,19 +1204,14 @@ vips_icc_transform_build( VipsObject *object )
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( icc->out_blob )
|
if( icc->out_blob )
|
||||||
icc->out_profile =
|
icc->out_profile = vips_icc_load_profile_blob( icc->out_blob,
|
||||||
vips_icc_load_profile_blob( icc->out_blob, NULL );
|
NULL, icc->intent, LCMS_USED_AS_OUTPUT );
|
||||||
|
|
||||||
if( !icc->out_profile ) {
|
if( !icc->out_profile ) {
|
||||||
vips_error( class->nickname, "%s", _( "no output profile" ) );
|
vips_error( class->nickname, "%s", _( "no output profile" ) );
|
||||||
return( -1 );
|
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 )->
|
if( VIPS_OBJECT_CLASS( vips_icc_transform_parent_class )->
|
||||||
build( object ) )
|
build( object ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
@ -1251,6 +1309,10 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
|
|||||||
if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) )
|
if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_icc_print_profile( profile_filename, profile );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
if( !(media = cmsReadTag( profile, cmsSigMediaWhitePointTag )) ) {
|
if( !(media = cmsReadTag( profile, cmsSigMediaWhitePointTag )) ) {
|
||||||
vips_error( "vips_icc_ac2rc",
|
vips_error( "vips_icc_ac2rc",
|
||||||
"%s", _( "unable to get media white point" ) );
|
"%s", _( "unable to get media white point" ) );
|
||||||
@ -1310,6 +1372,10 @@ vips_icc_is_compatible_profile( VipsImage *image,
|
|||||||
*/
|
*/
|
||||||
return( FALSE );
|
return( FALSE );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_icc_print_profile( "from memory", profile );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
if( vips_image_expected_bands( image ) !=
|
if( vips_image_expected_bands( image ) !=
|
||||||
vips_icc_profile_needs_bands( profile ) ) {
|
vips_icc_profile_needs_bands( profile ) ) {
|
||||||
VIPS_FREEF( cmsCloseProfile, profile );
|
VIPS_FREEF( cmsCloseProfile, profile );
|
||||||
|
@ -88,7 +88,8 @@ vips_profile_fallback_get( const char *name, size_t *length )
|
|||||||
return( data );
|
return( data );
|
||||||
} else {
|
} else {
|
||||||
g_free( data );
|
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.
|
* case.
|
||||||
*
|
*
|
||||||
* - @name can be the name of one of the ICC profiles embedded in libvips.
|
* - @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.
|
* - @name can be the full path to a file.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user