| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| // This file is available under and governed by the GNU General Public |
| // License version 2 only, as published by the Free Software Foundation. |
| // However, the following notice accompanied the original version of this |
| // file: |
| // |
| // |
| // Little cms |
| // Copyright (C) 1998-2007 Marti Maria |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining |
| // a copy of this software and associated documentation files (the "Software"), |
| // to deal in the Software without restriction, including without limitation |
| // the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| // and/or sell copies of the Software, and to permit persons to whom the Software |
| // is furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO |
| // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| |
| #include "lcms.h" |
| |
| |
| // Virtual (built-in) profiles |
| // ----------------------------------------------------------------------------------- |
| |
| |
| // This function creates a profile based on White point, primaries and |
| // transfer functions. |
| |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint, |
| LPcmsCIExyYTRIPLE Primaries, |
| LPGAMMATABLE TransferFunction[3]) |
| { |
| cmsHPROFILE hICC; |
| cmsCIEXYZ tmp; |
| MAT3 MColorants; |
| cmsCIEXYZTRIPLE Colorants; |
| cmsCIExyY MaxWhite; |
| |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) // can't allocate |
| return NULL; |
| |
| |
| cmsSetDeviceClass(hICC, icSigDisplayClass); |
| cmsSetColorSpace(hICC, icSigRgbData); |
| cmsSetPCS(hICC, icSigXYZData); |
| cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); |
| |
| |
| // Implement profile using following tags: |
| // |
| // 1 icSigProfileDescriptionTag |
| // 2 icSigMediaWhitePointTag |
| // 3 icSigRedColorantTag |
| // 4 icSigGreenColorantTag |
| // 5 icSigBlueColorantTag |
| // 6 icSigRedTRCTag |
| // 7 icSigGreenTRCTag |
| // 8 icSigBlueTRCTag |
| |
| // This conforms a standard RGB DisplayProfile as says ICC, and then I add |
| |
| // 9 icSigChromaticityTag |
| |
| // As addendum II |
| |
| |
| // Fill-in the tags |
| |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in"); |
| |
| |
| if (WhitePoint) { |
| |
| cmsxyY2XYZ(&tmp, WhitePoint); |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp); |
| } |
| |
| if (WhitePoint && Primaries) { |
| |
| MaxWhite.x = WhitePoint -> x; |
| MaxWhite.y = WhitePoint -> y; |
| MaxWhite.Y = 1.0; |
| |
| if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) |
| { |
| cmsCloseProfile(hICC); |
| return NULL; |
| } |
| |
| cmsAdaptMatrixToD50(&MColorants, &MaxWhite); |
| |
| Colorants.Red.X = MColorants.v[0].n[0]; |
| Colorants.Red.Y = MColorants.v[1].n[0]; |
| Colorants.Red.Z = MColorants.v[2].n[0]; |
| |
| Colorants.Green.X = MColorants.v[0].n[1]; |
| Colorants.Green.Y = MColorants.v[1].n[1]; |
| Colorants.Green.Z = MColorants.v[2].n[1]; |
| |
| Colorants.Blue.X = MColorants.v[0].n[2]; |
| Colorants.Blue.Y = MColorants.v[1].n[2]; |
| Colorants.Blue.Z = MColorants.v[2].n[2]; |
| |
| cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red); |
| cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue); |
| cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green); |
| } |
| |
| |
| if (TransferFunction) { |
| |
| // In case of gamma, we must dup' the table pointer |
| |
| cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]); |
| cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]); |
| cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]); |
| } |
| |
| if (Primaries) { |
| cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries); |
| } |
| |
| return hICC; |
| } |
| |
| |
| |
| // This function creates a profile based on White point and transfer function. |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint, |
| LPGAMMATABLE TransferFunction) |
| { |
| cmsHPROFILE hICC; |
| cmsCIEXYZ tmp; |
| |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) // can't allocate |
| return NULL; |
| |
| |
| cmsSetDeviceClass(hICC, icSigDisplayClass); |
| cmsSetColorSpace(hICC, icSigGrayData); |
| cmsSetPCS(hICC, icSigXYZData); |
| cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); |
| |
| |
| |
| // Implement profile using following tags: |
| // |
| // 1 icSigProfileDescriptionTag |
| // 2 icSigMediaWhitePointTag |
| // 6 icSigGrayTRCTag |
| |
| // This conforms a standard Gray DisplayProfile |
| |
| // Fill-in the tags |
| |
| |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms gray virtual profile"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "gray built-in"); |
| |
| |
| if (WhitePoint) { |
| |
| cmsxyY2XYZ(&tmp, WhitePoint); |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp); |
| } |
| |
| |
| if (TransferFunction) { |
| |
| // In case of gamma, we must dup' the table pointer |
| |
| cmsAddTag(hICC, icSigGrayTRCTag, (LPVOID) TransferFunction); |
| } |
| |
| return hICC; |
| |
| } |
| |
| |
| static |
| int IsPCS(icColorSpaceSignature ColorSpace) |
| { |
| return (ColorSpace == icSigXYZData || |
| ColorSpace == icSigLabData); |
| } |
| |
| static |
| void FixColorSpaces(cmsHPROFILE hProfile, |
| icColorSpaceSignature ColorSpace, |
| icColorSpaceSignature PCS, |
| DWORD dwFlags) |
| { |
| |
| if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { |
| |
| if (IsPCS(ColorSpace) && IsPCS(PCS)) { |
| |
| cmsSetDeviceClass(hProfile, icSigAbstractClass); |
| cmsSetColorSpace(hProfile, ColorSpace); |
| cmsSetPCS(hProfile, PCS); |
| return; |
| } |
| |
| if (IsPCS(ColorSpace) && !IsPCS(PCS)) { |
| |
| cmsSetDeviceClass(hProfile, icSigOutputClass); |
| cmsSetPCS(hProfile, ColorSpace); |
| cmsSetColorSpace(hProfile, PCS); |
| return; |
| } |
| |
| if (IsPCS(PCS) && !IsPCS(ColorSpace)) { |
| |
| cmsSetDeviceClass(hProfile, icSigInputClass); |
| cmsSetColorSpace(hProfile, ColorSpace); |
| cmsSetPCS(hProfile, PCS); |
| return; |
| } |
| } |
| |
| cmsSetDeviceClass(hProfile, icSigLinkClass); |
| cmsSetColorSpace(hProfile, ColorSpace); |
| cmsSetPCS(hProfile, PCS); |
| |
| } |
| |
| |
| 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; |
| } |
| |
| |
| // Does convert a transform into a device link profile |
| |
| cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags) |
| { |
| cmsHPROFILE hICC; |
| _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) hTransform; |
| LPLUT Lut; |
| LCMSBOOL MustFreeLUT; |
| LPcmsNAMEDCOLORLIST InputColorant = NULL; |
| LPcmsNAMEDCOLORLIST OutputColorant = NULL; |
| |
| |
| // Check if is a named color transform |
| |
| if (cmsGetDeviceClass(v ->InputProfile) == icSigNamedColorClass) { |
| |
| return CreateNamedColorDevicelink(hTransform); |
| |
| } |
| |
| if (v ->DeviceLink) { |
| |
| Lut = v -> DeviceLink; |
| MustFreeLUT = FALSE; |
| } |
| else { |
| |
| Lut = _cmsPrecalculateDeviceLink(hTransform, dwFlags); |
| if (!Lut) return NULL; |
| MustFreeLUT = TRUE; |
| } |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) { // can't allocate |
| |
| if (MustFreeLUT) cmsFreeLUT(Lut); |
| return NULL; |
| } |
| |
| |
| FixColorSpaces(hICC, v -> EntryColorSpace, v -> ExitColorSpace, dwFlags); |
| |
| cmsSetRenderingIntent(hICC, v -> Intent); |
| |
| // Implement devicelink profile using following tags: |
| // |
| // 1 icSigProfileDescriptionTag |
| // 2 icSigMediaWhitePointTag |
| // 3 icSigAToB0Tag |
| |
| |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Device link"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Device link"); |
| |
| |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); |
| |
| if (cmsGetDeviceClass(hICC) == icSigOutputClass) { |
| |
| cmsAddTag(hICC, icSigBToA0Tag, (LPVOID) Lut); |
| } |
| else |
| cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); |
| |
| |
| |
| // Try to read input and output colorant table |
| if (cmsIsTag(v ->InputProfile, icSigColorantTableTag)) { |
| |
| // Input table can only come in this way. |
| InputColorant = cmsReadColorantTable(v ->InputProfile, icSigColorantTableTag); |
| } |
| |
| // Output is a little bit more complex. |
| if (cmsGetDeviceClass(v ->OutputProfile) == icSigLinkClass) { |
| |
| // This tag may exist only on devicelink profiles. |
| if (cmsIsTag(v ->OutputProfile, icSigColorantTableOutTag)) { |
| |
| OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableOutTag); |
| } |
| |
| } else { |
| |
| if (cmsIsTag(v ->OutputProfile, icSigColorantTableTag)) { |
| |
| OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableTag); |
| } |
| } |
| |
| if (InputColorant) |
| cmsAddTag(hICC, icSigColorantTableTag, InputColorant); |
| |
| if (OutputColorant) |
| cmsAddTag(hICC, icSigColorantTableOutTag, OutputColorant); |
| |
| |
| |
| if (MustFreeLUT) cmsFreeLUT(Lut); |
| if (InputColorant) cmsFreeNamedColorList(InputColorant); |
| if (OutputColorant) cmsFreeNamedColorList(OutputColorant); |
| |
| return hICC; |
| |
| } |
| |
| |
| // This is a devicelink operating in the target colorspace with as many transfer |
| // functions as components |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace, |
| LPGAMMATABLE TransferFunctions[]) |
| { |
| cmsHPROFILE hICC; |
| LPLUT Lut; |
| |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) // can't allocate |
| return NULL; |
| |
| |
| cmsSetDeviceClass(hICC, icSigLinkClass); |
| cmsSetColorSpace(hICC, ColorSpace); |
| cmsSetPCS(hICC, ColorSpace); |
| cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); |
| |
| |
| // Creates a LUT with prelinearization step only |
| Lut = cmsAllocLUT(); |
| if (Lut == NULL) return NULL; |
| |
| // Set up channels |
| Lut ->InputChan = Lut ->OutputChan = _cmsChannelsOf(ColorSpace); |
| |
| // Copy tables to LUT |
| cmsAllocLinearTable(Lut, TransferFunctions, 1); |
| |
| // Create tags |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms linearization device link"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "linearization built-in"); |
| |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); |
| cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); |
| |
| // LUT is already on virtual profile |
| cmsFreeLUT(Lut); |
| |
| // Ok, done |
| return hICC; |
| } |
| |
| |
| // Ink-limiting algorithm |
| // |
| // Sum = C + M + Y + K |
| // If Sum > InkLimit |
| // Ratio= 1 - (Sum - InkLimit) / (C + M + Y) |
| // if Ratio <0 |
| // Ratio=0 |
| // endif |
| // Else |
| // Ratio=1 |
| // endif |
| // |
| // C = Ratio * C |
| // M = Ratio * M |
| // Y = Ratio * Y |
| // K: Does not change |
| |
| static |
| int InkLimitingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
| { |
| double InkLimit = *(double *) Cargo; |
| double SumCMY, SumCMYK, Ratio; |
| |
| InkLimit = (InkLimit * 655.35); |
| |
| SumCMY = In[0] + In[1] + In[2]; |
| SumCMYK = SumCMY + In[3]; |
| |
| if (SumCMYK > InkLimit) { |
| |
| Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); |
| if (Ratio < 0) |
| Ratio = 0; |
| } |
| else Ratio = 1; |
| |
| Out[0] = (WORD) floor(In[0] * Ratio + 0.5); // C |
| Out[1] = (WORD) floor(In[1] * Ratio + 0.5); // M |
| Out[2] = (WORD) floor(In[2] * Ratio + 0.5); // Y |
| |
| Out[3] = In[3]; // K (untouched) |
| |
| return TRUE; |
| } |
| |
| // This is a devicelink operating in CMYK for ink-limiting |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace, |
| double Limit) |
| { |
| cmsHPROFILE hICC; |
| LPLUT Lut; |
| |
| if (ColorSpace != icSigCmykData) { |
| cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported"); |
| return NULL; |
| } |
| |
| if (Limit < 0.0 || Limit > 400) { |
| |
| cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400"); |
| if (Limit < 0) Limit = 0; |
| if (Limit > 400) Limit = 400; |
| |
| } |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) // can't allocate |
| return NULL; |
| |
| |
| cmsSetDeviceClass(hICC, icSigLinkClass); |
| cmsSetColorSpace(hICC, ColorSpace); |
| cmsSetPCS(hICC, ColorSpace); |
| cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); |
| |
| |
| // Creates a LUT with 3D grid only |
| Lut = cmsAllocLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hICC); |
| return NULL; |
| } |
| |
| |
| cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace), |
| _cmsChannelsOf(ColorSpace)); |
| |
| if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) { |
| |
| // Shouldn't reach here |
| cmsFreeLUT(Lut); |
| cmsCloseProfile(hICC); |
| return NULL; |
| } |
| |
| // Create tags |
| |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in"); |
| |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); |
| |
| cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); |
| |
| // LUT is already on virtual profile |
| cmsFreeLUT(Lut); |
| |
| // Ok, done |
| return hICC; |
| } |
| |
| |
| |
| static |
| LPLUT Create3x3EmptyLUT(void) |
| { |
| LPLUT AToB0 = cmsAllocLUT(); |
| if (AToB0 == NULL) return NULL; |
| |
| AToB0 -> InputChan = AToB0 -> OutputChan = 3; |
| return AToB0; |
| } |
| |
| |
| |
| // Creates a fake Lab identity. |
| cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint) |
| { |
| cmsHPROFILE hProfile; |
| LPLUT Lut; |
| |
| hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); |
| if (hProfile == NULL) return NULL; |
| |
| cmsSetDeviceClass(hProfile, icSigAbstractClass); |
| cmsSetColorSpace(hProfile, icSigLabData); |
| cmsSetPCS(hProfile, icSigLabData); |
| |
| cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity"); |
| cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab built-in"); |
| |
| |
| // An empty LUTs is all we need |
| Lut = Create3x3EmptyLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hProfile); |
| return NULL; |
| } |
| |
| cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); |
| cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); |
| |
| cmsFreeLUT(Lut); |
| |
| return hProfile; |
| } |
| |
| |
| // Creates a fake Lab identity. |
| cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint) |
| { |
| cmsHPROFILE hProfile; |
| LPLUT Lut; |
| |
| hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); |
| if (hProfile == NULL) return NULL; |
| |
| cmsSetProfileICCversion(hProfile, 0x4000000); |
| |
| cmsSetDeviceClass(hProfile, icSigAbstractClass); |
| cmsSetColorSpace(hProfile, icSigLabData); |
| cmsSetPCS(hProfile, icSigLabData); |
| |
| cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity v4"); |
| cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab v4 built-in"); |
| |
| |
| // An empty LUTs is all we need |
| Lut = Create3x3EmptyLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hProfile); |
| return NULL; |
| } |
| |
| Lut -> wFlags |= LUT_V4_INPUT_EMULATE_V2; |
| cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); |
| |
| Lut -> wFlags |= LUT_V4_OUTPUT_EMULATE_V2; |
| cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); |
| |
| cmsFreeLUT(Lut); |
| |
| return hProfile; |
| } |
| |
| |
| |
| // Creates a fake XYZ identity |
| cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void) |
| { |
| cmsHPROFILE hProfile; |
| LPLUT Lut; |
| |
| hProfile = cmsCreateRGBProfile(cmsD50_xyY(), NULL, NULL); |
| if (hProfile == NULL) return NULL; |
| |
| cmsSetDeviceClass(hProfile, icSigAbstractClass); |
| cmsSetColorSpace(hProfile, icSigXYZData); |
| cmsSetPCS(hProfile, icSigXYZData); |
| |
| cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms XYZ identity"); |
| cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "XYZ built-in"); |
| |
| // An empty LUTs is all we need |
| Lut = Create3x3EmptyLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hProfile); |
| return NULL; |
| } |
| |
| cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); |
| cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); |
| cmsAddTag(hProfile, icSigPreview0Tag, (LPVOID) Lut); |
| |
| cmsFreeLUT(Lut); |
| return hProfile; |
| } |
| |
| |
| |
| /* |
| |
| If RÂ’sRGB,GÂ’sRGB, BÂ’sRGB < 0.04045 |
| |
| R = RÂ’sRGB / 12.92 |
| G = GÂ’sRGB / 12.92 |
| B = BÂ’sRGB / 12.92 |
| |
| |
| |
| else if RÂ’sRGB,GÂ’sRGB, BÂ’sRGB >= 0.04045 |
| |
| R = ((RÂ’sRGB + 0.055) / 1.055)^2.4 |
| G = ((GÂ’sRGB + 0.055) / 1.055)^2.4 |
| B = ((BÂ’sRGB + 0.055) / 1.055)^2.4 |
| |
| */ |
| |
| static |
| LPGAMMATABLE Build_sRGBGamma(void) |
| { |
| double Parameters[5]; |
| |
| Parameters[0] = 2.4; |
| Parameters[1] = 1. / 1.055; |
| Parameters[2] = 0.055 / 1.055; |
| Parameters[3] = 1. / 12.92; |
| Parameters[4] = 0.04045; // d |
| |
| return cmsBuildParametricGamma(1024, 4, Parameters); |
| } |
| |
| // Create the ICC virtual profile for sRGB space |
| cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void) |
| { |
| cmsCIExyY D65; |
| cmsCIExyYTRIPLE Rec709Primaries = { |
| {0.6400, 0.3300, 1.0}, |
| {0.3000, 0.6000, 1.0}, |
| {0.1500, 0.0600, 1.0} |
| }; |
| LPGAMMATABLE Gamma22[3]; |
| cmsHPROFILE hsRGB; |
| |
| cmsWhitePointFromTemp(6504, &D65); |
| Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(); |
| |
| hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma22); |
| cmsFreeGamma(Gamma22[0]); |
| if (hsRGB == NULL) return NULL; |
| |
| |
| cmsAddTag(hsRGB, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hsRGB, icSigDeviceModelDescTag, (LPVOID) "sRGB built-in"); |
| cmsAddTag(hsRGB, icSigProfileDescriptionTag, (LPVOID) "sRGB built-in"); |
| |
| return hsRGB; |
| } |
| |
| |
| |
| typedef struct { |
| double Brightness; |
| double Contrast; |
| double Hue; |
| double Saturation; |
| cmsCIEXYZ WPsrc, WPdest; |
| |
| } BCHSWADJUSTS, *LPBCHSWADJUSTS; |
| |
| |
| static |
| int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
| { |
| cmsCIELab LabIn, LabOut; |
| cmsCIELCh LChIn, LChOut; |
| cmsCIEXYZ XYZ; |
| LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; |
| |
| |
| cmsLabEncoded2Float(&LabIn, In); |
| |
| |
| cmsLab2LCh(&LChIn, &LabIn); |
| |
| // Do some adjusts on LCh |
| |
| LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness; |
| LChOut.C = LChIn.C + bchsw -> Saturation; |
| LChOut.h = LChIn.h + bchsw -> Hue; |
| |
| |
| cmsLCh2Lab(&LabOut, &LChOut); |
| |
| // Move white point in Lab |
| |
| cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut); |
| cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ); |
| |
| // Back to encoded |
| |
| cmsFloat2LabEncoded(Out, &LabOut); |
| |
| return TRUE; |
| } |
| |
| |
| // Creates an abstract profile operating in Lab space for Brightness, |
| // contrast, Saturation and white point displacement |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, |
| double Bright, |
| double Contrast, |
| double Hue, |
| double Saturation, |
| int TempSrc, |
| int TempDest) |
| { |
| cmsHPROFILE hICC; |
| LPLUT Lut; |
| BCHSWADJUSTS bchsw; |
| cmsCIExyY WhitePnt; |
| |
| bchsw.Brightness = Bright; |
| bchsw.Contrast = Contrast; |
| bchsw.Hue = Hue; |
| bchsw.Saturation = Saturation; |
| |
| cmsWhitePointFromTemp(TempSrc, &WhitePnt); |
| cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); |
| |
| cmsWhitePointFromTemp(TempDest, &WhitePnt); |
| cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); |
| |
| hICC = _cmsCreateProfilePlaceholder(); |
| if (!hICC) // can't allocate |
| return NULL; |
| |
| |
| cmsSetDeviceClass(hICC, icSigAbstractClass); |
| cmsSetColorSpace(hICC, icSigLabData); |
| cmsSetPCS(hICC, icSigLabData); |
| |
| cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); |
| |
| |
| // Creates a LUT with 3D grid only |
| Lut = cmsAllocLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hICC); |
| return NULL; |
| } |
| |
| cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3); |
| |
| if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) { |
| |
| // Shouldn't reach here |
| cmsFreeLUT(Lut); |
| cmsCloseProfile(hICC); |
| return NULL; |
| } |
| |
| // Create tags |
| |
| cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); |
| cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile"); |
| cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in"); |
| |
| cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); |
| |
| cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); |
| |
| // LUT is already on virtual profile |
| cmsFreeLUT(Lut); |
| |
| // Ok, done |
| return hICC; |
| |
| } |
| |
| |
| // Creates a fake NULL profile. This profile return 1 channel as always 0. |
| // Is useful only for gamut checking tricks |
| |
| cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void) |
| { |
| cmsHPROFILE hProfile; |
| LPLUT Lut; |
| LPGAMMATABLE EmptyTab; |
| |
| hProfile = _cmsCreateProfilePlaceholder(); |
| if (!hProfile) // can't allocate |
| return NULL; |
| |
| cmsSetDeviceClass(hProfile, icSigOutputClass); |
| cmsSetColorSpace(hProfile, icSigGrayData); |
| cmsSetPCS(hProfile, icSigLabData); |
| |
| |
| // An empty LUTs is all we need |
| Lut = cmsAllocLUT(); |
| if (Lut == NULL) { |
| cmsCloseProfile(hProfile); |
| return NULL; |
| } |
| |
| Lut -> InputChan = 3; |
| Lut -> OutputChan = 1; |
| |
| EmptyTab = cmsAllocGamma(2); |
| EmptyTab ->GammaTable[0] = 0; |
| EmptyTab ->GammaTable[1] = 0; |
| |
| cmsAllocLinearTable(Lut, &EmptyTab, 2); |
| |
| cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); |
| |
| cmsFreeLUT(Lut); |
| cmsFreeGamma(EmptyTab); |
| |
| return hProfile; |
| } |