first hack at supporting XYZ PCS

still need to fix new encode / decode
This commit is contained in:
John Cupitt 2013-11-12 14:58:59 +00:00
parent 98d708a2d9
commit 0257dda270
6 changed files with 182 additions and 29 deletions

View File

@ -486,7 +486,7 @@ vips_colour_code_build( VipsObject *object )
VipsImage *in;
VipsImage *extra;
t = (VipsImage **) vips_object_local_array( object, 4 );
t = (VipsImage **) vips_object_local_array( object, 5 );
in = code->in;
extra = NULL;
@ -538,6 +538,15 @@ vips_colour_code_build( VipsObject *object )
in = t[3];
}
if( in &&
code->input_coding == VIPS_CODING_NONE &&
code->input_interpretation != VIPS_INTERPRETATION_ERROR ) {
if( vips_colourspace( in, &t[4],
code->input_interpretation, NULL ) )
return( -1 );
in = t[4];
}
colour->n = 1;
colour->in = (VipsImage **) vips_object_local_array( object, 2 );
colour->in[0] = in;
@ -588,6 +597,7 @@ vips_colour_code_class_init( VipsColourCodeClass *class )
static void
vips_colour_code_init( VipsColourCode *code )
{
code->input_interpretation = VIPS_INTERPRETATION_ERROR;
}
G_DEFINE_ABSTRACT_TYPE( VipsColourDifference, vips_colour_difference,

View File

@ -76,6 +76,7 @@
#define cmsSigRgbData icSigRgbData
#define cmsSigLabData icSigLabData
#define cmsSigCmykData icSigCmykData
#define cmsSigXYZData icSigXYZData
#endif
#include <vips/vips.h>
@ -96,6 +97,28 @@ typedef DWORD cmsUInt32Number;
#define cmsFLAGS_NOCACHE (0)
#endif
/**
* VipsIntent:
* @VIPS_INTENT_PERCEPTUAL: perceptual rendering intent
* @VIPS_INTENT_RELATIVE: relative colorimetric rendering intent
* @VIPS_INTENT_SATURATION: saturation rendering intent
* @VIPS_INTENT_ABSOLUTE: absolute colorimetric rendering intent
*
* The rendering intent. #VIPS_INTENT_ABSOLUTE is best for
* scientific work, #VIPS_INTENT_RELATIVE is usually best for
* accurate communication with other imaging libraries.
*/
/**
* VipsPCS:
* @VIPS_PCS_LAB: use CIELAB D65 as the Profile Connection Space
* @VIPS_PCS_XYZ: use XYZ as the Profile Connection Space
*
* Pick a Profile Connection Space for vips_icc_import() and
* vips_icc_export(). LAB is usually best, XYZ can be more convenient in some
* cases.
*/
/**
* vips_icc_present:
*
@ -129,6 +152,7 @@ typedef struct _VipsIcc {
VipsColourCode parent_instance;
VipsIntent intent;
VipsPCS pcs;
int depth;
cmsHPROFILE in_profile;
@ -182,6 +206,15 @@ vips_icc_dispose( GObject *gobject )
G_OBJECT_CLASS( vips_icc_parent_class )->dispose( gobject );
}
/* Is a profile just a pcs stub.
*/
static gboolean
is_pcs( cmsHPROFILE profile )
{
return( cmsGetColorSpace( profile ) == cmsSigLabData ||
cmsGetColorSpace( profile ) == cmsSigXYZData );
}
static int
vips_icc_build( VipsObject *object )
{
@ -223,9 +256,19 @@ vips_icc_build( VipsObject *object )
case cmsSigLabData:
code->input_bands = 3;
code->input_format = VIPS_FORMAT_FLOAT;
code->input_interpretation =
VIPS_INTERPRETATION_LAB;
icc->in_icc_format = TYPE_Lab_16;
break;
case cmsSigXYZData:
code->input_bands = 3;
code->input_format = VIPS_FORMAT_FLOAT;
code->input_interpretation =
VIPS_INTERPRETATION_XYZ;
icc->in_icc_format = TYPE_XYZ_16;
break;
default:
vips_error( class->nickname,
_( "unimplemented input color space 0x%x" ),
@ -268,6 +311,13 @@ vips_icc_build( VipsObject *object )
icc->out_icc_format = TYPE_Lab_16;
break;
case cmsSigXYZData:
colour->interpretation = VIPS_INTERPRETATION_XYZ;
colour->format = VIPS_FORMAT_FLOAT;
colour->bands = 3;
icc->out_icc_format = TYPE_XYZ_16;
break;
default:
vips_error( class->nickname,
_( "unimplemented output color space 0x%x" ),
@ -279,8 +329,8 @@ vips_icc_build( VipsObject *object )
*/
if( icc->in_profile &&
icc->out_profile &&
cmsGetColorSpace( icc->in_profile ) == cmsSigLabData &&
cmsGetColorSpace( icc->out_profile ) == cmsSigLabData ) {
is_pcs( icc->in_profile ) &&
is_pcs( icc->out_profile ) ) {
vips_error( class->nickname,
"%s", _( "no device profile" ) );
return( -1 );
@ -323,6 +373,13 @@ vips_icc_class_init( VipsIccClass *class )
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 );
#ifdef HAVE_LCMS2
cmsSetLogErrorHandler( icc_error );
#else
@ -339,6 +396,7 @@ vips_icc_init( VipsIcc *icc )
{
icc->lock = vips_g_mutex_new();
icc->intent = VIPS_INTENT_RELATIVE;
icc->pcs = VIPS_PCS_LAB;
icc->depth = 8;
}
@ -420,16 +478,18 @@ vips_icc_import_build( VipsObject *object )
vips_check_intent( class->nickname,
icc->in_profile, icc->intent, LCMS_USED_AS_INPUT );
if( icc->pcs == VIPS_PCS_LAB ) {
#ifdef HAVE_LCMS2
{
cmsCIExyY white;
cmsWhitePointFromTemp( &white, 6500 );
cmsCIExyY white;
cmsWhitePointFromTemp( &white, 6500 );
icc->out_profile = cmsCreateLab4Profile( &white );
}
icc->out_profile = cmsCreateLab4Profile( &white );
#else
icc->out_profile = cmsCreateLabProfile( NULL );
icc->out_profile = cmsCreateLabProfile( NULL );
#endif
}
else
icc->out_profile = cmsCreateXYZProfile();
if( VIPS_OBJECT_CLASS( vips_icc_import_parent_class )->build( object ) )
return( -1 );
@ -452,6 +512,21 @@ decode_lab( guint16 *fixed, float *lab, int n )
}
}
static void
decode_xyz( guint16 *fixed, float *xyz, int n )
{
int i;
for( i = 0; i < n; i++ ) {
xyz[0] = (double) fixed[0] / 652.800;
xyz[1] = (double) fixed[1] / 652.800;
xyz[2] = (double) fixed[2] / 652.800;
xyz += 3;
fixed += 3;
}
}
/* Process a buffer of data.
*/
static void
@ -481,7 +556,10 @@ vips_icc_import_line( VipsColour *colour,
g_mutex_unlock( icc->lock );
#endif
decode_lab( encoded, q, chunk );
if( icc->pcs == VIPS_PCS_LAB )
decode_lab( encoded, q, chunk );
else
decode_xyz( encoded, q, chunk );
p += PIXEL_BUFFER_SIZE * VIPS_IMAGE_SIZEOF_PEL( colour->in[0] );
q += PIXEL_BUFFER_SIZE * 3;
@ -517,6 +595,7 @@ vips_icc_import_class_init( VipsIccImportClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsIccImport, input_profile_filename ),
NULL );
}
static void
@ -544,16 +623,18 @@ vips_icc_export_build( VipsObject *object )
VipsIcc *icc = (VipsIcc *) object;
VipsIccExport *export = (VipsIccExport *) object;
if( icc->pcs == VIPS_PCS_LAB ) {
#ifdef HAVE_LCMS2
{
cmsCIExyY white;
cmsWhitePointFromTemp( &white, 6500 );
cmsCIExyY white;
cmsWhitePointFromTemp( &white, 6500 );
icc->in_profile = cmsCreateLab4Profile( &white );
}
icc->in_profile = cmsCreateLab4Profile( &white );
#else
icc->in_profile = cmsCreateLabProfile( NULL );
icc->in_profile = cmsCreateLabProfile( NULL );
#endif
}
else
icc->in_profile = cmsCreateXYZProfile();
if( code->in &&
!export->output_profile_filename &&
@ -631,6 +712,44 @@ encode_lab( float *lab, guint16 *fixed, int n )
}
}
#define MAX_ENCODEABLE_XYZ (100 * (1.0 + 32767.0 / 32768.0))
// 1.15 fixed point for XYZ
static void
encode_xyz( float *xyz, guint16 *fixed, int n )
{
int i;
for( i = 0; i < n; i++ ) {
float X = xyz[0];
float Y = xyz[1];
float Z = xyz[2];
if( X < 0 )
X = 0;
if( X > MAX_ENCODEABLE_XYZ )
X = MAX_ENCODEABLE_XYZ;
if( Y < 0 )
Y = 0;
if( Y > MAX_ENCODEABLE_XYZ )
Y = MAX_ENCODEABLE_XYZ;
if( Z < 0 )
Z = 0;
if( Z > MAX_ENCODEABLE_XYZ )
Z = MAX_ENCODEABLE_XYZ;
fixed[0] = X * 652.800 + 0.5;
fixed[1] = Y * 652.800 + 0.5;
fixed[2] = Z * 652.800 + 0.5;
xyz += 3;
fixed += 3;
}
}
/* Process a buffer of data.
*/
static void
@ -652,7 +771,10 @@ vips_icc_export_line( VipsColour *colour,
for( x = 0; x < width; x += PIXEL_BUFFER_SIZE ) {
const int chunk = VIPS_MIN( width - x, PIXEL_BUFFER_SIZE );
encode_lab( p, encoded, chunk );
if( icc->pcs == VIPS_PCS_LAB )
encode_lab( p, encoded, chunk );
else
encode_xyz( p, encoded, chunk );
#ifdef HAVE_LCMS2
cmsDoTransform( icc->trans, encoded, q, chunk );
@ -982,8 +1104,10 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
* @input_profile: get the input profile from here
* @intent: transform with this intent
* @embedded: use profile embedded in input image
* @pcs: use XYZ or LAB PCS
*
* Import an image from device space to D65 LAB with an ICC profile.
* Import an image from device space to D65 LAB with an ICC profile. If @pcs is
* set to #VIPS_PCS_XYZ, use CIE XYZ PCS instead.
*
* If @embedded is set, the input profile is taken from the input image
* metadata. If there is no embedded profile,
@ -1018,8 +1142,11 @@ vips_icc_import( VipsImage *in, VipsImage **out, ... )
* @intent: transform with this intent
* @depth: depth of output image in bits
* @output_profile: get the output profile from here
* @pcs: use XYZ or LAB PCS
*
* Export an image from D65 LAB to device space with an ICC profile.
* If @pcs is
* set to #VIPS_PCS_XYZ, use CIE XYZ PCS instead.
* If @output_profile is not set, use the embedded profile, if any.
* If @output_profile is set, export with that and attach it to the output
* image.

View File

@ -150,6 +150,7 @@ typedef struct _VipsColourCode {
VipsCoding input_coding;
VipsBandFormat input_format;
int input_bands;
VipsInterpretation input_interpretation;
} VipsColourCode;

View File

@ -93,17 +93,6 @@ extern "C" {
#define VIPS_D3250_Y0 (100.0)
#define VIPS_D3250_Z0 (45.8501)
/**
* VipsIntent:
* @VIPS_INTENT_PERCEPTUAL: perceptual rendering intent
* @VIPS_INTENT_RELATIVE: relative colorimetric rendering intent
* @VIPS_INTENT_SATURATION: saturation rendering intent
* @VIPS_INTENT_ABSOLUTE: absolute colorimetric rendering intent
*
* The rendering intent. #VIPS_INTENT_ABSOLUTE is best for
* scientific work, #VIPS_INTENT_RELATIVE is usually best for
* accurate communication with other imaging libraries.
*/
typedef enum {
VIPS_INTENT_PERCEPTUAL = 0,
VIPS_INTENT_RELATIVE,
@ -111,6 +100,12 @@ typedef enum {
VIPS_INTENT_ABSOLUTE
} VipsIntent;
typedef enum {
VIPS_PCS_LAB,
VIPS_PCS_XYZ,
VIPS_PCS_LAST
} VipsPCS;
gboolean vips_colourspace_issupported( const VipsImage *image );
int vips_colourspace( VipsImage *in, VipsImage **out,
VipsInterpretation space, ... )

View File

@ -68,6 +68,8 @@ GType vips_access_get_type (void) G_GNUC_CONST;
/* enumerations from "../../../libvips/include/vips/colour.h" */
GType vips_intent_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_INTENT (vips_intent_get_type())
GType vips_pcs_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_PCS (vips_pcs_get_type())
/* enumerations from "../../../libvips/include/vips/operation.h" */
GType vips_operation_flags_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_OPERATION_FLAGS (vips_operation_flags_get_type())

View File

@ -594,6 +594,24 @@ vips_intent_get_type( void )
return( etype );
}
GType
vips_pcs_get_type( void )
{
static GType etype = 0;
if( etype == 0 ) {
static const GEnumValue values[] = {
{VIPS_PCS_LAB, "VIPS_PCS_LAB", "lab"},
{VIPS_PCS_XYZ, "VIPS_PCS_XYZ", "xyz"},
{VIPS_PCS_LAST, "VIPS_PCS_LAST", "last"},
{0, NULL, NULL}
};
etype = g_enum_register_static( "VipsPCS", values );
}
return( etype );
}
/* enumerations from "../../libvips/include/vips/operation.h" */
GType
vips_operation_flags_get_type( void )