// Compute the conversion layer static cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[], cmsUInt32Number Intent, cmsBool BPC, cmsFloat64Number AdaptationState, cmsMAT3* m, cmsVEC3* off) { int k; // m and off are set to identity and this is detected latter on _cmsMAT3identity(m); _cmsVEC3init(off, 0, 0, 0); // If intent is abs. colorimetric, if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { cmsCIEXYZ WhitePointIn, WhitePointOut; cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); if (!ComputeAbsoluteIntent(AdaptationState, &WhitePointIn, &ChromaticAdaptationMatrixIn, &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; } else { // Rest of intents may apply BPC. if (BPC) { cmsCIEXYZ BlackPointIn, BlackPointOut; cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); // If black points are equal, then do nothing if (BlackPointIn.X != BlackPointOut.X || BlackPointIn.Y != BlackPointOut.Y || BlackPointIn.Z != BlackPointOut.Z) ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); } } // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so // we have first to convert from encoded to XYZ and then convert back to encoded. // y = Mx + Off // x = x'c // y = M x'c + Off // y = y'c; y' = y / c // y' = (Mx'c + Off) /c = Mx' + (Off / c) for (k=0; k < 3; k++) { off ->n[k] /= MAX_ENCODEABLE_XYZ; } return TRUE; }
static void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) { if (lIsAbsolute) { // For absolute colorimetric intent, encode back to relative // and generate a relative Pipeline // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) cmsCIEXYZ White; _cmsReadMediaWhitePoint(&White, hProfile); _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" "/TransformPQR [\n" "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", White.X, White.Y, White.Z); return; } _cmsIOPrintf(m,"%% Bradford Cone Space\n" "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); // No BPC if (!DoBPC) { _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" "/TransformPQR [\n" "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); } else { // BPC _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" "/TransformPQR [\n"); _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " "2 index 3 get 2 index 3 get sub mul " "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " "3 index 3 get 3 index 3 get exch sub div " "exch pop exch pop exch pop exch pop } bind\n"); _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " "2 index 4 get 2 index 4 get sub mul " "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " "3 index 4 get 3 index 4 get exch sub div " "exch pop exch pop exch pop exch pop } bind\n"); _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " "2 index 5 get 2 index 5 get sub mul " "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " "3 index 5 get 3 index 5 get exch sub div " "exch pop exch pop exch pop exch pop } bind\n]\n"); } }
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { // Zero for black point if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return FALSE; } // v4 + perceptual & saturation intents does have its own black point, and it is // well specified enough to use it. Black point tag is deprecated in V4. if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { // Matrix shaper share MRC & perceptual intents if (cmsIsMatrixShaper(hProfile)) return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; return TRUE; } #ifdef CMS_USE_PROFILE_BLACK_POINT_TAG // v2, v4 rel/abs colorimetric if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) && Intent == INTENT_RELATIVE_COLORIMETRIC) { cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; cmsCIELab Lab; // If black point is specified, then use it, BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag); if (BlackPtr != NULL) { BlackXYZ = *BlackPtr; _cmsReadMediaWhitePoint(&MediaWhite, hProfile); // Black point is absolute XYZ, so adapt to D50 to get PCS value cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); // Force a=b=0 to get rid of any chroma cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); Lab.a = Lab.b = 0; if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); if (BlackPoint != NULL) *BlackPoint = TrustedBlackPoint; return TRUE; } } #endif // That is about v2 profiles. // If output profile, discount ink-limiting and that's all if (Intent == INTENT_RELATIVE_COLORIMETRIC && (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) && (cmsGetColorSpace(hProfile) == cmsSigCmykData)) return BlackPointUsingPerceptualBlack(BlackPoint, hProfile); // Nope, compute BP using current intent. return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); }