diff options
Diffstat (limited to 'xicc/xicc.c')
-rw-r--r-- | xicc/xicc.c | 466 |
1 files changed, 363 insertions, 103 deletions
diff --git a/xicc/xicc.c b/xicc/xicc.c index 9b6d867..a1c4531 100644 --- a/xicc/xicc.c +++ b/xicc/xicc.c @@ -61,7 +61,7 @@ static icxLuBase *xicc_set_luobj(xicc *p, icmLookupFunc func, icRenderingIntent icmLookupOrder order, int flags, int no, int nobw, cow *points, icxMatrixModel *skm, double dispLuminance, double wpscale, double smooth, double avgdev, - icxViewCond *vc, icxInk *ink, xcal *cal, int quality); + double demph, icxViewCond *vc, icxInk *ink, xcal *cal, int quality); static void icxLutSpaces(icxLuBase *p, icColorSpaceSignature *ins, int *inn, icColorSpaceSignature *outs, int *outn, icColorSpaceSignature *pcs); @@ -929,7 +929,7 @@ icxInk *ink /* inking details (NULL for default) */ icRenderingIntent n_intent = intent; /* Native Intent to request */ icColorSpaceSignature n_pcs = icmSigDefaultData; /* Native PCS to request */ -//printf("~1 xicc_get_luobj got intent %s and pcsor %s\n",icx2str(icmRenderingIntent,intent),icx2str(icmColorSpaceSignature,pcsor)); +//printf("~1 xicc_get_luobj got intent '%s' and pcsor '%s'\n",icx2str(icmRenderingIntent,intent),icx2str(icmColorSpaceSignature,pcsor)); /* Ensure that appropriate PCS is slected for an appearance intent */ if (intent == icxAppearance @@ -939,6 +939,7 @@ icxInk *ink /* inking details (NULL for default) */ || intent == icxSaturationAppearance || intent == icxAbsSaturationAppearance) { pcsor = icxSigJabData; +//printf("~1 pcsor = %s\n",tag2str(pcsor)); /* Translate non-Jab intents to the equivalent appearance "intent" if pcsor == Jab. */ /* This is how we get these when the UI's don't list all the apperances intents, */ @@ -960,6 +961,7 @@ icxInk *ink /* inking details (NULL for default) */ else intent = icxAppearance; } +//printf("~1 intent = %s\n",tag2str(intent)); /* Translate intent asked for into intent needed in icclib */ if (intent == icxAppearance @@ -971,6 +973,7 @@ icxInk *ink /* inking details (NULL for default) */ else if (intent == icxSaturationAppearance || intent == icxAbsSaturationAppearance) n_intent = icmAbsoluteSaturation; +//printf("~1 n_intent = %s\n",tag2str(n_intent)); if (pcsor != icmSigDefaultData) n_pcs = pcsor; /* There is an icclib override */ @@ -995,15 +998,16 @@ icxInk *ink /* inking details (NULL for default) */ && (intent == icxAbsAppearance || intent == icxAbsPerceptualAppearance || intent == icxAbsSaturationAppearance)) { /* make sure its "Abs CAM" */ +//printf("~1 xicc_get_luobj using absolute apperance space with white = D50\n"); /* Set white point and flare color to D50 */ /* (Hmm. This doesn't match what happens within collink with absolute intent!!) */ vc->Wxyz[0] = icmD50.X/icmD50.Y; vc->Wxyz[1] = icmD50.Y/icmD50.Y; // Normalise white reference to Y = 1 ? vc->Wxyz[2] = icmD50.Z/icmD50.Y; - vc->Fxyz[0] = icmD50.X; - vc->Fxyz[1] = icmD50.Y; - vc->Fxyz[2] = icmD50.Z; + vc->Gxyz[0] = icmD50.X; + vc->Gxyz[1] = icmD50.Y; + vc->Gxyz[2] = icmD50.Z; } /* Call xiccLu wrapper creation */ @@ -1054,6 +1058,7 @@ double dispLuminance, /* > 0.0 if display luminance value and is known */ double wpscale, /* > 0.0 if input white point is to be scaled */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ +double demph, /* dark emphasis factor for cLUT grid res. */ icxViewCond *vc, /* Viewing Condition (NULL if not using CAM) */ icxInk *ink, /* inking details (NULL for default) */ xcal *cal, /* Optional cal, will override any existing (not deleted with xicc)*/ @@ -1104,7 +1109,7 @@ int quality /* Quality metric, 0..3 */ case icmLutType: /* ~~~ Should add check that it is a fwd profile ~~~ */ - xplu = set_icxLuLut(p, plu, func, intent, flags, no, nobw, points, skm, dispLuminance, wpscale, smooth, avgdev, vc, ink, quality); + xplu = set_icxLuLut(p, plu, func, intent, flags, no, nobw, points, skm, dispLuminance, wpscale, smooth, avgdev, demph, vc, ink, quality); break; default: @@ -1217,7 +1222,8 @@ icxViewCond *vc /* Viewing parameters to return */ double Lvr = -1.0; /* Reflective device image luminance */ double Lv = -1.0; /* device image luminance */ double Yf = -1.0; /* Flare relative luminance to Lv */ - double Fxyz[3] = {-1.0, -1.0, -1.0}; /* Flare color */ + double Yg = -1.0; /* Glare relative luminance to La */ + double Gxyz[3] = {-1.0, -1.0, -1.0}; /* Glare color */ icTechnologySignature tsig = icMaxEnumTechnology; /* Technology Signature */ icProfileClassSignature devc = icMaxEnumClass; int trans = -1; /* Set to 0 if not transparency, 1 if it is */ @@ -1241,7 +1247,8 @@ icxViewCond *vc /* Viewing parameters to return */ if ((ro = (icmMeasurement *)pp->read_tag(pp, icSigMeasurementTag)) != NULL && ro->ttype == icSigMeasurementType) { - Yf = ro->flare; + Yf = 0.0 * ro->flare; // ????? + Yg = 1.0 * ro->flare; // ????? /* ro->illuminant ie D50, D65, D93, A etc. */ } } @@ -1345,7 +1352,8 @@ icxViewCond *vc /* Viewing parameters to return */ printf("Reflective Image White Lvr = %f\n",Lvr); printf("Device Image White Lv = %f\n",Lv); printf("Relative Flare Yf = %f\n",Yf); - printf("Flare color %f %f %f\n",Fxyz[0], Fxyz[1], Fxyz[2]); + printf("Relative Glare Yg = %f\n",Yg); + printf("Glare color %f %f %f\n",Gxyz[0], Gxyz[1], Gxyz[2]); printf("Technology = %s\n",tag2str(tsig)); printf("deviceClass = %s\n",tag2str(devc)); printf("Transparency = %d\n",trans); @@ -1357,7 +1365,8 @@ icxViewCond *vc /* Viewing parameters to return */ && Yb >= 0.0 && Lv >= 0.0 && Yf >= 0.0 - && Fxyz[0] >= 0.0 && Fxyz[1] >= 0.0 && Fxyz[2] >= 0.0) { + && Yg >= 0.0 + && Gxyz[0] >= 0.0 && Gxyz[1] >= 0.0 && Gxyz[2] >= 0.0) { vc->Ev = vc_none; vc->Wxyz[0] = Wxyz[0]; @@ -1367,9 +1376,10 @@ icxViewCond *vc /* Viewing parameters to return */ vc->Yb = Yb; vc->Lv = Lv; vc->Yf = Yf; - vc->Fxyz[0] = Fxyz[0]; - vc->Fxyz[1] = Fxyz[1]; - vc->Fxyz[2] = Fxyz[2]; + vc->Yg = Yg; + vc->Gxyz[0] = Gxyz[0]; + vc->Gxyz[1] = Gxyz[1]; + vc->Gxyz[2] = Gxyz[2]; return 0; } @@ -1400,9 +1410,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.01; /* Assume 1% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1419,9 +1431,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.005; /* Assume 0.5% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1437,8 +1451,12 @@ icxViewCond *vc /* Viewing parameters to return */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ Yf = 0.0; /* Assume 0% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1453,9 +1471,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_dim; /* Assume dim viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.01; /* Assume 1% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1472,9 +1492,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.02; /* Assume 2% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1489,9 +1511,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.00; /* Assume 0% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.0; /* Assume 0% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1507,9 +1531,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_dim; /* Dim environment */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.01; /* Assume 1% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } /* Assume very darkened room, no background */ @@ -1522,9 +1548,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_dark; /* Dark environment */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.01; /* Assume 1% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1549,9 +1577,11 @@ icxViewCond *vc /* Viewing parameters to return */ if (Lv < 0.0) /* No device image luminance */ Ev = vc_average; /* Assume average viewing conditions */ if (Yf < 0.0) /* No flare figure */ - Yf = 0.01; /* Assume 1% flare */ - if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */ - Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2]; + Yf = 0.0; /* Assume 0% flare */ + if (Yg < 0.0) /* No glare figure */ + Yg = 0.01; /* Assume 1% glare */ + if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */ + Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2]; break; } @@ -1623,19 +1653,20 @@ double *wp /* Provide white point if xicc is NULL */ } } - /* Set a default flare color */ - vc->Fxyz[0] = vc->Wxyz[0]; - vc->Fxyz[1] = vc->Wxyz[1]; - vc->Fxyz[2] = vc->Wxyz[2]; + /* Set a default Glare color */ + vc->Gxyz[0] = vc->Wxyz[0]; + vc->Gxyz[1] = vc->Wxyz[1]; + vc->Gxyz[2] = vc->Wxyz[2]; } /* Typical adapting field luminances and white luminance in reflective setup: + (Note that displays Lv is typically brighter under the same conditions) E = illuminance in Lux - Lv = White luminance assuming 100% reflectance La = Adapting field luminance in cd/m^2, assuming 20% reflectance from surround + Lv = White luminance assuming 100% reflectance E La Lv Condition 11 0.7 4 Twilight @@ -1651,6 +1682,22 @@ double *wp /* Provide white point if xicc is NULL */ 10000 637 3183 Typical outdoors, full daylight 50000 3185 15915 Bright summers day + Display numbers: + + SMPTE video standard white 100 + SMPTE cinema standard white 55 + + Flare is image content dependent, and is typically 1% from factors + including display self illumination and observer/camera internal + stray light. Because image content is not static, using a 1% of white point + flare results quite erronious appearance modelling for predominantly + dark images. As a result, it is best to default to a Yf of 0%, + and only introduce a higher number depending on the known image content. + + Glare is assumed to be from the ambient light reflecting from the display + and also striking the observer directly, and is (typically) defaulted + to 1% of ambient here. + */ if (no == -1 @@ -1663,7 +1710,22 @@ double *wp /* Provide white point if xicc is NULL */ vc->La = 50.0; /* Practical to Good lighting */ vc->Lv = 250.0; /* Average viewing conditions ratio */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ + } + } + else if (no == 2 + || (as != NULL && stricmp(as,"pc") == 0)) { + + no = 2; + if (vc != NULL) { + vc->desc = " pc - Critical print evaluation environment (ISO-3664 P1)"; + vc->Ev = vc_average; /* Average viewing conditions */ + vc->La = 127.0; /* 0.2 * Lv ? */ + vc->Lv = 2000.0/3.1415; /* White of the image field */ + vc->Yb = 0.2; /* Grey world */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 0 @@ -1672,10 +1734,12 @@ double *wp /* Provide white point if xicc is NULL */ no = 0; if (vc != NULL) { vc->desc = " pp - Practical Reflection Print (ISO-3664 P2)"; - vc->Ev = vc_average; /* Average viewing conditions */ - vc->La = 32.0; /* Use a practical print evaluation number */ + vc->Ev = vc_none; /* Use explicit La/Lv */ + vc->La = 32.0; /* 0.2 * Lv ? */ + vc->Lv = 500.0/3.1415; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 1 @@ -1684,22 +1748,26 @@ double *wp /* Provide white point if xicc is NULL */ no = 1; if (vc != NULL) { vc->desc = " pe - Print evaluation environment (CIE 116-1995)"; - vc->Ev = vc_average; /* Average viewing conditions */ - vc->La = 64.0; /* Good */ + vc->Ev = vc_none; /* Use explicit La/Lv */ + vc->La = 30.0; /* 0.2 * Lv ? */ + vc->Lv = 150.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } - else if (no == 2 - || (as != NULL && stricmp(as,"pc") == 0)) { + else if (no == 4 + || (as != NULL && stricmp(as,"mb") == 0)) { - no = 2; + no = 4; if (vc != NULL) { - vc->desc = " pc - Critical print evaluation environment (ISO-3664 P1)"; - vc->Ev = vc_average; /* Average viewing conditions */ - vc->La = 127.0; /* Critical */ + vc->desc = " mb - Bright monitor in bright work environment"; + vc->Ev = vc_none; /* Use explicit La/Lv */ + vc->La = 42.0; /* Bright work environment */ + vc->Lv = 150.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 3 @@ -1708,22 +1776,12 @@ double *wp /* Provide white point if xicc is NULL */ no = 3; if (vc != NULL) { vc->desc = " mt - Monitor in typical work environment"; - vc->Ev = vc_average; /* Average viewing conditions */ + vc->Ev = vc_none; /* Use explicit La/Lv */ vc->La = 22.0; /* Typical work environment */ + vc->Lv = 120.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.02; /* 2% flare */ - } - } - else if (no == 4 - || (as != NULL && stricmp(as,"mb") == 0)) { - - no = 4; - if (vc != NULL) { - vc->desc = " mb - Bright monitor in bright work environment"; - vc->Ev = vc_average; /* Average viewing conditions */ - vc->La = 42.0; /* Bright work environment */ - vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.02; /* 2% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 5 @@ -1732,10 +1790,12 @@ double *wp /* Provide white point if xicc is NULL */ no = 5; if (vc != NULL) { vc->desc = " md - Monitor in darkened work environment"; - vc->Ev = vc_dim; /* Dim viewing conditions */ - vc->La = 4.0; /* Darkened work environment */ + vc->Ev = vc_none; /* Use explicit La/Lv */ + vc->La = 10.0; /* Darkened work environment */ + vc->Lv = 100.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 6 @@ -1744,10 +1804,12 @@ double *wp /* Provide white point if xicc is NULL */ no = 6; if (vc != NULL) { vc->desc = " jm - Projector in dim environment"; - vc->Ev = vc_dim; /* Dim viewing conditions */ + vc->Ev = vc_none; /* Use explicit La/Lv */ vc->La = 10.0; /* Adaptation is from display */ + vc->Lv = 80.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 7 @@ -1756,46 +1818,65 @@ double *wp /* Provide white point if xicc is NULL */ no = 7; if (vc != NULL) { vc->desc = " jd - Projector in dark environment"; - vc->Ev = vc_dark; /* Dark viewing conditions */ - vc->La = 10.0; /* Adaptation is from display */ + vc->Ev = vc_none; /* Use explicit La/Lv */ + vc->La = 8.0; /* Adaptation is from display */ + vc->Lv = 80.0; /* White of the image field */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare ? */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else if (no == 8 - || (as != NULL && stricmp(as,"pcd") == 0)) { + || (as != NULL && stricmp(as,"tv") == 0)) { no = 8; if (vc != NULL) { + vc->desc = " tv - Television/Film Studio"; + vc->Ev = vc_none; /* Compute from La/Lv */ + vc->La = 0.2 * 1000.0/3.1415; /* Adative/Surround */ + vc->Yb = 0.2; /* Grey world */ + vc->Lv = 1000.0/3.1415; /* White of the image field */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ + } + } + else if (no == 9 + || (as != NULL && stricmp(as,"pcd") == 0)) { + + no = 9; + if (vc != NULL) { vc->desc = "pcd - Photo CD - original scene outdoors"; vc->Ev = vc_average; /* Average viewing conditions */ vc->La = 320.0; /* Typical outdoors, 1600 cd/m^2 */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.00; /* 0% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.0; /* 0% glare - assumed to be compensated ? */ } } - else if (no == 9 + else if (no == 10 || (as != NULL && stricmp(as,"ob") == 0)) { - no = 9; + no = 10; if (vc != NULL) { vc->desc = " ob - Original scene - Bright Outdoors"; vc->Ev = vc_average; /* Average viewing conditions */ vc->La = 2000.0; /* Bright Outdoors */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.00; /* 0% flare */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.0; /* 0% glare - assumed to be compensated ? */ } } - else if (no == 10 + else if (no == 11 || (as != NULL && stricmp(as,"cx") == 0)) { - no = 10; + no = 11; if (vc != NULL) { vc->desc = " cx - Cut Sheet Transparencies on a viewing box"; vc->Ev = vc_cut_sheet; /* Cut sheet viewing conditions */ vc->La = 53.0; /* Dim, adapted to slide ? */ vc->Yb = 0.2; /* Grey world */ - vc->Yf = 0.01; /* 1% flare ? */ + vc->Yf = 0.0; /* 0% flare */ + vc->Yg = 0.01; /* 1% glare */ } } else { @@ -1828,7 +1909,8 @@ icxViewCond *vc if (vc->Ev == vc_none) printf(" Image luminance = %f cd/m^2\n",vc->Lv); printf(" Flare to image ratio = %f\n",vc->Yf); - printf(" Flare color = %f %f %f\n",vc->Fxyz[0], vc->Fxyz[1], vc->Fxyz[2]); + printf(" Glare to ambient ratio = %f\n",vc->Yg); + printf(" Flare color = %f %f %f\n",vc->Gxyz[0], vc->Gxyz[1], vc->Gxyz[2]); } @@ -1899,11 +1981,11 @@ int no, /* Enumeration selected, icxNoGMIntent for none */ char *as /* Alias string selector, NULL for none */ ) { #ifdef USE_CAM - int colccas = 0x2; /* Use cas clipping for colorimetric style intents */ - int perccas = 0x1; /* Use cas for perceptual style intents */ + int colccas = 0x3; /* Use abs. CAS for abs colorimetric intents */ + int perccas = 0x2; /* Use CAS for other intents */ #else - int colccas = 0x0; /* Use Lab for colorimetric style intents */ - int perccas = 0x0; /* Use Lab for perceptual style intents */ + int colccas = 0x1; /* Use abs. Lab for abs colorimetric intents */ + int perccas = 0x0; /* Use Lab for other intents */ fprintf(stderr,"!!!!!! Warning, USE_CAM is off in xicc.c !!!!!!\n"); #endif @@ -1927,6 +2009,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; gmi->glumbexf = 0.0; gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -1958,6 +2041,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; gmi->glumbexf = 0.0; gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -1982,6 +2066,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; gmi->glumbexf = 0.0; gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -2008,6 +2093,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; /* No compression at black end */ gmi->glumbexf = 0.0; /* No expansion at black end */ gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -2033,6 +2119,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */ gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */ gmi->glumknf = 0.0; /* No knee on grey mapping */ + gmi->bph = gmm_bendBP; /* extent and bend */ gmi->gamcpf = 0.0; /* No gamut compression */ gmi->gamexf = 0.0; /* No gamut expansion */ gmi->gamcknf = 0.0; /* No knee in gamut compress */ @@ -2060,6 +2147,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */ gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */ gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */ + gmi->bph = gmm_bendBP; /* extent and bend */ gmi->gamcpf = 1.0; /* Full gamut compression */ gmi->gamexf = 0.0; /* No gamut expansion */ gmi->gamcknf = 0.8; /* High Sigma knee in gamut compress */ @@ -2073,7 +2161,7 @@ char *as /* Alias string selector, NULL for none */ /* Don't align neutral axes, but perceptually compress out of gamut */ /* and map appearance space Jab to Jab. */ - no = 5; + no = 6; gmi->as = "pa"; gmi->desc = "pa - Perceptual Apperance "; gmi->icci = icPerceptual; @@ -2085,6 +2173,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */ gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */ gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */ + gmi->bph = gmm_bendBP; /* extent and bend */ gmi->gamcpf = 1.0; /* Full gamut compression */ gmi->gamexf = 0.0; /* No gamut expansion */ gmi->gamcknf = 0.8; /* High Sigma knee in gamut compress */ @@ -2098,7 +2187,7 @@ char *as /* Alias string selector, NULL for none */ /* Align neutral axes and perceptually map white and black points, */ /* perceptually compress and expand to match gamuts and map Jab to Jab. */ - no = 6; + no = 7; gmi->as = "ms"; gmi->desc = "ms - Saturation"; gmi->icci = icSaturation; @@ -2110,6 +2199,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */ gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */ gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */ + gmi->bph = gmm_bendBP; /* extent and bend */ gmi->gamcpf = 1.0; /* Full gamut compression */ gmi->gamexf = 1.0; /* Full gamut expansion */ gmi->gamcknf = 1.0; /* High Sigma knee in gamut compress/expand */ @@ -2123,7 +2213,7 @@ char *as /* Alias string selector, NULL for none */ || (as != NULL && stricmp(as,"s") == 0)) { /* Same as "ms" but enhance saturation */ - no = 7; + no = 8; gmi->as = "s"; gmi->desc = " s - Enhanced Saturation [ICC Saturation]"; gmi->icci = icSaturation; @@ -2135,6 +2225,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */ gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */ gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */ + gmi->bph = gmm_bendBP; /* extent and bend */ gmi->gamcpf = 1.0; /* Full gamut compression */ gmi->gamexf = 1.0; /* Full gamut expansion */ gmi->gamcknf = 1.0; /* High sigma knee in gamut compress */ @@ -2147,11 +2238,11 @@ char *as /* Alias string selector, NULL for none */ || (as != NULL && stricmp(as,"al") == 0)) { /* Map absolute L*a*b* to L*a*b* and clip out of gamut */ - no = 8; + no = 9; gmi->as = "al"; gmi->desc = "al - Absolute Colorimetric (Lab)"; gmi->icci = icAbsoluteColorimetric; - gmi->usecas = 0x0; /* Don't use appearance space, use L*a*b* */ + gmi->usecas = 0x1; /* Don't use appearance space, use abs. L*a*b* */ gmi->usemap = 0; /* Don't use gamut mapping */ gmi->greymf = 0.0; gmi->glumwcpf = 0.0; @@ -2159,6 +2250,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; gmi->glumbexf = 0.0; gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -2172,11 +2264,11 @@ char *as /* Alias string selector, NULL for none */ /* Align neutral axes and linearly map white point, then */ /* map L*a*b* to L*a*b* and clip out of gamut */ - no = 3; + no = 10; gmi->as = "rl"; - gmi->desc = "rl - White Point Matched Appearance (Lab)"; + gmi->desc = "rl - White Point Matched Colorimetric (Lab)"; gmi->icci = icRelativeColorimetric; - gmi->usecas = 0x0; /* Don't use appearance space, use L*a*b* */ + gmi->usecas = 0x0; /* Don't use appearance space, use relative L*a*b* */ gmi->usemap = 1; /* Use gamut mapping */ gmi->greymf = 1.0; /* And linearly map white point */ gmi->glumwcpf = 1.0; @@ -2184,6 +2276,7 @@ char *as /* Alias string selector, NULL for none */ gmi->glumbcpf = 0.0; gmi->glumbexf = 0.0; gmi->glumknf = 0.0; + gmi->bph = gmm_noBPadpt; /* No BP adapation */ gmi->gamcpf = 0.0; gmi->gamexf = 0.0; gmi->gamcknf = 0.0; @@ -2210,10 +2303,12 @@ icxGMappingIntent *gmi /* Gamut Mapping parameters to return */ printf(" Closest ICC intent = '%s'\n",icm2str(icmRenderingIntent,gmi->icci)); if ((gmi->usecas & 0xff) == 0) - printf(" Not using Color Apperance Space\n"); + printf(" Not using Color Apperance Space - using L*a*b*\n"); else if ((gmi->usecas & 0xff) == 1) - printf(" Using Color Apperance Space\n"); + printf(" Not using Color Apperance Space - using Absoute L*a*b*\n"); else if ((gmi->usecas & 0xff) == 2) + printf(" Using Color Apperance Space\n"); + else if ((gmi->usecas & 0xff) == 3) printf(" Using Absolute Color Apperance Space\n"); if ((gmi->usecas & 0x100) != 0) @@ -2229,6 +2324,13 @@ icxGMappingIntent *gmi /* Gamut Mapping parameters to return */ printf(" Grey axis black compression factor %f\n", gmi->glumbcpf); printf(" Grey axis black expansion factor %f\n", gmi->glumbexf); printf(" Grey axis knee factor %f\n", gmi->glumknf); + printf(" Black point algorithm: "); + switch(gmi->bph) { + case gmm_clipBP: printf("Neutral axis no-adapt extend and clip\n"); break; + case gmm_BPadpt: printf("Neutral axis fully adapt\n"); break; + case gmm_bendBP: printf("Neutral axis no-adapt extend and bend\n"); break; + case gmm_noBPadpt: printf("Neutral axis no-adapt\n"); break; + } printf(" Gamut compression factor %f\n", gmi->gamcpf); printf(" Gamut expansion factor %f\n", gmi->gamexf); printf(" Gamut compression knee factor %f\n", gmi->gamcknf); @@ -3581,6 +3683,164 @@ void icxdpdiMulBy3x3Parm( out[2] = ov[2]; } +/* ------------------------------------------- */ +/* BT.1886 support */ + +/* Compute technical gamma from effective gamma in BT.1886 style */ + +/* Info for optimization */ +typedef struct { + double thyr; /* 50% input target */ + double roo; /* 0% input target */ +} gam_fits; + +/* gamma + input offset function handed to powell() */ +static double gam_fit(void *dd, double *v) { + gam_fits *gf = (gam_fits *)dd; + double gamma = v[0]; + double a, b; + double rv = 0.0; + double tt; + + if (gamma < 0.0) { + rv += 100.0 * -gamma; + gamma = 1e-4; + } + + tt = pow(gf->roo, 1.0/gamma); + b = tt/(1.0 - tt); /* Offset */ + a = pow(1.0 - tt, gamma); /* Gain */ + + tt = a * pow((0.5 + b), gamma); + tt = tt - gf->thyr; + rv += tt * tt; + + return rv; +} + +/* Given the effective gamma and the output offset Y, */ +/* return the technical gamma needed for the correct 50% response. */ +double xicc_tech_gamma( + double egamma, /* effective gamma needed */ + double off /* Output offset required */ +) { + gam_fits gf; + double op[1], sa[1], rv; + + if (off <= 0.0) { + return egamma; + } + + gf.thyr = pow(0.5, egamma); /* Advetised 50% target */ + gf.roo = off; + + op[0] = egamma; + sa[0] = 0.1; + + if (powell(&rv, 1, op, sa, 1e-6, 500, gam_fit, (void *)&gf, NULL, NULL) != 0) + warning("Computing effective gamma and input offset is inaccurate"); + + return op[0]; +} + + +/* Set the bt1886_info to a default do nothing state */ +void bt1886_setnop(bt1886_info *p) { + p->ingo = 0.0; + p->outsc = 1.0; + p->outL = 0.0; + p->tab[0] = 0.0; + p->tab[1] = 0.0; +} + +/* Setup the bt1886_info for the given target */ +void bt1886_setup(bt1886_info *p, double *XYZbp, double gamma) { + double Lab[3], bkipow; + p->gamma = gamma; + + icmXYZ2Lab(&icmD50, Lab, XYZbp); + + p->outL = Lab[0]; /* For bp blend */ + p->tab[0] = Lab[1]; /* a* b* correction needed */ + p->tab[1] = Lab[2]; + + bkipow = pow(XYZbp[1], 1.0/p->gamma); + p->ingo = bkipow/(1.0 - bkipow); /* non-linear Y that makes out black point */ + p->outsc = pow(1.0 - bkipow, p->gamma); /* Scale to restore 1 -> 1 */ +} + +/* Apply BT.1886 black offset and gamma curve to the XYZ out of the input profile. */ +/* Do this in the colorspace defined by the input profile matrix lookup, */ +/* so it will be relative XYZ. We assume that BT.1886 does a Rec709 to gamma */ +/* viewing adjustment, irrespective of the source profile transfer curve. */ +void bt1886_apply(bt1886_info *p, icmLuMatrix *lu, double *out, double *in) { + int j; + double vv; + +#ifdef DEBUG + printf("bt1886 XYZ in %f %f %f\n", in[0],in[1],in[2]); +#endif + + lu->bwd_matrix(lu, out, in); + +#ifdef DEBUG + printf("bt1886 RGB in %f %f %f\n", out[0],out[1],out[2]); +#endif + + for (j = 0; j < 3; j++) { + vv = out[j]; + + /* Convert linear light to Rec709 transfer curve */ + if (vv < 0.018) + vv = 4.5 * vv; + else + vv = 1.099 * pow(vv, 0.45) - 0.099; + + /* Apply input offset & re-scale, and then gamma of 2.4/custom gamma */ + vv = vv + p->ingo; + + if (vv > 0.0) + vv = p->outsc * pow(vv, p->gamma); + + out[j] = vv; + } + + lu->fwd_matrix(lu, out, out); + +#ifdef DEBUG + printf("bt1886 RGB bt.1886 %f %f %f\n", out[0],out[1],out[2]); +#endif + + icmXYZ2Lab(&icmD50, out, out); + +#ifdef DEBUG + printf("bt1886 Lab after Y adj. %f %f %f\n", out[0],out[1],out[2]); +#endif + + /* Blend ab to required black point offset p->tab[] as L approaches black. */ + vv = (out[0] - p->outL)/(100.0 - p->outL); /* 0 at bp, 1 at wp */ + vv = 1.0 - vv; + + if (vv < 0.0) + vv = 0.0; + else if (vv > 1.0) + vv = 1.0; + vv = pow(vv, 40.0); + out[1] += vv * p->tab[0]; + out[2] += vv * p->tab[1]; + +#ifdef DEBUG + printf("bt1886 Lab after wp adj. %f %f %f\n", out[0],out[1],out[2]); +#endif + + icmLab2XYZ(&icmD50, out, out); + +#ifdef DEBUG + printf("bt1886 XYZ out %f %f %f\n", out[0],out[1],out[2]); +#endif +} + +/* - - - - - - - - - - */ #undef stricmp |