add lcms2 support

This commit is contained in:
John Cupitt 2010-08-02 15:58:30 +00:00
parent 669dda83d2
commit a3f8ddadfc
4 changed files with 880 additions and 71 deletions

View File

@ -8,6 +8,7 @@
- im_divide spots /0
- remove liboil dependency, we will use Orc instead
- various small cleanups (thanks Tim)
- add lcms2 support
12/5/10 started 7.22.2
- the conditional image of ifthenelse can be any format, a (!=0) is added if

7
TODO
View File

@ -1,3 +1,8 @@
- use D65 in cmsCreateLab4Profile() ? not sure
- VImage("poop.v") should use "rd" as well? Python too?
- im_divide() can /0 for complex
- unlink temps earlier on *nix systems
@ -21,8 +26,6 @@
- lcms2 needs testing
- tools subdirs are now pretty stupid :-( just have a single dir
- test

View File

@ -375,17 +375,23 @@ if test x"$with_magick" != "xno"; then
LIBS=$save_LIBS
fi
# lcms
# lcms ... look for lcms2 first, it has better threading support
AC_ARG_WITH([lcms],
AS_HELP_STRING([--without-lcms], [build without lcms (default: test)]))
if test x"$with_lcms" != "xno"; then
PKG_CHECK_MODULES(LCMS, lcms,
[AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.])
PKG_CHECK_MODULES(LCMS, lcms2,
[AC_DEFINE(HAVE_LCMS2,1,[define if you have lcms2 installed.])
with_lcms=yes
PACKAGES_USED="$PACKAGES_USED lcms"],
[AC_MSG_WARN([lcms not found; disabling lcms support])
with_lcms=no
with_lcms_ver=lcms2
PACKAGES_USED="$PACKAGES_USED lcms2"],
[PKG_CHECK_MODULES(LCMS, lcms,
[AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.])
with_lcms=yes
with_lcms_ver=lcms
PACKAGES_USED="$PACKAGES_USED lcms"],
[AC_MSG_WARN([lcms2/lcms not found; disabling lcms support])
with_lcms=no])
])
fi
@ -640,7 +646,7 @@ build docs with gtkdoc $enable_gtk_doc
use fftw3 for FFT: $with_fftw3
Magick package: $with_magickpackage
file import with libMagick: $with_magick
ICC profile support with lcms: $with_lcms
ICC profile support with lcms: $with_lcms (version $with_lcms_ver)
file import with OpenEXR: $with_OpenEXR
file import with matio: $with_matio
text rendering with pangoft2: $with_pangoft2

View File

