int cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags) { // v4 + perceptual & saturation intents does have its own black point if ((cmsGetProfileICCversion(hProfile) >= 0x4000000) && (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { return GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags); } #ifdef HONOR_BLACK_POINT_TAG // v2, v4 rel/abs colorimetric if (cmsIsTag(hProfile, icSigMediaBlackPointTag) && Intent == INTENT_RELATIVE_COLORIMETRIC) { cmsCIEXYZ BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; cmsCIELab Lab; // If black point is specified, then use it, cmsTakeMediaBlackPoint(&BlackXYZ, hProfile); cmsTakeMediaWhitePoint(&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); // Return BP as D50 relative or absolute XYZ (depends on flags) if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &TrustedBlackPoint); else *BlackPoint = TrustedBlackPoint; } #endif // If output profile, discount ink-limiting if (Intent == INTENT_RELATIVE_COLORIMETRIC && (cmsGetDeviceClass(hProfile) == icSigOutputClass) && (cmsGetColorSpace(hProfile) == icSigCmykData)) return BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags); // Nope, compute BP using current intent. return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); }
static int BlackPointUsingPerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags) { cmsHTRANSFORM hPercLab2CMYK, hRelColCMYK2Lab; cmsHPROFILE hLab; cmsCIELab LabIn, LabOut; WORD CMYK[MAXCHANNELS]; cmsCIEXYZ BlackXYZ, MediaWhite; if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return 0; } hLab = cmsCreateLabProfile(NULL); hPercLab2CMYK = cmsCreateTransform(hLab, TYPE_Lab_DBL, hProfile, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC); hRelColCMYK2Lab = cmsCreateTransform(hProfile, TYPE_CMYK_16, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC); LabIn.L = LabIn.a = LabIn.b = 0; cmsDoTransform(hPercLab2CMYK, &LabIn, CMYK, 1); cmsDoTransform(hRelColCMYK2Lab, CMYK, &LabOut, 1); if (LabOut.L > 50) LabOut.L = 50; LabOut.a = LabOut.b = 0; cmsDeleteTransform(hPercLab2CMYK); cmsDeleteTransform(hRelColCMYK2Lab); cmsCloseProfile(hLab); cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)){ cmsTakeMediaWhitePoint(&MediaWhite, hProfile); cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ); } else *BlackPoint = BlackXYZ; return 1; }
static cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) { _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; cmsHPROFILE hICC; cmsCIEXYZ WhitePoint; int i, nColors; size_t Size; LPcmsNAMEDCOLORLIST nc2; hICC = _cmsCreateProfilePlaceholder(); if (hICC == NULL) return NULL; cmsSetRenderingIntent(hICC, v -> Intent); cmsSetDeviceClass(hICC, icSigNamedColorClass); cmsSetColorSpace(hICC, v ->ExitColorSpace); cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile)); cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile); cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint); cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS"); cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link"); cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link"); nColors = cmsNamedColorCount(xform); nc2 = cmsAllocNamedColorList(nColors); Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1)); CopyMemory(nc2, v->NamedColorList, Size); nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace); for (i=0; i < nColors; i++) { cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); } cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2); cmsFreeNamedColorList(nc2); return hICC; }
// Get Perceptual black of v4 profiles. static int GetV4PerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags) { if (dwFlags & LCMS_BPFLAGS_D50_ADAPTED) { BlackPoint->X = PERCEPTUAL_BLACK_X; BlackPoint->Y = PERCEPTUAL_BLACK_Y; BlackPoint->Z = PERCEPTUAL_BLACK_Z; } else { cmsCIEXYZ D50BlackPoint, MediaWhite; cmsTakeMediaWhitePoint(&MediaWhite, hProfile); D50BlackPoint.X = PERCEPTUAL_BLACK_X; D50BlackPoint.Y = PERCEPTUAL_BLACK_Y; D50BlackPoint.Z = PERCEPTUAL_BLACK_Z; cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &D50BlackPoint); } return 1; }
static int BlackPointAsDarkerColorant(cmsHPROFILE hInput, int Intent, LPcmsCIEXYZ BlackPoint, DWORD dwFlags) { WORD *Black, *White; cmsHTRANSFORM xform; icColorSpaceSignature Space; int nChannels; DWORD dwFormat; cmsHPROFILE hLab; cmsCIELab Lab; cmsCIEXYZ BlackXYZ, MediaWhite; // If the profile does not support input direction, assume Black point 0 if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return 0; } // Try to get black by using black colorant Space = cmsGetColorSpace(hInput); if (!_cmsEndPointsBySpace(Space, &White, &Black, &nChannels)) { BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; return 0; } dwFormat = CHANNELS_SH(nChannels)|BYTES_SH(2); hLab = cmsCreateLabProfile(NULL); xform = cmsCreateTransform(hInput, dwFormat, hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC); cmsDoTransform(xform, Black, &Lab, 1); // Force it to be neutral, clip to max. L* of 50 Lab.a = Lab.b = 0; if (Lab.L > 50) Lab.L = 50; cmsCloseProfile(hLab); cmsDeleteTransform(xform); cmsLab2XYZ(NULL, &BlackXYZ, &Lab); if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { *BlackPoint = BlackXYZ; } else { if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) { cmsTakeMediaWhitePoint(&MediaWhite, hInput); cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ); } else *BlackPoint = BlackXYZ; } return 1; }