📄 cmsgmt.c
字号:
Lab -> b < bmin || Lab->b > bmax) { cmsCIELCh LCh; double h, slope; // Falls outside a, b limits. Transports to LCh space, // and then do the clipping if (Lab -> a == 0.0) { // Is hue exactly 90? // atan will not work, so clamp here Lab -> b = Lab->b < 0 ? bmin : bmax; return; } cmsLab2LCh(&LCh, Lab); slope = Lab -> b / Lab -> a; h = LCh.h; // There are 4 zones if ((h >= 0. && h < 45.) || (h >= 315 && h <= 360.)) { // clip by amax Lab -> a = amax; Lab -> b = amax * slope; } else if (h >= 45. && h < 135) { // clip by bmax Lab -> b = bmax; Lab -> a = bmax / slope; } else if (h >= 135 && h < 225) { // clip by amin Lab -> a = amin; Lab -> b = amin * slope; } else if (h >= 225 && h < 315) { // clip by bmin Lab -> b = bmin; Lab -> a = bmin / slope; } else cmsSignalError(LCMS_ERRC_ABORTED, "Invalid angle"); }}// Several utilities -------------------------------------------------------// Translate from our colorspace to ICC representationicColorSpaceSignature LCMSEXPORT _cmsICCcolorSpace(int OurNotation){ switch (OurNotation) { case 1: case PT_GRAY: return icSigGrayData; case 2: case PT_RGB: return icSigRgbData; case PT_CMY: return icSigCmyData; case PT_CMYK: return icSigCmykData; case PT_YCbCr:return icSigYCbCrData; case PT_YUV: return icSigLuvData; case PT_XYZ: return icSigXYZData; case PT_Lab: return icSigLabData; case PT_YUVK: return icSigLuvKData; case PT_HSV: return icSigHsvData; case PT_HLS: return icSigHlsData; case PT_Yxy: return icSigYxyData; case PT_HiFi: return icSigHexachromeData; case PT_HiFi7: return icSigHeptachromeData; case PT_HiFi8: return icSigOctachromeData; case PT_HiFi9: return icSigMCH9Data; case PT_HiFi10: return icSigMCHAData; case PT_HiFi11: return icSigMCHBData; case PT_HiFi12: return icSigMCHCData; case PT_HiFi13: return icSigMCHDData; case PT_HiFi14: return icSigMCHEData; case PT_HiFi15: return icSigMCHFData; default: return icMaxEnumData; }}int LCMSEXPORT _cmsLCMScolorSpace(icColorSpaceSignature ProfileSpace){ switch (ProfileSpace) { case icSigGrayData: return PT_GRAY; case icSigRgbData: return PT_RGB; case icSigCmyData: return PT_CMY; case icSigCmykData: return PT_CMYK; case icSigYCbCrData:return PT_YCbCr; case icSigLuvData: return PT_YUV; case icSigXYZData: return PT_XYZ; case icSigLabData: return PT_Lab; case icSigLuvKData: return PT_YUVK; case icSigHsvData: return PT_HSV; case icSigHlsData: return PT_HLS; case icSigYxyData: return PT_Yxy; case icSig6colorData: case icSigHexachromeData: return PT_HiFi; case icSigHeptachromeData: case icSig7colorData: return PT_HiFi7; case icSigOctachromeData: case icSig8colorData: return PT_HiFi8; case icSigMCH9Data: case icSig9colorData: return PT_HiFi9; case icSigMCHAData: case icSig10colorData: return PT_HiFi10; case icSigMCHBData: case icSig11colorData: return PT_HiFi11; case icSigMCHCData: case icSig12colorData: return PT_HiFi12; case icSigMCHDData: case icSig13colorData: return PT_HiFi13; case icSigMCHEData: case icSig14colorData: return PT_HiFi14; case icSigMCHFData: case icSig15colorData: return PT_HiFi15; default: return icMaxEnumData; }}int LCMSEXPORT _cmsChannelsOf(icColorSpaceSignature ColorSpace){ switch (ColorSpace) { case icSigGrayData: return 1; case icSig2colorData: return 2; case icSigXYZData: case icSigLabData: case icSigLuvData: case icSigYCbCrData: case icSigYxyData: case icSigRgbData: case icSigHsvData: case icSigHlsData: case icSigCmyData: case icSig3colorData: return 3; case icSigLuvKData: case icSigCmykData: case icSig4colorData: return 4; case icSigMCH5Data: case icSig5colorData: return 5; case icSigHexachromeData: case icSig6colorData: return 6; case icSigHeptachromeData: case icSig7colorData: return 7; case icSigOctachromeData: case icSig8colorData: return 8; case icSigMCH9Data: case icSig9colorData: return 9; case icSigMCHAData: case icSig10colorData: return 10; case icSigMCHBData: case icSig11colorData: return 11; case icSigMCHCData: case icSig12colorData: return 12; case icSigMCHDData: case icSig13colorData: return 13; case icSigMCHEData: case icSig14colorData: return 14; case icSigMCHFData: case icSig15colorData: return 15; default: return 3; }}// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable // number of gridpoints that would make exact match. However, a // prelinearization of 258 entries, would map 0xFF00 on entry 257. // This is almost what we need, unfortunately, the rest of entries // should be scaled by (255*257/256) and this is not exact.// // An intermediate solution would be to use 257 entries. This does not// map 0xFF00 exactly on a node, but so close that the dE induced is// negligible. AND the rest of curve is exact.staticvoid CreateLabPrelinearization(LPGAMMATABLE LabTable[]){ int i; LabTable[0] = cmsAllocGamma(257); LabTable[1] = cmsBuildGamma(257, 1.0); LabTable[2] = cmsBuildGamma(257, 1.0); // L* uses 257 entries. Entry 256 holds 0xFFFF, so, the effective range // is 0..0xFF00. Last entry (257) is also collapsed to 0xFFFF // From 0 to 0xFF00 for (i=0; i < 256; i++) LabTable[0]->GammaTable[i] = RGB_8_TO_16(i); // Repeat last for 0xFFFF LabTable[0] ->GammaTable[256] = 0xFFFF; }// Used by gamut & softproofingtypedef struct { cmsHTRANSFORM hInput; // From whatever input color space. NULL for Lab cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back double Thereshold; // The thereshold after which is considered out of gamut } GAMUTCHAIN,FAR* LPGAMUTCHAIN;// This sampler does compute gamut boundaries by comparing original// values with a transform going back and forth. Values above ERR_THERESHOLD // of maximum are considered out of gamut.#define ERR_THERESHOLD 5 staticint GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo){ LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo; WORD Proof[MAXCHANNELS], Check[MAXCHANNELS]; WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS]; cmsCIELab LabIn1, LabOut1; cmsCIELab LabIn2, LabOut2; double dE1, dE2, ErrorRatio; // Assume in-gamut by default. dE1 = 0.; dE2 = 0; ErrorRatio = 1.0; // Any input space? I can use In[] no matter channels // because is just one pixel if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1); // converts from PCS to colorant. This always // does return in-gamut values, cmsDoTransform(t -> hForward, In, Proof, 1); // Now, do the inverse, from colorant to PCS. cmsDoTransform(t -> hReverse, Proof, Check, 1); // Try again, but this time taking Check as input cmsDoTransform(t -> hForward, Check, Proof2, 1); cmsDoTransform(t -> hReverse, Proof2, Check2, 1); // Does the transform returns out-of-gamut? if (Check[0] == 0xFFFF && Check[1] == 0xFFFF && Check[2] == 0xFFFF) Out[0] = 0xFF00; // Out of gamut! else { // Transport encoded values cmsLabEncoded2Float(&LabIn1, In); cmsLabEncoded2Float(&LabOut1, Check); // Take difference of direct value dE1 = cmsDeltaE(&LabIn1, &LabOut1); cmsLabEncoded2Float(&LabIn2, Check); cmsLabEncoded2Float(&LabOut2, Check2); // Take difference of converted value dE2 = cmsDeltaE(&LabIn2, &LabOut2); // if dE1 is small and dE2 is small, value is likely to be in gamut if (dE1 < t->Thereshold && dE2 < t->Thereshold) Out[0] = 0; else // if dE1 is small and dE2 is big, undefined. Assume in gamut if (dE1 < t->Thereshold && dE2 > t->Thereshold) Out[0] = 0; else // dE1 is big and dE2 is small, clearly out of gamut if (dE1 > t->Thereshold && dE2 < t->Thereshold) Out[0] = (WORD) _cmsQuickFloor((dE1 - t->Thereshold) + .5); else { // dE1 is big and dE2 is also big, could be due to perceptual mapping // so take error ratio if (dE2 == 0.0) ErrorRatio = dE1; else ErrorRatio = dE1 / dE2; if (ErrorRatio > t->Thereshold) Out[0] = (WORD) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); else Out[0] = 0; } } return TRUE;}// Does compute a gamut LUT going back and forth across // pcs -> relativ. colorimetric intent -> pcs// the dE obtained is then annotated on the LUT.// values truely out of gamut, are clipped to dE = 0xFFFE// and values changed are supposed to be handled by// any gamut remapping, so, are out of gamut as well.//// **WARNING: This algorithm does assume that gamut// remapping algorithms does NOT move in-gamut colors,// of course, many perceptual and saturation intents does// not work in such way, but relativ. ones should.staticLPLUT ComputeGamutWithInput(cmsHPROFILE hInput, cmsHPROFILE hProfile, int Intent){ cmsHPROFILE hLab; LPLUT Gamut; DWORD dwFormat; GAMUTCHAIN Chain; int nErrState, nChannels, nGridpoints; LPGAMMATABLE Trans[3]; icColorSpaceSignature ColorSpace; ZeroMemory(&Chain, sizeof(GAMUTCHAIN)); hLab = cmsCreateLabProfile(NULL); // Safeguard against early abortion nErrState = cmsErrorAction(LCMS_ERROR_IGNORE); // The figure of merit. On matrix-shaper profiles, should be almost zero as // the conversion is pretty exact. On LUT based profiles, different resolutions // of input and output CLUT may result in differences. if (!cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && !cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_OUTPUT)) Chain.Thereshold = 1.0; else Chain.Thereshold = ERR_THERESHOLD; ColorSpace = cmsGetColorSpace(hProfile); // If input profile specified, create a transform from such profile to Lab if (hInput != NULL) { nChannels = _cmsChannelsOf(ColorSpace); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); Chain.hInput = cmsCreateTransform(hInput, dwFormat, hLab, TYPE_Lab_16, Intent, cmsFLAGS_NOTPRECALC); } else { // Input transform=NULL (Lab) Used to compute the gamut tag // This table will take 53 points to give some accurancy, // 53 * 53 * 53 * 2 = 291K nChannels = 3; // For Lab nGridpoints = 53; Chain.hInput = NULL; dwFormat = (CHANNELS_SH(_cmsChannelsOf(ColorSpace))|BYTES_SH(2)); } // Does create the forward step Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16, hProfile, dwFormat, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC); // Does create the backwards step Chain.hReverse = cmsCreateTransform(hProfile, dwFormat, hLab, TYPE_Lab_16,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -