Files
argyllcms/xicc/xcolorants.c
2026-08-20 20:28:40 +01:00

784 lines
21 KiB
C

/*
* International Color Consortium color transform expanded support
* Known colorants support.
*/
/*
* Author: Graeme W. Gill
* Date: 24/2/2002
* Version: 1.00
*
* Copyright 2002 Graeme W. Gill
* All rights reserved.
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*
*/
/* TTBD:
Would like to have some short string way of defining whether
the ink combination is additive or subtractive, instead
of just guessing.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#ifndef SALONEINSTLIB
#include "icc.h"
#else
#include "numsup.h"
#include "sa_conv.h"
#endif
#include "xcolorants.h"
#include "sort.h"
/* Colorant table for N color device characterisation. */
/* This is ordered to match the ICC colorspace convention. */
/* NOTE:- need to keep these in same order as ink mask */
/* enumeration (lsb to msb), or strange things result! */
static struct {
inkmask m; /* Mask value */
char *c; /* 1/2 Character name */
char *s; /* Everyday name */
char *ps; /* Postscript colorant name */
double aXYZ[3]; /* Rough XYZ values (0..1) for that additive colorant */
double sXYZ[3]; /* Rough XYZ values (0..1) for that subtractive colorant */
} icx_ink_table[] = {
{ ICX_CYAN, ICX_C_CYAN, ICX_S_CYAN, ICX_PS_CYAN,
{ 0.12, 0.18, 0.48 },
{ 0.12, 0.18, 0.48 } },
{ ICX_MAGENTA, ICX_C_MAGENTA, ICX_S_MAGENTA, ICX_PS_MAGENTA,
{ 0.38, 0.19, 0.20 },
{ 0.38, 0.19, 0.20 } },
{ ICX_YELLOW, ICX_C_YELLOW, ICX_S_YELLOW, ICX_PS_YELLOW,
{ 0.76, 0.81, 0.11 },
{ 0.76, 0.81, 0.11 } },
{ ICX_BLACK, ICX_C_BLACK, ICX_S_BLACK, ICX_PS_BLACK,
{ 0.01, 0.01, 0.01 },
{ 0.04, 0.04, 0.04 } },
{ ICX_ORANGE, ICX_C_ORANGE, ICX_S_ORANGE, ICX_PS_ORANGE,
{ 0.59, 0.41, 0.03 },
{ 0.59, 0.41, 0.05 } },
{ ICX_RED, ICX_C_RED, ICX_S_RED, ICX_PS_RED,
{ 0.412414, 0.212642, 0.019325 },
{ 0.40, 0.21, 0.05 } },
{ ICX_GREEN, ICX_C_GREEN, ICX_S_GREEN, ICX_PS_GREEN,
{ 0.357618, 0.715136, 0.119207 },
{ 0.11, 0.27, 0.21 } },
{ ICX_BLUE, ICX_C_BLUE, ICX_S_BLUE, ICX_PS_BLUE,
{ 0.180511, 0.072193, 0.950770 },
{ 0.11, 0.27, 0.47 } },
{ ICX_VIOLET, ICX_C_VIOLET, ICX_S_VIOLET, ICX_PS_VIOLET,
{ 0.0925, 0.0442, 0.244 },
{ 0.0925, 0.0442, 0.244 } },
{ ICX_WHITE, ICX_C_WHITE, ICX_S_WHITE, ICX_PS_WHITE,
{ 0.950543, 1.0, 1.089303 }, /* D65 ? */
{ 0.9642, 1.00, 0.8249 } }, /* D50 */
{ ICX_LIGHT_CYAN, ICX_C_LIGHT_CYAN, ICX_S_LIGHT_CYAN, ICX_PS_LIGHT_CYAN,
{ 0.76, 0.89, 1.08 },
{ 0.76, 0.89, 1.08 } },
{ ICX_LIGHT_MAGENTA, ICX_C_LIGHT_MAGENTA, ICX_S_LIGHT_MAGENTA, ICX_PS_LIGHT_MAGENTA,
{ 0.83, 0.74, 1.02 },
{ 0.83, 0.74, 1.02 } },
{ ICX_LIGHT_YELLOW, ICX_C_LIGHT_YELLOW, ICX_S_LIGHT_YELLOW, ICX_PS_LIGHT_YELLOW,
{ 0.88, 0.97, 0.72 },
{ 0.88, 0.97, 0.72 } },
{ ICX_LIGHT_BLACK, ICX_C_LIGHT_BLACK, ICX_S_LIGHT_BLACK, ICX_PS_LIGHT_BLACK,
{ 0.56, 0.60, 0.65 },
{ 0.56, 0.60, 0.65 } },
{ ICX_MEDIUM_CYAN, ICX_C_MEDIUM_CYAN, ICX_S_MEDIUM_CYAN, ICX_PS_MEDIUM_CYAN,
{ 0.61, 0.81, 1.07 },
{ 0.61, 0.81, 1.07 } },
{ ICX_MEDIUM_MAGENTA, ICX_C_MEDIUM_MAGENTA, ICX_S_MEDIUM_MAGENTA, ICX_PS_MEDIUM_MAGENTA,
{ 0.74, 0.53, 0.97 },
{ 0.74, 0.53, 0.97 } },
{ ICX_MEDIUM_YELLOW, ICX_C_MEDIUM_YELLOW, ICX_S_MEDIUM_YELLOW, ICX_PS_MEDIUM_YELLOW,
{ 0.82, 0.93, 0.40 },
{ 0.82, 0.93, 0.40 } },
{ ICX_MEDIUM_BLACK, ICX_C_MEDIUM_BLACK, ICX_S_MEDIUM_BLACK, ICX_PS_MEDIUM_BLACK,
{ 0.27, 0.29, 0.31 },
{ 0.27, 0.29, 0.31 } },
{ ICX_LIGHT_LIGHT_BLACK, ICX_C_LIGHT_LIGHT_BLACK, ICX_S_LIGHT_LIGHT_BLACK, ICX_PS_LIGHT_LIGHT_BLACK,
{ 0.76, 0.72, 0.65 }, /* Very rough - should substiture real numbers */
{ 0.76, 0.72, 0.65 } },
{ 0, "", "", "", { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }
};
/* Colorant table for N color device characterisation */
static struct {
inkmask m; /* Mask combination */
inkmask rm; /* Light ink reduced mask combination */
icColorSpaceSignature psig; /* Appropriate primary ICC signature */
icColorSpaceSignature ssig; /* Appropriate secondary ICC signature */
char *desc; /* Description */
} icx_colcomb_table[] = {
{ ICX_K, ICX_K, icSigGrayData, icSigGrayData,
"Print grey" },
{ ICX_W, ICX_W, icSigGrayData, icSigGrayData,
"Video grey" },
{ ICX_IRGB, ICX_IRGB, icSigRgbData, icSigRgbData,
"Print RGB" },
{ ICX_RGB, ICX_RGB, icSigRgbData, icSigRgbData,
"Video RGB" },
{ ICX_CMYK, ICX_CMYK, icSigCmykData, icSigCmykData,
"CMYK" },
{ ICX_CMY, ICX_CMY, icSigCmyData, icSigCmyData,
"CMY" },
{ ICX_CMYKcm, ICX_CMYK, icSig6colorData, icSigMch6Data,
"CMYK + Light CM" },
{ ICX_CMYKcmk, ICX_CMYK, icSig7colorData, icSig7colorData,
"CMYK + Light CMK" },
{ ICX_CMYKRB, ICX_W, icSig6colorData, icSigMch6Data,
"CMYK + Red + Blue" },
{ ICX_CMYKOG, ICX_W, icSig6colorData, icSigMch6Data,
"CMYK + Orange + Green" },
{ ICX_CMYKRGB, ICX_W, icSig7colorData, icSigMch7Data,
"CMYK + Red + Green + Blue" },
{ ICX_CMYKOGV, ICX_W, icSig7colorData, icSigMch7Data,
"CMYK + Orange + Green + Violet" },
{ ICX_CMYKOGB, ICX_W, icSig7colorData, icSigMch7Data,
"CMYK + Orange + Green + Blue" },
{ ICX_CMYKcmk1k, ICX_CMYK, icSig8colorData, icSigMch8Data,
"CMYK + Light CMK + Light Light K" },
{ ICX_CMYKOGcm, ICX_CMYKOG, icSig8colorData, icSigMch8Data,
"CMYK + Orange + Green + Light CM" },
{ ICX_CMYKcm2c2m, ICX_CMYK, icSig8colorData, icSigMch8Data,
"CMYK + Light CM + Medium CM" },
{ 0, 0, 0, 0, "" }
};
/* Given an ink combination mask, return the number of recognised inks in it */
int icx_noofinks(inkmask mask) {
int i, count = 0;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask & icx_ink_table[i].m) {
count++;
}
}
return count;
}
/* Given an ink combination mask, return the 1-2 character based string */
/* If winv is nz, include ICX_INVERTED indicator if set */
/* Return NULL on error. free() after use */
char *icx_inkmask2char(inkmask mask, int winv) {
int i;
char *rv;
if ((rv = malloc(2 * ICX_MXINKS + 1)) == NULL)
return NULL;
*rv = '\000';
if (winv && (mask & ICX_INVERTED))
strcat(rv, "i");
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask & icx_ink_table[i].m) {
strcat(rv, icx_ink_table[i].c);
}
}
return rv;
}
/* Given the 1-2 character string, return the ink combination mask */
/* Note that ICX_ADDITIVE will be guessed */
/* Return 0 if unrecognised character in string */
inkmask icx_char2inkmask(char *chstring) {
inkmask mask = 0;
int k, i;
char *cp;
cp = chstring;
for (k = 0; ; k++) {
if (*cp == '\000') {
break;
}
/* "Inverted" prefix ? */
if (k == 0 && *cp == 'i') {
mask |= ICX_INVERTED;
cp++;
continue;
}
/* Colorant ? */
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (strncmp(cp, icx_ink_table[i].c, strlen(icx_ink_table[i].c)) == 0) {
mask |= icx_ink_table[i].m;
cp += strlen(icx_ink_table[i].c);
break;
}
}
if (icx_ink_table[i].m == 0) { /* oops - unrecognised character */
return 0;
}
}
/* Check it out againts known combinations to guess additive */
for (i = 0; icx_colcomb_table[i].m != 0; i++) {
if ((icx_colcomb_table[i].m & ~ICX_ADDITIVE) == mask) {
mask = icx_colcomb_table[i].m;
break;
}
}
return mask;
}
/* Given an ink combination mask that may contain light inks, */
/* return the corresponding ink mask without light inks. */
/* Return 0 if ink combination not recognised. */
inkmask icx_ink2primary_ink(inkmask mask) {
int i;
for (i = 0; icx_colcomb_table[i].m != 0; i++) {
if (mask == icx_colcomb_table[i].m) {
return icx_colcomb_table[i].rm;
}
}
return 0;
}
/* Given an ink combination mask and a single ink mask, */
/* return the index number within the set for that ink. */
/* Return -1 if mask1 not in mask */
int icx_ink2index(inkmask mask, inkmask mask1) {
int i, count = 0;
if ((mask1 & mask) == 0)
return -1;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask1 == icx_ink_table[i].m) {
return count;
}
if (mask & icx_ink_table[i].m) {
count++;
}
}
return -1;
}
/* Given an ink combination mask and a index number, */
/* return the single ink mask. */
/* Return 0 if there are no inks at that index */
inkmask icx_index2ink(inkmask mask, int ixno) {
int i, count = 0;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask & icx_ink_table[i].m) {
if (ixno == count)
return icx_ink_table[i].m;
count++;
}
}
return 0;
}
/* Given a single ink mask, */
/* return its string representation */
char *icx_ink2string(inkmask mask) {
int i;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask == icx_ink_table[i].m)
return icx_ink_table[i].s;
}
return NULL;
}
/* Given a single ink mask, */
/* return its 1-2 character representation */
char *icx_ink2char(inkmask mask) {
int i;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask == icx_ink_table[i].m)
return icx_ink_table[i].c;
}
return NULL;
}
/* Given a single ink mask, */
/* return its Postscript string representation */
char *icx_ink2psstring(inkmask mask) {
int i;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (mask == icx_ink_table[i].m)
return icx_ink_table[i].ps;
}
return NULL;
}
/* Return an enumerated single colorant description */
/* Return 0 if no such enumeration, single colorant mask if there is */
inkmask icx_enum_colorant(
int no, /* Enumeration mask index */
char **desc /* Return enumeration description */
) {
int i;
for (i = 0; icx_ink_table[i].m != 0; i++) {
if (i == no) {
if (desc != NULL)
*desc = icx_ink_table[i].s;
return icx_ink_table[i].m;
}
}
return 0;
}
/* Return an enumerated colorant combination */
/* Return 0 if no such enumeration, colorant combination mask if there is */
inkmask icx_enum_colorant_comb(
int no, /* Enumeration mask index */
char **desc /* Return enumeration description */
) {
int i;
for (i = 0; icx_colcomb_table[i].m != 0; i++) {
if (i == no) {
if (desc != NULL)
*desc = icx_colcomb_table[i].desc;
return icx_colcomb_table[i].m;
}
}
return 0;
}
/* Given an colorant combination mask, */
/* check if it matches the given ICC colorspace signature. */
/* return NZ if it does. */
/* (We don't check colorant colors for multi-colorant devices though) */
int icx_colorant_comb_match_icc(
inkmask mask, /* Colorant combination mask */
icColorSpaceSignature sig /* ICC signature */
) {
int i;
for (i = 0; icx_colcomb_table[i].m != 0; i++) {
if (mask == icx_colcomb_table[i].m) {
if (sig == icx_colcomb_table[i].psig
|| sig == icx_colcomb_table[i].ssig) {
return 1;
} else {
return 0;
}
}
}
return 0;
}
/* Given an ICC colorspace signature, return the appropriate */
/* colorant combination mask. Return 0 if ambiguous signature. */
inkmask icx_icc_to_colorant_comb(icColorSpaceSignature sig, icProfileClassSignature deviceClass) {
switch (sig) {
case icSigGrayData:
if (deviceClass == icSigOutputClass)
return ICX_K;
return ICX_W;
case icSigCmyData:
return ICX_CMY;
case icSigRgbData:
if (deviceClass == icSigOutputClass)
return ICX_IRGB;
return ICX_RGB;
case icSigCmykData:
return ICX_CMYK;
default:
break;
}
return 0;
}
/* Given an ICC colorspace signature, and a matching list */
/* of the D50 L*a*b* colors of the colorants, return the best matching */
/* colorant combination mask. Return 0 if not an applicable colorspace. */
/* (Note we're not dealing with colorant order here.) */
inkmask icx_icc_cv_to_colorant_comb(
icColorSpaceSignature sig, /* Input ICC colorspace signature */
icProfileClassSignature deviceClass, /* Device class */
double cvals[][3] /* Input L*a*b* colorant values */
) {
int i, j;
int imask;
int ninks, ncol;
double slab[ICX_MXINKS][3];
double alab[ICX_MXINKS][3];
typedef struct { int x; double v; } mchstr;
mchstr mch[MAX_CHAN][ICX_MXINKS]; /* match index */
int used[ICX_MXINKS];
int co[ICX_MXINKS]; /* Combination counter */
double cmv; /* Combination match value */
int bco[ICX_MXINKS]; /* Best Combinat */
double bcmv; /* Best Match value */
int order[MAX_CHAN]; /* Place holder, not currently used - return ink order */
switch (sig) {
case icSigXYZData:
case icSigLabData:
case icSigLuvData:
case icSigYCbCrData:
case icSigYxyData:
case icSigHsvData:
case icSigHlsData:
return 0;
case icSigGrayData:
if (deviceClass == icSigOutputClass)
return ICX_K;
return ICX_W;
case icSigCmyData:
return ICX_CMY;
case icSigRgbData:
if (deviceClass == icSigOutputClass)
return ICX_IRGB;
return ICX_RGB;
case icSigCmykData:
return ICX_CMYK;
default:
break;
}
#ifdef NEVER /* Rely on device class (above) */
if (sig == icSigGrayData) {
/* This only works reliably if we got the Lab data from a non-ICC */
/* conformant monochrome profile (one that doesn't force device 0 */
/* input of the grayTRTC to black), or uses a LUT based monochrome */
/* profile. */
/* It also won't work if someone uses a monochrome profile for a */
/* device with a non grey colorant. */
/* Really ICC should have icSigWhiteData, icSigBlackData, icSig1colorData ? */
if (cvals[0][0] > 50.0) {
return ICX_W;
} else {
return ICX_K;
}
}
#endif /* NEVER */
/* Compute Lab values of stock inks, and count them */
for (ninks = 0; ninks < ICX_MXINKS; ninks++) {
if (icx_ink_table[ninks].m == 0)
break;
icmXYZ2Lab(&icmD50, slab[ninks], icx_ink_table[ninks].sXYZ);
icmXYZ2Lab(&icmD50, alab[ninks], icx_ink_table[ninks].aXYZ);
}
ncol = icmCSSig2nchan(sig); /* Number of colorants */
/* Compute ideal matching of device colorants to stock inks */
for (i = 0; i < ncol; i++) {
for (j = 0; j < ninks; j++) {
double tt;
mch[i][j].x = j;
mch[i][j].v = icmCIE94sq(cvals[i], slab[j]);
tt = icmCIE94sq(cvals[i], alab[j]);
if (tt < mch[i][j].v)
mch[i][j].v = tt;
}
/* Sort the matches for this colorant */
#define HEAP_COMPARE(A,B) (A.v < B.v)
HEAPSORT(mchstr, mch[i], ninks)
#undef HEAP_COMPARE
//printf("\n~1 Colorant %d has best matches\n",i);
//for (j = 0; j < ninks; j++)
//printf("~1 ix %d color = '%s' dE %f\n",j, icx_ink_table[mch[i][j].x].s, mch[i][j].v);
}
/* Do exaustive combination search, using early */
/* out to keep combination count down.*/
/* Reset the combination counter */
for (j = 0; j < ninks; j++) /* Reset ink used flag */
used[j] = 0;
cmv = 0.0;
for (i = ncol-1; i >= 0; i--) {
for (j = 0; j < ninks; j++) { /* Set to lowest usable number */
if (used[mch[i][j].x] == 0) { /* Can use this one */
used[mch[i][j].x] = 1; /* Now it's matched */
cmv += mch[i][j].v;
co[i] = j;
break; /* we assume ncol < ninks */
}
}
}
//printf("\n~1 Initial combination has cmv = %f\n",cmv);
//for (i = 0; i < ncol; i++)
//printf("~1 Chan %d color = '%s'\n",i, icx_ink_table[mch[i][co[i]].x].s);
/* Set this as the best initial match */
for (i = 0; i < ncol; i++)
bco[i] = co[i];
bcmv = cmv;
/* Now go through all other combinations */
for (;;) {
/* Increment counter */
for (i = 0;;) {
/* Work up the digits, incrementing each one */
for (; i < ncol; i++) {
j = co[i];
used[mch[i][j].x] = 0;
cmv -= mch[i][j].v;
while (++j < ninks && (used[mch[i][j].x] != 0 || (cmv + mch[i][j].v) >= bcmv)) {
};
if (j < ninks) { /* No carry */
used[mch[i][j].x] = 1;
cmv += mch[i][j].v;
co[i] = j;
break;
}
/* Carry to next colorant */
}
if (i >= ncol)
break; /* Run out of digits to increment */
/* Now work down again, resetting digit */
for (--i; i >= 0; i--) {
for (j = 0; j < ninks; j++) { /* Set to lowest usable number */
if (used[mch[i][j].x] == 0 && (cmv + mch[i][j].v) < bcmv) {
/* Can use this one */
used[mch[i][j].x] = 1; /* Now it's matched */
cmv += mch[i][j].v;
co[i] = j;
break;
}
}
if (j >= ninks) { /* No combination is feasible */
i++; /* Go back to incrementing next highest digit */
break;
}
}
if (i < 0) /* We reset all the lower digits */
break; /* We're at a good combination, so done. */
}
if (i >= ncol)
break; /* We're done all combinations */
//printf("\n~1 New combination has cmv = %f\n",cmv);
//for (i = 0; i < ncol; i++)
//printf("~1 Chan %d color = '%s'\n",i, icx_ink_table[mch[i][co[i]].x].s);
/* See if this is better (it always should be !) */
if (cmv < bcmv) {
for (i = 0; i < ncol; i++)
bco[i] = co[i];
bcmv = cmv;
}
}
/* Compile the result */
for (imask = 0, i = 0; i < ncol; i++) {
imask |= icx_ink_table[mch[i][bco[i]].x].m;
order[i] = bco[i]; /* icx ink_table index corresponding to channel */
}
/* Slight hack to recognise some additive combinations */
if (imask == ICX_WHITE)
imask = ICX_W;
else if (imask == (ICX_RED | ICX_GREEN | ICX_BLUE))
imask = ICX_RGB;
return imask;
}
/* Given a colorant combination mask */
/* return the primary matching ICC colorspace signature. */
/* return 0 if there is no match */
icColorSpaceSignature icx_colorant_comb_to_icc(
inkmask mask /* Colorant combination mask */
) {
int i;
for (i = 0; icx_colcomb_table[i].m != 0; i++) {
if (mask == icx_colcomb_table[i].m)
return icx_colcomb_table[i].psig;
}
return 0;
}
/* - - - - - - - - - - - - - - - - - */
/* Approximate device colorant model */
/* Given device values, return an estimate of the XYZ value for that color. */
static void icxColorantLu_to_XYZ(
icxColorantLu *s,
double XYZ[3], /* Output */
double d[ICX_MXINKS] /* Input */
) {
int e, j;
if (s->mask & ICX_ADDITIVE ) {
/* We assume a simple additive model with gamma */
XYZ[0] = XYZ[1] = XYZ[2] = 0.0;
for (e = 0; e < s->di; e++) {
double v = d[e];
if (v < 0.0)
v = 0.0;
else if (v > 1.0)
v = 1.0;
if (v <= 0.03928)
v /= 12.92;
else
v = pow((0.055 + v)/1.055, 2.4); /* Gamma */
for (j = 0; j < 3; j++)
XYZ[j] += v * icx_ink_table[s->iix[e]].aXYZ[j];
}
/* Normalise Y to 1.0, & add black glare */
for (j = 0; j < 3; j++) {
XYZ[j] *= s->Ynorm;
XYZ[j] = XYZ[j] * (1.0 - icx_ink_table[s->bkix].aXYZ[j]) + icx_ink_table[s->bkix].aXYZ[j];
}
} else {
/* We assume a simple screened subtractive filter model, with dot gain */
/* start with white */
XYZ[0] = icx_ink_table[s->whix].sXYZ[0];
XYZ[1] = icx_ink_table[s->whix].sXYZ[1];
XYZ[2] = icx_ink_table[s->whix].sXYZ[2];
/* And filter it out for each component */
for (e = 0; e < s->di; e++) {
double v = d[e];
if (v < 0.0)
v = 0.0;
else if (v > 1.0)
v = 1.0;
v = 1.0 - pow(1.0 - v, 2.2); /* Compute dot gain */
for (j = 0; j < 3; j++) {
double fv;
/* Normalise filtering effect of this colorant */
fv = icx_ink_table[s->iix[e]].aXYZ[j]/icx_ink_table[s->whix].sXYZ[j];
/* Compute screened filtering effect */
fv = (1.0 - v) + v * fv;
/* Apply filter to our current value */
XYZ[j] *= fv;
}
}
}
}
/* Given device values, return an estimate of the */
/* relative Lab value for that color. */
static void icxColorantLu_to_rLab(
icxColorantLu *s,
double Lab[3], /* Output */
double d[ICX_MXINKS] /* Input */
) {
icxColorantLu_to_XYZ(s, Lab, d); /* Compute XYZ */
icmXYZ2Lab(&s->wp, Lab, Lab); /* Convert from XYZ to Lab */
}
/* We're done with aproximate device model */
static void icxColorantLu_del(icxColorantLu *s) {
if (s != NULL) {
free(s);
}
}
/* Given an ink definition, return an aproximate */
/* device to CIE color converted object. */
icxColorantLu *new_icxColorantLu(inkmask mask) {
int i, e;
icxColorantLu *s;
if ((s = (icxColorantLu *)malloc(sizeof(icxColorantLu))) == NULL) {
fprintf(stderr,"icxColorantLu: malloc failed allocating object\n");
exit(-1);
}
/* Initialise methods */
s->del = icxColorantLu_del;
s->dev_to_XYZ = icxColorantLu_to_XYZ;
s->dev_to_rLab = icxColorantLu_to_rLab;
/* Init */
s->mask = mask;
for (e = i = 0; icx_ink_table[i].m != 0; i++) {
if (ICX_WHITE == icx_ink_table[i].m)
s->whix = i;
if (ICX_BLACK == icx_ink_table[i].m)
s->bkix = i;
if (mask & icx_ink_table[i].m)
s->iix[e++] = i;
}
s->di = e;
s->Ynorm = 0.0;
if (mask & ICX_ADDITIVE ) {
for (e = 0; e < s->di; e++)
s->Ynorm += icx_ink_table[s->iix[e]].aXYZ[1];
s->Ynorm = 1.0/s->Ynorm;
icmAry2XYZ(s->wp, icx_ink_table[s->whix].aXYZ);
} else {
icmAry2XYZ(s->wp, icx_ink_table[s->whix].sXYZ);
}
return s;
}