@ -15,6 +15,8 @@
* - small cleanups
* - call attach_profile() before im_wrapone() so the profile will get
* written if we are wrinting to a file
* 2/8/10
* - add lcms2
*/
/*
@ -48,68 +50,7 @@
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#ifndef HAVE_LCMS
#include <vips/vips.h>
#include <vips/thread.h>
int
im_icc_present( void )
{
return( 0 );
}
int
im_icc_transform( IMAGE *in, IMAGE *out,
const char *input_profile_filename,
const char *output_profile_filename,
VipsIntent intent )
{
im_error( "im_icc_transform", "%s",
_( "lcms library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_import( IMAGE *in, IMAGE *out,
const char *input_profile_filename, VipsIntent intent )
{
im_error( "im_icc_import", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_import_embedded( IMAGE *in, IMAGE *out, VipsIntent intent )
{
im_error( "im_icc_import", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_export_depth( IMAGE *in, IMAGE *out, int depth,
const char *output_profile_filename, VipsIntent intent )
{
im_error( "im_icc_export_depth", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename )
{
im_error( "im_icc_ac2rc", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
#else
#if defined( HAVE_LCMS )
#include <stdio.h>
#include <math.h>
@ -896,4 +837,862 @@ im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename )
return( 0 );
}
#elif defined( HAVE_LCMS2 )
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <lcms2.h>
#include <vips/vips.h>
#include <vips/colour.h>
#include <vips/region.h>
#include <vips/util.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
/**
* VipsIntent:
* @IM_INTENT_PERCEPTUAL:
* @IM_INTENT_RELATIVE_COLORIMETRIC:
* @IM_INTENT_SATURATION:
* @IM_INTENT_ABSOLUTE_COLORIMETRIC:
*
* The rendering intent. #IM_INTENT_ABSOLUTE_COLORIMETRIC is best for
* scientific work, #IM_INTENT_RELATIVE_COLORIMETRIC is usually best for
* accurate communication with other imaging libraries.
*/
/* Call lcms with up to this many pixels at once.
*/
#define PIXEL_BUFFER_SIZE (10000)
static const char *
decode_intent( VipsIntent intent )
{
switch( intent ) {
case IM_INTENT_PERCEPTUAL: return( "PERCEPTUAL" );
case IM_INTENT_RELATIVE_COLORIMETRIC: return( "RELATIVE" );
case IM_INTENT_SATURATION: return( "SATURATION" );
case IM_INTENT_ABSOLUTE_COLORIMETRIC: return( "ABSOLUTE" );
default: return( "<unknown>" );
}
}
/**
* im_icc_present:
*
* VIPS can optionally be built without the ICC library. Use this function to
* test for its availability.
*
* Returns: non-zero if the ICC library is present.
*/
int
im_icc_present( void )
{
return( 1 );
}
/* Global state for a transform.
*/
typedef struct {
IMAGE *in;
IMAGE *out;
const char *input_profile_filename;
const char *output_profile_filename;
VipsIntent intent;
cmsHPROFILE in_profile;
cmsHPROFILE out_profile;
cmsHTRANSFORM trans;
/* We need to single-thread calls to LCMS.
*/
GMutex *lock;
} Icc;
/* Error from lcms.
*/
static void
icc_error( cmsContext context, cmsUInt32Number code, const char *text )
{
im_error( "im_icc", "%s", text );
}
static int
icc_destroy( Icc *icc )
{
IM_FREEF( cmsDeleteTransform, icc->trans );
IM_FREEF( cmsCloseProfile, icc->in_profile );
IM_FREEF( cmsCloseProfile, icc->out_profile );
IM_FREEF( g_mutex_free, icc->lock );
return( 0 );
}
static Icc *
icc_new( IMAGE *in, IMAGE *out, VipsIntent intent )
{
Icc *icc;
/* Ask lcms not to abort on error.
*/
cmsSetLogErrorHandler( icc_error );
if( !(icc = IM_NEW( out, Icc )) )
return( NULL );
icc->in = in;
icc->out = out;
icc->input_profile_filename = NULL;
icc->output_profile_filename = NULL;
icc->intent = intent;
icc->in_profile = 0;
icc->out_profile = 0;
icc->trans = 0;
icc->lock = g_mutex_new();
if( im_add_close_callback( out,
(im_callback_fn) icc_destroy, icc, NULL ) )
return( NULL );
return( icc );
}
static Icc *
icc_new_file( IMAGE *in, IMAGE *out,
const char *input_profile_filename,
const char *output_profile_filename,
VipsIntent intent )
{
Icc *icc;
cmsCIExyY white;
if( !(icc = icc_new( in, out, intent )) )
return( NULL );
if( input_profile_filename ) {
icc->input_profile_filename =
im_strdup( out, input_profile_filename );
if( !(icc->in_profile = cmsOpenProfileFromFile(
input_profile_filename, "r" )) )
im_error( "im_icc_transform",
_( "unable to open profile \"%s\"" ),
input_profile_filename );
}
if( output_profile_filename ) {
icc->output_profile_filename =
im_strdup( out, output_profile_filename );
if( !(icc->out_profile = cmsOpenProfileFromFile(
output_profile_filename, "r" )) )
im_error( "im_icc_transform",
_( "unable to open profile \"%s\"" ),
output_profile_filename );
}
cmsWhitePointFromTemp( &white, 6500 );
if( !output_profile_filename )
icc->out_profile = cmsCreateLab4Profile( &white );
if( !input_profile_filename )
icc->in_profile = cmsCreateLab4Profile( &white );
if( !icc->in_profile || !icc->out_profile ) {
im_error( "im_icc_transform",
"%s", _( "unable to create profiles" ) );
return( NULL );
}
return( icc );
}
static Icc *
icc_new_mem( IMAGE *in, IMAGE *out,
void *data, int data_length,
VipsIntent intent )
{
Icc *icc;
cmsCIExyY white;
if( !(icc = icc_new( in, out, intent )) )
return( NULL );
if( !(icc->in_profile = cmsOpenProfileFromMem( data, data_length )) ) {
im_error( "im_icc_transform",
"%s", _( "unable to read profile" ) );
return( NULL );
}
/* Use D65.
*/
cmsWhitePointFromTemp( &white, 6500 );
icc->out_profile = cmsCreateLab4Profile( &white );
return( icc );
}
/* Pack a buffer of floats into lcms's fixed-point formats. Cut from
* lcms-1.0.8.
*/
static void
encode_lab( float *lab, guint16 *fixed, int n )
{
int i;
for( i = 0; i < n; i++ ) {
float L = lab[0];
float a = lab[1];
float b = lab[2];
if( L < 0 )
L = 0;
if( L > 100. )
L = 100.;
if( a < -128. )
a = -128;
if( a > 127.9961 )
a = 127.9961;
if( b < -128. )
b = -128;
if( b > 127.9961 )
b = 127.9961;
fixed[0] = L * 652.800 + 0.5;
fixed[1] = (a + 128.0) * 256.0 + 0.5;
fixed[2] = (b + 128.0) * 256.0 + 0.5;
lab += 3;
fixed += 3;
}
}
static void
decode_lab( guint16 *fixed, float *lab, int n )
{
int i;
for( i = 0; i < n; i++ ) {
lab[0] = (double) fixed[0] / 652.800;
lab[1] = ((double) fixed[1] / 256.0) - 128.0;
lab[2] = ((double) fixed[2] / 256.0) - 128.0;
lab += 3;
fixed += 3;
}
}
static void
transform_buf( PEL *in, PEL *out, int n, Icc *icc )
{
g_mutex_lock( icc->lock );
cmsDoTransform( icc->trans, in, out, n );
g_mutex_unlock( icc->lock );
}
static int
attach_profile( IMAGE *im, const char *filename )
{
char *data;
unsigned int data_length;
if( !(data = im__file_read_name( filename, VIPS_ICC_DIR,
&data_length )) )
return( -1 );
if( im_meta_set_blob( im, IM_META_ICC_NAME,
(im_callback_fn) im_free, data, data_length ) ) {
im_free( data );
return( -1 );
}
return( 0 );
}
/**
* im_icc_transform:
* @in: input image
* @out: output image
* @input_profile_filename: get the input profile from here
* @output_profile_filename: get the output profile from here
* @intent: transform with this intent
*
* Transform an image with the ICC library. The input image is moved to
* profile-connection space with the input profile and then to the output
* space with the output profile.
*
* Use im_icc_import() and im_icc_export_depth() to do either the first or
* second half of this operation in isolation.
*
* See also: im_icc_import(), im_icc_export_depth().
*
* Returns: 0 on success, -1 on error.
*/
int
im_icc_transform( IMAGE *in, IMAGE *out,
const char *input_profile_filename,
const char *output_profile_filename,
VipsIntent intent )
{
Icc *icc;
cmsUInt32Number in_icc_format;
cmsUInt32Number out_icc_format;
if( im_check_uncoded( "im_icc_transform", in ) )
return( -1 );
if( !(icc = icc_new_file( in, out,
input_profile_filename, output_profile_filename, intent )) )
return( -1 );
if( !cmsIsIntentSupported( icc->in_profile,
intent, LCMS_USED_AS_INPUT ) )
im_warn( "im_icc_transform",
_( "intent %d (%s) not supported by "
"profile \"%s\"; falling back to default intent "
"(usually PERCEPTUAL)" ),
intent, decode_intent( intent ),
input_profile_filename );
if( !cmsIsIntentSupported( icc->out_profile,
intent, LCMS_USED_AS_OUTPUT ) )
im_warn( "im_icc_transform",
_( "intent %d (%s) not supported by "
"profile \"%s\"; falling back to default intent "
"(usually PERCEPTUAL)" ),
intent, decode_intent( intent ),
output_profile_filename );
switch( cmsGetColorSpace( icc->in_profile ) ) {
case cmsSigCmykData:
if( in->Bands != 4 ) {
im_error( "im_icc_transform",
"%s", _( "CMYK input profile "
"needs a 4 band input image" ) );
return( -1 );
}
in_icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 );
break;
case cmsSigRgbData:
if( in->Bands != 3 ) {
im_error( "im_icc_transform",
"%s", _( "RGB input profile "
"needs a 3 band input image" ) );
return( -1 );
}
in_icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 );
break;
default:
im_error( "im_icc_transform",
_( "unimplemented input color space 0x%x" ),
cmsGetColorSpace( icc->in_profile ) );
return( -1 );
}
/* Prepare the output image.
*/
if( im_cp_desc( out, in ) )
return( -1 );
switch( cmsGetColorSpace( icc->out_profile ) ) {
case cmsSigCmykData:
out->Type = IM_TYPE_CMYK;
out->BandFmt = IM_BANDFMT_UCHAR;
out->Bands = 4;
out_icc_format = TYPE_CMYK_8;
break;
case cmsSigRgbData:
out->Type = IM_TYPE_RGB;
out->BandFmt = IM_BANDFMT_UCHAR;
out->Bands = 3;
out_icc_format = TYPE_RGB_8;
break;
default:
im_error( "im_icc_transform",
_( "unimplemented output color space 0x%x" ),
cmsGetColorSpace( icc->out_profile ) );
return( -1 );
}
switch( in->BandFmt ) {
case IM_BANDFMT_UCHAR:
in_icc_format |= BYTES_SH( 1 );
break;
case IM_BANDFMT_USHORT:
in_icc_format |= BYTES_SH( 2 );
break;
default:
im_error( "im_icc_transform",
"%s", _( "uchar or ushort input only" ) );
return( -1 );
}
/* Use cmsFLAGS_NOCACHE to disable the 1-pixel cache and make
* calling cmsDoTrsnaform() from multiple threads safe.
*/
if( !(icc->trans = cmsCreateTransform( icc->in_profile, in_icc_format,
icc->out_profile, out_icc_format, intent,
cmsFLAGS_NOCACHE )) )
return( -1 );
if( attach_profile( out, output_profile_filename ) )
return( -1 );
/* Process!
*/
if( im_wrapone( in, out,
(im_wrapone_fn) transform_buf, icc, NULL ) )
return( -1 );
return( 0 );
}
static void
import_buf( PEL *in, float *out, int n, Icc *icc )
{
/* Buffer of encoded 16-bit pixels we write to.
*/
guint16 encoded[3 * PIXEL_BUFFER_SIZE];
while( n > 0 ) {
const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE );
cmsDoTransform( icc->trans, in, encoded, chunk );
decode_lab( encoded, out, chunk );
in += chunk * IM_IMAGE_SIZEOF_PEL( icc->in );
out += chunk * 3;
n -= chunk;
}
}
static int
icc_import( IMAGE *in, IMAGE *out, Icc *icc )
{
cmsUInt32Number icc_format;
if( im_check_uncoded( "im_icc_import", in ) )
return( -1 );
if( !cmsIsIntentSupported( icc->in_profile,
icc->intent, LCMS_USED_AS_INPUT ) )
im_warn( "im_icc_import",
_( "intent %d (%s) not supported by "
"profile; falling back to default intent "
"(usually PERCEPTUAL)" ),
icc->intent, decode_intent( icc->intent ) );
/* Prepare the output image.
*/
if( im_cp_desc( out, in ) )
return( -1 );
out->Type = IM_TYPE_LAB;
out->BandFmt = IM_BANDFMT_FLOAT;
out->Bands = 3;
switch( cmsGetColorSpace( icc->in_profile ) ) {
case cmsSigCmykData:
if( in->Bands != 4 ) {
im_error( "im_icc_import",
"%s", _( "CMYK profile needs a "
"4 band input image" ) );
return( -1 );
}
icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 );
break;
case cmsSigRgbData:
if( in->Bands != 3 ) {
im_error( "im_icc_import",
"%s", _( "RGB profile needs a "
"3 band input image" ) );
return( -1 );
}
icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 );
break;
default:
im_error( "im_icc_import", _( "unimplemented input color "
"space 0x%x" ), cmsGetColorSpace( icc->in_profile ) );
return( -1 );
}
switch( in->BandFmt ) {
case IM_BANDFMT_UCHAR:
icc_format |= BYTES_SH( 1 );
break;
case IM_BANDFMT_USHORT:
icc_format |= BYTES_SH( 2 );
break;
default:
im_error( "im_icc_transform",
"%s", _( "uchar or ushort input only" ) );
return( -1 );
}
/* Use cmsFLAGS_NOCACHE to disable the 1-pixel cache and make
* calling cmsDoTrsnaform() from multiple threads safe.
*/
if( !(icc->trans = cmsCreateTransform( icc->in_profile, icc_format,
icc->out_profile, TYPE_Lab_16, icc->intent,
cmsFLAGS_NOCACHE )) )
return( -1 );
/* Process!
*/
if( im_wrapone( in, out, (im_wrapone_fn) import_buf, icc, NULL ) )
return( -1 );
return( 0 );
}
/**
* im_icc_import:
* @in: input image
* @out: output image
* @input_profile_filename: get the input profile from here
* @intent: transform with this intent
*
* Import an image with the ICC library. The input image in device space
* is moved to D65 LAB with the input profile.
*
* See also: im_icc_transform(), im_icc_import_embedded().
*
* Returns: 0 on success, -1 on error.
*/
int
im_icc_import( IMAGE *in, IMAGE *out,
const char *input_profile_filename, VipsIntent intent )
{
Icc *icc;
if( !(icc = icc_new_file( in, out,
input_profile_filename, NULL, intent )) ||
icc_import( in, out, icc ) )
return( -1 );
return( 0 );
}
/**
* im_icc_import_embedded:
* @in: input image
* @out: output image
* @intent: transform with this intent
*
* Import an image with the ICC library. The input image in device space
* is moved to D65 LAB with the input profile attached to the image under the
* name #IM_META_ICC_NAME.
*
* See also: im_icc_transform(), im_icc_import().
*
* Returns: 0 on success, -1 on error.
*/
int
im_icc_import_embedded( IMAGE *in, IMAGE *out, VipsIntent intent )
{
Icc *icc;
void *data;
size_t data_length;
if( im_header_get_typeof( in, IM_META_ICC_NAME ) == 0 ) {
im_error( "im_icc_import_embedded",
"%s", _( "no embedded profile" ) );
return( -1 );
}
if( im_meta_get_blob( in, IM_META_ICC_NAME, &data, &data_length ) ||
!(icc = icc_new_mem( in, out, data, data_length, intent )) ||
icc_import( in, out, icc ) )
return( -1 );
return( 0 );
}
static void
export_buf( float *in, PEL *out, int n, Icc *icc )
{
/* Buffer of encoded 16-bit pixels we transform.
*/
guint16 encoded[3 * PIXEL_BUFFER_SIZE];
while( n > 0 ) {
const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE );
encode_lab( in, encoded, chunk );
cmsDoTransform( icc->trans, encoded, out, chunk );
in += chunk * 3;
out += chunk * IM_IMAGE_SIZEOF_PEL( icc->out );
n -= chunk;
}
}
/**
* im_icc_export_depth:
* @in: input image
* @out: output image
* @depth: depth to export at
* @output_profile_filename: use this profile
* @intent: transform with this intent
*
* Export an image with the ICC library. The input image in
* D65 LAB is transformed to device space using the supplied profile.
* @depth can be 8 or 16, for 8 or 16-bit image export.
*
* See also: im_icc_transform(), im_icc_import().
*
* Returns: 0 on success, -1 on error.
*/
int
im_icc_export_depth( IMAGE *in, IMAGE *out, int depth,
const char *output_profile_filename, VipsIntent intent )
{
Icc *icc;
cmsUInt32Number icc_format;
/* Do IM_CODING_LABQ too.
*/
if( in->Coding == IM_CODING_LABQ ) {
IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" );
if( !t1 || im_LabQ2Lab( in, t1 ) )
return( -1 );
in = t1;
}
/* Do IM_CODING_RAD.
*/
if( in->Coding == IM_CODING_RAD ) {
IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" );
if( !t1 || im_rad2float( in, t1 ) )
return( -1 );
in = t1;
}
/* Check input image.
*/
if( im_check_uncoded( "im_icc_export", in ) ||
im_check_bands( "im_icc_export", in, 3 ) ||
im_check_format( "im_icc_export", in, IM_BANDFMT_FLOAT ) )
return( -1 );
if( depth != 8 && depth != 16 ) {
im_error( "im_icc_export", "%s", _( "unsupported bit depth" ) );
return( -1 );
}
if( !(icc = icc_new_file( in, out,
NULL, output_profile_filename, intent )) )
return( -1 );
if( !cmsIsIntentSupported( icc->out_profile,
intent, LCMS_USED_AS_OUTPUT ) )
im_warn( "im_icc_export",
_( "intent %d (%s) not supported by "
"profile \"%s\"; falling back to default intent "
"(usually PERCEPTUAL)" ),
intent, decode_intent( intent ),
output_profile_filename );
/* Prepare the output image.
*/
if( im_cp_desc( out, in ) )
return( -1 );
switch( cmsGetColorSpace( icc->out_profile ) ) {
case cmsSigCmykData:
out->Type = IM_TYPE_CMYK;
out->BandFmt = depth == 8 ?
IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT;
out->Bands = 4;
icc_format = depth == 8 ? TYPE_CMYK_8 : TYPE_CMYK_16;
break;
case cmsSigRgbData:
out->Type = depth == 8 ?
IM_TYPE_RGB : IM_TYPE_RGB16;
out->BandFmt = depth == 8 ?
IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT;
out->Bands = 3;
icc_format = depth == 8 ? TYPE_RGB_8 : TYPE_RGB_16;
break;
default:
im_error( "im_icc_export", _( "unimplemented output color "
"space 0x%x" ), cmsGetColorSpace( icc->out_profile ) );
return( -1 );
}
/* Use cmsFLAGS_NOCACHE to disable the 1-pixel cache and make
* calling cmsDoTrsnaform() from multiple threads safe.
*/
if( !(icc->trans = cmsCreateTransform( icc->in_profile, TYPE_Lab_16,
icc->out_profile, icc_format, intent,
cmsFLAGS_NOCACHE )) )
return( -1 );
if( attach_profile( out, output_profile_filename ) )
return( -1 );
/* Process!
*/
if( im_wrapone( in, out, (im_wrapone_fn) export_buf, icc, NULL ) )
return( -1 );
return( 0 );
}
/**
* im_icc_ac2rc:
* @in: input image
* @out: output image
* @profile_filename: use this profile
*
* Transform an image from absolute to relative colorimetry using the
* MediaWhitePoint stored in the ICC profile.
*
* See also: im_icc_transform(), im_icc_import().
*
* Returns: 0 on success, -1 on error.
*/
int
im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename )
{
cmsHPROFILE profile;
cmsCIEXYZ *media;
double X, Y, Z;
double add[3];
double mul[3];
IMAGE *t[2];
if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) )
return( -1 );
if( !(media = cmsReadTag( profile, cmsSigMediaWhitePointTag )) ) {
im_error( "im_icc_ac2rc", "%s", _( "unable to get media "
"white point" ) );
return( -1 );
}
X = media->X;
Y = media->Y;
Z = media->Z;
cmsCloseProfile( profile );
add[0] = 0.0;
add[1] = 0.0;
add[2] = 0.0;
mul[0] = IM_D50_X0 / (X * 100.0);
mul[1] = IM_D50_Y0 / (Y * 100.0);
mul[2] = IM_D50_Z0 / (Z * 100.0);
/* Do IM_CODING_LABQ too.
*/
if( in->Coding == IM_CODING_LABQ ) {
IMAGE *t1 = im_open_local( out, "im_icc_ac2rc-1", "p" );
if( !t1 || im_LabQ2Lab( in, t1 ) )
return( -1 );
in = t1;
}
/* Do IM_CODING_RAD.
*/
if( in->Coding == IM_CODING_RAD ) {
IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" );
if( !t1 || im_rad2float( in, t1 ) )
return( -1 );
in = t1;
}
if( im_open_local_array( out, t, 2, "im_icc_ac2rc-2", "p" ) ||
im_Lab2XYZ_temp( in, t[0], IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) ||
im_lintra_vec( 3, mul, t[0], add, t[1] ) ||
im_XYZ2Lab_temp( t[1], out, IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) )
return( -1 );
return( 0 );
}
#else /*no lcms*/
#include <vips/vips.h>
int
im_icc_present( void )
{
return( 0 );
}
int
im_icc_transform( IMAGE *in, IMAGE *out,
const char *input_profile_filename,
const char *output_profile_filename,
VipsIntent intent )
{
im_error( "im_icc_transform", "%s",
_( "lcms library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_import( IMAGE *in, IMAGE *out,
const char *input_profile_filename, VipsIntent intent )
{
im_error( "im_icc_import", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_import_embedded( IMAGE *in, IMAGE *out, VipsIntent intent )
{
im_error( "im_icc_import", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_export_depth( IMAGE *in, IMAGE *out, int depth,
const char *output_profile_filename, VipsIntent intent )
{
im_error( "im_icc_export_depth", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
int
im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename )
{
im_error( "im_icc_ac2rc", "%s",
_( "lmcs library not linked to this VIPS" ) );
return( -1 );
}
#endif /*HAVE_LCMS*/