summaryrefslogtreecommitdiff
path: root/profile/profout.c
diff options
context:
space:
mode:
Diffstat (limited to 'profile/profout.c')
-rw-r--r--profile/profout.c298
1 files changed, 227 insertions, 71 deletions
diff --git a/profile/profout.c b/profile/profout.c
index 4ad62aa..f12589b 100644
--- a/profile/profout.c
+++ b/profile/profout.c
@@ -38,13 +38,21 @@
* Fix error handling
* fix verbose output
* hand icc object back rather than writing file ?
+ *
+ * In theory we could add a ARGYLL_CREATE_OUTPUT_PROFILE_WITH_CHAD option
+ * for the case of a non-D50 illuminant. spec2cie (and colprof internally) would
+ * have to put the illuminant white point in the .ti3 file and chromatically
+ * transform the XYZ/Lab values, and then colprof would create the chad tag
+ * with the illuminant to D50 matrix. icclib would have to undo the
+ * transform for absolute intent.
*/
/*
Outline of code flow:
profout:
- Create ICC profile and all the tags. Table tags are initialy not set.
+ Create ICC profile and all the tags, and setup any options.
+ Table tags are initialy not set.
Read in the CGTATS data and convert spectral to PCS if needed.
@@ -164,8 +172,8 @@ typedef struct {
double filter_thr; /* Clip DE threshold */
double filter_ratio; /* Clip DE to radius factor */
double filter_maxrad; /* Clip maximum filter radius */
- icColorSpaceSignature pcsspace; /* The PCS colorspace */
- icColorSpaceSignature devspace; /* The device colorspace */
+ icColorSpaceSignature pcsspace; /* The profile PCS colorspace */
+ icColorSpaceSignature devspace; /* The profile device colorspace */
icxLuLut *x; /* A2B icxLuLut we are inverting in std PCS */
int ntables; /* Number of tables being set. 1 = colorimetric */
@@ -176,6 +184,8 @@ typedef struct {
icxLuBase *ixp; /* Source profile perceptual PCS to CAM conversion */
icxLuBase *ox; /* Destination profile CAM to std PCS conversion */
/* (This is NOT used for the colorimetric B2A table creation!) */
+ icxcam *icam; /* Alternate to ixp when using default general compression */
+ icColorSpaceSignature mapsp; /* output space needed from icam conversion */
/* Abstract transform for each table. These may be */
/* duplicates. */
@@ -406,8 +416,25 @@ void out_b2a_clut(void *cntx, double *out, double in[3]) {
}
DBG(("convert PCS' to PCS got %f %f %f\n",in1[0],in1[1],in1[2]))
- /* Convert from PCS to CAM/Gamut mapping space */
- p->ixp->fwd_relpcs_outpcs(p->ixp, p->pcsspace, in1, in1);
+ /* Convert from profile PCS to CAM/Gamut mapping space */
+ if (p->ixp != NULL) {
+ p->ixp->fwd_relpcs_outpcs(p->ixp, p->pcsspace, in1, in1);
+
+ } else { /* General compression fallback conversion to CAM/Gamut mapping space */
+
+ if (p->mapsp == icxSigJabData) { /* Want icxSigJabData for mapping */
+
+ if (p->pcsspace == icSigLabData)
+ icmLab2XYZ(&icmD50, in1, in1);
+
+ p->icam->XYZ_to_cam(p->icam, in1, in1);
+
+ } else { /* Must be icSigLabData for mapping */
+
+ if (p->pcsspace == icSigXYZData)
+ icmXYZ2Lab(&icmD50, in1, in1);
+ }
+ }
DBG(("convert PCS to CAM got %f %f %f\n",in1[0],in1[1],in1[2]))
@@ -651,15 +678,19 @@ make_output_icc(
char *file_name, /* output icc name */
cgats *icg, /* input cgats structure */
int spec, /* Use spectral data flag */
- icxIllumeType tillum, /* Target/simulated instrument illuminant */
- xspect *cust_tillum, /* Possible custom target/simulated instrument illumination */
- icxIllumeType illum, /* Spectral illuminant */
- xspect *cust_illum, /* Possible custom illumination */
- icxObserverType observ, /* Spectral observer */
+ icxIllumeType tillum, /* Target/simulated instrument illuminant, if set. */
+ xspect *cust_tillum, /* Custom target/simulated illumination spectrum */
+ /* if tillum == icxIT_custom */
+ icxIllumeType illum, /* CIE calc. illuminant spectrum, and FWA inst. */
+ /* illuminant if tillum not set. */
+ xspect *cust_illum, /* Custom CIE illumination spectrum if illum == icxIT_custom */
+ icxObserverType observ, /* CIE calc. observer */
int fwacomp, /* FWA compensation requested */
double smooth, /* RSPL smoothing factor, -ve if raw */
double avgdev, /* reading Average Deviation as a proportion of the input range */
double demph, /* Emphasise dark region grid resolution in cLUT */
+ int gcompr, /* Gamut compression % if > 0, rather than use ipname */
+ int gexpr, /* Gamut saturation expansion % if gcompr > 0 rather */
char *ipname, /* input icc profile - enables gamut map, NULL if none */
char *sgname, /* source image gamut - NULL if none */
char *absname[3], /* abstract profile name for each table */
@@ -676,6 +707,8 @@ make_output_icc(
double dispLuminance = 0.0; /* Display luminance. 0.0 if not known */
int isdnormed = 0; /* Has display data been normalised to white Y = 100 ? */
int allintents; /* nz if all intents should possibly be created */
+ double *ill_wp = NULL; /* If illum is not D50, illum white point XYZ */
+ double _ill_wp[3]; /* (What ill_wp points at if it is not NULL) */
icmFile *wr_fp;
icc *wr_icco;
int npat; /* Number of patches */
@@ -727,7 +760,7 @@ make_output_icc(
if (strcmp(icg->t[0].kdata[ti],"DISPLAY") == 0) {
isdisp = 1;
- if (isLut && ipname != NULL)
+ if (isLut && (ipname != NULL || gcompr > 0))
allintents = 1;
else
allintents = 0; /* Only the default intent */
@@ -757,6 +790,28 @@ make_output_icc(
}
}
+ /* See if CIE illuminant white point is given, in case CIE data */
+ /* is used, and 'chad' tag is going to be created. */
+ if (!isdisp) {
+ int ti;
+
+ if ((ti = icg->find_kword(icg, 0, "ILLUMINANT_WHITE_POINT_XYZ")) >= 0) {
+ if (sscanf(icg->t[0].kdata[ti], " %lf %lf %lf ",&_ill_wp[0],&_ill_wp[1],&_ill_wp[2]) == 3) {
+
+ /* Normalize it */
+ if (_ill_wp[1] > 1e-6) {
+ _ill_wp[0] /= _ill_wp[1];
+ _ill_wp[2] /= _ill_wp[1];
+ _ill_wp[1] = 1.0;
+
+ ill_wp = _ill_wp;
+ }
+ }
+ if (verb)
+ fprintf(verbo,"Unable to parse 'ILLUMINANT_WHITE_POINT_XYZ' keyword\n");
+ }
+ }
+
/* Figure out what sort of device colorspace it is */
{
int ti;
@@ -1309,7 +1364,7 @@ make_output_icc(
if (allintents) { /* All the intents may be needed */
- if (ipname == NULL) { /* No gamut mapping */
+ if (ipname == NULL && gcompr <= 0) { /* No gamut mapping */
icmLut *wo;
/* link intent 0 = perceptual to intent 1 = colorimetric */
@@ -1476,7 +1531,7 @@ make_output_icc(
if (fseek(fp, 0, SEEK_END))
error("Unable to seek to end of file '%s'",in_name);
wo->size = ftell(fp) + 1; /* Size needed + null */
- wo->allocate((icmBase *)wo);/* Allocate space */
+ wo->allocate((icmBase *)wo); /* Allocate space */
if (fseek(fp, 0, SEEK_SET))
error("Unable to seek to end of file '%s'",in_name);
@@ -1730,6 +1785,16 @@ make_output_icc(
cust_illum = NULL;
}
+ /* If CIE calculation illuminant is not standard, compute it's white point, */
+ /* in case we are going to create 'chad' tag. */
+ if (!isdisp && illum != icxIT_D50) {
+ ill_wp = _ill_wp;
+
+ /* Compute XYZ of illuminant */
+ if (icx_ill_sp2XYZ(ill_wp, observ, NULL, illum, 0.0, cust_illum) != 0)
+ error("icx_ill_sp2XYZ returned error");
+ }
+
/* Create a spectral conversion object */
if ((sp2cie = new_xsp2cie(illum, cust_illum, observ, NULL,
wantLab ? icSigLabData : icSigXYZData, icxClamp)) == NULL)
@@ -1902,6 +1967,12 @@ make_output_icc(
} /* End of reading in CGATs file */
+ /* If we have been given an illuminant white point, set this in the */
+ /* ICC profile so that it can save a 'chad' tag if */
+ /* ARGYLL_CREATE_OUTPUT_PROFILE_WITH_CHAD is set. */
+ if (ill_wp != NULL)
+ wr_icco->set_illum(wr_icco, ill_wp);
+
#ifdef EMPH_DISP_BLACKPOINT
/* Add extra weighting to sample points near black for additive display. */
/* Not sure what the justification is, apart from making the black */
@@ -2000,6 +2071,7 @@ make_output_icc(
error("Creation of xicc failed");
flags |= ICX_CLIP_NEAREST; /* This will avoid clip caused rev setup */
+ /* which we don't need when creating A2B */
if (noisluts)
flags |= ICX_NO_IN_SHP_LUTS;
@@ -2082,13 +2154,12 @@ make_output_icc(
xicc *x;
icxViewCond *v, *vc;
int es;
+ double *wp = NULL;
if (i == 0) { /* Input */
v = ivc_p; /* Override parameters */
es = ivc_e;
vc = &ivc; /* Target parameters */
- if (src_xicc == NULL)
- continue; /* Source viewing conditions won't be used */
x = src_xicc;
} else { /* Output */
v = ovc_p; /* Override parameters */
@@ -2096,13 +2167,16 @@ make_output_icc(
vc = &ovc; /* Target parameters */
x = wr_xicc;
}
+
+ if (x == NULL)
+ wp = icmD50_ary3; /* So xicc_enum_viewcond will work without xicc */
/* Set the default */
- xicc_enum_viewcond(x, vc, -1, NULL, 0, NULL);
+ xicc_enum_viewcond(x, vc, -1, NULL, 0, wp);
/* Override the viewing conditions */
if (es >= 0)
- if (xicc_enum_viewcond(x, vc, es, NULL, 0, NULL) == -2)
+ if (xicc_enum_viewcond(x, vc, es, NULL, 0, wp) == -2)
error ("%d, %s",x->errc, x->err);
if (v->Ev >= 0)
vc->Ev = v->Ev;
@@ -2142,6 +2216,8 @@ make_output_icc(
vc->Gxyz[0] = x/y * vc->Gxyz[1];
vc->Gxyz[2] = z/y * vc->Gxyz[1];
}
+ if (v->hkscale >= 0.0)
+ vc->hkscale = v->hkscale;
}
/* Get a suitable forward conversion object to invert. */
@@ -2153,7 +2229,16 @@ make_output_icc(
if (verb)
flags |= ICX_VERBOSE;
- flags |= ICX_CLIP_NEAREST; /* Not vector clip */
+#ifdef NEVER /* [Und] */
+ /* Vector is somewhat flakey (some CuspMap based vectors don't 100%
+ intersect the gamut, having to fall back on nearest), and the result
+ is rather poor in saturation for green and yellow in particular. */
+ flags |= ICX_CLIP_VECTOR;
+ warning("!!!! ICX_CLIP_VECTOR in profout.c is on !!!!");
+#else
+ flags |= ICX_CLIP_NEAREST;
+#endif
+
#ifdef USE_CAM_CLIP_OPT
flags |= ICX_CAM_CLIP; /* Clip in CAM Jab space rather than Lab */
#else
@@ -2180,6 +2265,8 @@ make_output_icc(
cx.ixp = NULL; /* Perceptual PCS to CAM conversion */
cx.ox = NULL; /* CAM to PCS conversion */
+ cx.icam = NULL;
+ cx.mapsp = 0;
cx.pmap = NULL; /* perceptual gamut map */
cx.smap = NULL; /* Saturation gamut map */
@@ -2191,7 +2278,7 @@ make_output_icc(
/* Determine the number of tables */
cx.ntables = 1;
- if (src_xicc) { /* Creating separate perceptual and Saturation tables */
+ if (src_xicc || gcompr) { /* Creating separate perceptual and Saturation tables */
cx.ntables = 2;
if (sepsat)
cx.ntables = 3;
@@ -2261,16 +2348,17 @@ make_output_icc(
wr_icco, icSigBToA1Tag)) == NULL)
error("read_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
- if (src_xicc) { /* Creating separate perceptual and Saturation tables */
+ if (src_xicc || gcompr) { /* Creating separate perceptual and Saturation tables */
icRenderingIntent intentp; /* Gamut mapping space perceptual selection */
icRenderingIntent intents; /* Gamut mapping space saturation selection */
icRenderingIntent intento; /* Gamut mapping space output selection */
- gamut *csgamp; /* Incoming colorspace perceptual gamut */
- gamut *csgams; /* Incoming colorspace saturation gamut */
- gamut *igam = NULL; /* Incoming image gamut */
- gamut *ogam; /* Destination colorspace gamut */
- double gres; /* Gamut surface feature resolution */
- int mapres; /* Mapping rspl resolution */
+ gamut *csgamp = NULL; /* Incoming colorspace perceptual gamut */
+ gamut *csgams = NULL; /* Incoming colorspace saturation gamut */
+ gamut *igam = NULL; /* Incoming image gamut */
+ gamut *ogam = NULL; /* Destination colorspace gamut */
+ gamut *ihgam = NULL; /* Input space general compression hull gamut */
+ double gres; /* Gamut surface feature resolution */
+ int mapres; /* Mapping rspl resolution */
if (verb)
printf("Creating Gamut Mapping\n");
@@ -2353,50 +2441,56 @@ make_output_icc(
/* Unlike icclink, we've not provided a way for the user */
/* to set the source profile ink limit, so estimate it */
/* from the profile */
- icxDefaultLimits(src_xicc, &iink.tlimit, iink.tlimit,
- &iink.klimit, iink.klimit);
-
- /* Get lookup object simply for fwd_relpcs_outpcs() */
- /* and perceptual input gamut shell creation. */
- /* Note that the intent=Appearance will trigger Jab CAM, */
- /* overriding icSigLabData.. */
-#ifdef NEVER
- printf("~1 input space flags = 0x%x\n",ICX_CLIP_NEAREST);
- printf("~1 input space intent = %s\n",icx2str(icmRenderingIntent,intentp));
- printf("~1 input space pcs = %s\n",icx2str(icmColorSpaceSignature,icSigLabData));
- printf("~1 input space viewing conditions =\n"); xicc_dump_viewcond(&ivc);
- printf("~1 input space inking =\n"); xicc_dump_inking(&iink);
-#endif
- if ((cx.ixp = src_xicc->get_luobj(src_xicc,ICX_CLIP_NEAREST
- , icmFwd, intentp, icSigLabData, icmLuOrdNorm, &ivc, &iink)) == NULL)
- error ("%d, %s",src_xicc->errc, src_xicc->err);
+ if (src_xicc != NULL) {
+ icxDefaultLimits(src_xicc, &iink.tlimit, iink.tlimit,
+ &iink.klimit, iink.klimit);
- /* Create the source colorspace gamut surface */
- if (verb)
- printf(" Finding Source Colorspace Perceptual Gamut\n");
-
- if ((csgamp = cx.ixp->get_gamut(cx.ixp, gres)) == NULL)
- error ("%d, %s",src_xicc->errc, src_xicc->err);
-
- if (sepsat) {
- icxLuBase *ixs = NULL; /* Source profile saturation lookup for gamut */
- /* Get lookup object for saturation input gamut shell creation */
+ /* Get lookup object simply for fwd_relpcs_outpcs() */
+ /* and perceptual input gamut shell creation. */
/* Note that the intent=Appearance will trigger Jab CAM, */
/* overriding icSigLabData.. */
- if ((ixs = src_xicc->get_luobj(src_xicc, ICX_CLIP_NEAREST
- , icmFwd, intents, icSigLabData, icmLuOrdNorm, &ivc, &iink)) == NULL)
- error ("%d, %s",src_xicc->errc, src_xicc->err);
-
+#ifdef NEVER
+ printf("~1 input space flags = 0x%x\n",ICX_CLIP_NEAREST);
+ printf("~1 input space intent = %s\n",icx2str(icmRenderingIntent,intentp));
+ printf("~1 input space pcs = %s\n",icx2str(icmColorSpaceSignature,icSigLabData));
+ printf("~1 input space viewing conditions =\n"); xicc_dump_viewcond(&ivc);
+ printf("~1 input space inking =\n"); xicc_dump_inking(&iink);
+#endif
+ if ((cx.ixp = src_xicc->get_luobj(src_xicc,ICX_CLIP_NEAREST
+ , icmFwd, intentp, icSigLabData, icmLuOrdNorm, &ivc, &iink)) == NULL)
+ error ("%d, %s",src_xicc->errc, src_xicc->err);
+
+ /* Create the source colorspace gamut surface */
if (verb)
- printf(" Finding Source Colorspace Saturation Gamut\n");
-
- if ((csgams = ixs->get_gamut(ixs, gres)) == NULL)
+ printf(" Finding Source Colorspace Perceptual Gamut\n");
+
+ if ((csgamp = cx.ixp->get_gamut(cx.ixp, gres)) == NULL)
+ error ("%d, %s",src_xicc->errc, src_xicc->err);
+
+ if (sepsat) {
+ icxLuBase *ixs = NULL; /* Source profile saturation lookup for gamut */
+ /* Get lookup object for saturation input gamut shell creation */
+ /* Note that the intent=Appearance will trigger Jab CAM, */
+ /* overriding icSigLabData.. */
+ if ((ixs = src_xicc->get_luobj(src_xicc, ICX_CLIP_NEAREST
+ , icmFwd, intents, icSigLabData, icmLuOrdNorm, &ivc, &iink)) == NULL)
error ("%d, %s",src_xicc->errc, src_xicc->err);
- ixs->del(ixs);
+
+ if (verb)
+ printf(" Finding Source Colorspace Saturation Gamut\n");
+
+ if ((csgams = ixs->get_gamut(ixs, gres)) == NULL)
+ error ("%d, %s",src_xicc->errc, src_xicc->err);
+ ixs->del(ixs);
+ }
}
-
+
/* Read image source gamut if provided */
- if (sgname != NULL) { /* Optional source gamut - ie. from an images */
+ /* Optional source gamut - ie. from an images, */
+ /* ignored if gcompr > 0 */
+ if (sgname != NULL && gcompr > 0)
+ warning("Image gamut ignored for general gamut compression");
+ if (sgname != NULL && gcompr == 0) {
int isJab = 0;
if ((pgmi->usecas & 0xff) >= 0x2)
@@ -2418,7 +2512,7 @@ make_output_icc(
/* At the moment it's up to the user to get this right. */
}
}
-
+
/* Get lookup object for bwd_outpcs_relpcs(), */
/* and output gamut shell creation */
/* Note that the intent=Appearance will trigger Jab CAM, */
@@ -2435,6 +2529,45 @@ make_output_icc(
if ((ogam = cx.ox->get_gamut(cx.ox, gres)) == NULL)
error ("%d, %s",wr_xicc->errc, wr_xicc->err);
+ /* General compression rather than source gamut */
+ if (gcompr > 0) {
+ /* Create alternative to ixp for conv. to Gamut maping space. */
+ if (cx.ixp == NULL) {
+ double wp[3], bp[3] = { 0.0, 0.0, 0.0 };
+ cx.mapsp = xiccIsIntentJab(intentp) ? icxSigJabData : icSigLabData;
+
+ if (cx.mapsp == icxSigJabData) {
+ cx.icam = new_icxcam(cam_default);
+ cx.icam->set_view(cx.icam, ivc.Ev, ivc.Wxyz, ivc.La, ivc.Yb, ivc.Lv,
+ ivc.Yf, ivc.Yg, ivc.Gxyz,
+ XICC_USE_HK, ivc.hkscale);
+ }
+
+ /* Create a dumy source gamut, used by new_gammap to create */
+ /* the L mapping */
+ if ((csgamp = new_gamut(0.0, cx.mapsp == icxSigJabData, 0)) == NULL)
+ error ("Creating fake input gamut failed");
+
+ if (cx.mapsp == icxSigJabData) {
+ cx.icam->XYZ_to_cam(cx.icam, wp, icmD50_ary3);
+ cx.icam->XYZ_to_cam(cx.icam, bp, bp);
+ } else {
+ icmXYZ2Lab(&icmD50, bp, icmD50_ary3);
+ icmXYZ2Lab(&icmD50, bp, bp);
+ }
+ csgamp->setwb(csgamp, wp, bp, bp);
+ csgams = csgamp;
+ }
+
+ /* Expand destination gamut cylindrically to make source hull gamut. */
+ if (verb)
+ printf(" Creating fake source gamut with compression %d%%\n",gcompr);
+ if ((ihgam = new_gamut(1.0, 0, 0)) == NULL
+ || ihgam->exp_cyl(ihgam, ogam, (100.0 + gcompr)/100.0)) {
+ error ("Creating expanded input failed");
+ }
+ }
+
if (verb)
printf(" Creating Gamut match\n");
@@ -2447,7 +2580,8 @@ make_output_icc(
/* values outside the grid range. */
/* setup perceptual gamut mapping */
- cx.pmap = new_gammap(verb, csgamp, igam, ogam, pgmi, 0, 0, 0, 0, mapres,
+ cx.pmap = new_gammap(verb, csgamp, igam, ogam, pgmi,
+ ihgam, 0, 0, 0, 0, mapres,
NULL, NULL, gamdiag ? "gammap_p.wrl" : NULL
);
if (cx.pmap == NULL)
@@ -2459,8 +2593,24 @@ make_output_icc(
error("read_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
if (sepsat) {
+ /* General expansion rather than source gamut */
+ if (gcompr > 0) {
+ if (gexpr == 0)
+ gexpr = gcompr;
+ /* Expand destination gamut cylindrically to make source gamut. */
+ if (verb)
+ printf(" Creating fake source gamut with expansion %d%%\n",gexpr);
+ if (ihgam != NULL)
+ ihgam->del(ihgam);
+ if ((ihgam = new_gamut(1.0, 0, 0)) == NULL
+ || ihgam->exp_cyl(ihgam, ogam, (100.0 - gexpr)/100.0)) {
+ error ("Creating compressed input failed");
+ }
+ }
+
/* setup saturation gamut mapping */
- cx.smap = new_gammap(verb, csgams, igam, ogam, sgmi, 0, 0, 0, 0, mapres,
+ cx.smap = new_gammap(verb, csgams, igam, ogam, sgmi,
+ ihgam, 0, 0, 0, 0, mapres,
NULL, NULL, gamdiag ? "gammap_s.wrl" : NULL
);
if (cx.smap == NULL)
@@ -2471,18 +2621,22 @@ make_output_icc(
wr_icco, icSigBToA2Tag)) == NULL)
error("read_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
}
- csgamp->del(csgamp);
- csgamp = NULL;
- if (sepsat) {
+ if (csgams != NULL && csgams != csgamp) {
csgams->del(csgams);
csgams = NULL;
}
+ if (csgamp != NULL) {
+ csgamp->del(csgamp);
+ csgamp = NULL;
+ }
if (igam != NULL) {
igam->del(igam);
igam = NULL;
}
- ogam->del(ogam);
- ogam = NULL;
+ if (ogam != NULL) {
+ ogam->del(ogam);
+ ogam = NULL;
+ }
}
}
cx.ochan = wo[0]->outputChan;
@@ -2802,6 +2956,8 @@ make_output_icc(
cx.ixp->del(cx.ixp), cx.ixp = NULL;
if (cx.ox != NULL)
cx.ox->del(cx.ox), cx.ox = NULL;
+ if (cx.icam != NULL)
+ cx.icam->del(cx.icam), cx.icam = NULL;
if (src_xicc != NULL)
src_xicc->del(src_xicc), src_xicc = NULL;