1*3ac0a46fSAndroid Build Coastguard Worker //---------------------------------------------------------------------------------
2*3ac0a46fSAndroid Build Coastguard Worker //
3*3ac0a46fSAndroid Build Coastguard Worker // Little Color Management System
4*3ac0a46fSAndroid Build Coastguard Worker // Copyright (c) 1998-2023 Marti Maria Saguer
5*3ac0a46fSAndroid Build Coastguard Worker //
6*3ac0a46fSAndroid Build Coastguard Worker // Permission is hereby granted, free of charge, to any person obtaining
7*3ac0a46fSAndroid Build Coastguard Worker // a copy of this software and associated documentation files (the "Software"),
8*3ac0a46fSAndroid Build Coastguard Worker // to deal in the Software without restriction, including without limitation
9*3ac0a46fSAndroid Build Coastguard Worker // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10*3ac0a46fSAndroid Build Coastguard Worker // and/or sell copies of the Software, and to permit persons to whom the Software
11*3ac0a46fSAndroid Build Coastguard Worker // is furnished to do so, subject to the following conditions:
12*3ac0a46fSAndroid Build Coastguard Worker //
13*3ac0a46fSAndroid Build Coastguard Worker // The above copyright notice and this permission notice shall be included in
14*3ac0a46fSAndroid Build Coastguard Worker // all copies or substantial portions of the Software.
15*3ac0a46fSAndroid Build Coastguard Worker //
16*3ac0a46fSAndroid Build Coastguard Worker // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*3ac0a46fSAndroid Build Coastguard Worker // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18*3ac0a46fSAndroid Build Coastguard Worker // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19*3ac0a46fSAndroid Build Coastguard Worker // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20*3ac0a46fSAndroid Build Coastguard Worker // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21*3ac0a46fSAndroid Build Coastguard Worker // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22*3ac0a46fSAndroid Build Coastguard Worker // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23*3ac0a46fSAndroid Build Coastguard Worker //
24*3ac0a46fSAndroid Build Coastguard Worker //---------------------------------------------------------------------------------
25*3ac0a46fSAndroid Build Coastguard Worker //
26*3ac0a46fSAndroid Build Coastguard Worker
27*3ac0a46fSAndroid Build Coastguard Worker #include "lcms2_internal.h"
28*3ac0a46fSAndroid Build Coastguard Worker
29*3ac0a46fSAndroid Build Coastguard Worker
30*3ac0a46fSAndroid Build Coastguard Worker #define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
31*3ac0a46fSAndroid Build Coastguard Worker #define cmsmax(a, b) (((a) > (b)) ? (a) : (b))
32*3ac0a46fSAndroid Build Coastguard Worker
33*3ac0a46fSAndroid Build Coastguard Worker // This file contains routines for resampling and LUT optimization, black point detection
34*3ac0a46fSAndroid Build Coastguard Worker // and black preservation.
35*3ac0a46fSAndroid Build Coastguard Worker
36*3ac0a46fSAndroid Build Coastguard Worker // Black point detection -------------------------------------------------------------------------
37*3ac0a46fSAndroid Build Coastguard Worker
38*3ac0a46fSAndroid Build Coastguard Worker
39*3ac0a46fSAndroid Build Coastguard Worker // PCS -> PCS round trip transform, always uses relative intent on the device -> pcs
40*3ac0a46fSAndroid Build Coastguard Worker static
CreateRoundtripXForm(cmsHPROFILE hProfile,cmsUInt32Number nIntent)41*3ac0a46fSAndroid Build Coastguard Worker cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
42*3ac0a46fSAndroid Build Coastguard Worker {
43*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID = cmsGetProfileContextID(hProfile);
44*3ac0a46fSAndroid Build Coastguard Worker cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
45*3ac0a46fSAndroid Build Coastguard Worker cmsHTRANSFORM xform;
46*3ac0a46fSAndroid Build Coastguard Worker cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
47*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
48*3ac0a46fSAndroid Build Coastguard Worker cmsHPROFILE hProfiles[4];
49*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number Intents[4];
50*3ac0a46fSAndroid Build Coastguard Worker
51*3ac0a46fSAndroid Build Coastguard Worker hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab;
52*3ac0a46fSAndroid Build Coastguard Worker Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC;
53*3ac0a46fSAndroid Build Coastguard Worker
54*3ac0a46fSAndroid Build Coastguard Worker xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents,
55*3ac0a46fSAndroid Build Coastguard Worker States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
56*3ac0a46fSAndroid Build Coastguard Worker
57*3ac0a46fSAndroid Build Coastguard Worker cmsCloseProfile(hLab);
58*3ac0a46fSAndroid Build Coastguard Worker return xform;
59*3ac0a46fSAndroid Build Coastguard Worker }
60*3ac0a46fSAndroid Build Coastguard Worker
61*3ac0a46fSAndroid Build Coastguard Worker // Use darker colorants to obtain black point. This works in the relative colorimetric intent and
62*3ac0a46fSAndroid Build Coastguard Worker // assumes more ink results in darker colors. No ink limit is assumed.
63*3ac0a46fSAndroid Build Coastguard Worker static
BlackPointAsDarkerColorant(cmsHPROFILE hInput,cmsUInt32Number Intent,cmsCIEXYZ * BlackPoint,cmsUInt32Number dwFlags)64*3ac0a46fSAndroid Build Coastguard Worker cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
65*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number Intent,
66*3ac0a46fSAndroid Build Coastguard Worker cmsCIEXYZ* BlackPoint,
67*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number dwFlags)
68*3ac0a46fSAndroid Build Coastguard Worker {
69*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number *Black;
70*3ac0a46fSAndroid Build Coastguard Worker cmsHTRANSFORM xform;
71*3ac0a46fSAndroid Build Coastguard Worker cmsColorSpaceSignature Space;
72*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nChannels;
73*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number dwFormat;
74*3ac0a46fSAndroid Build Coastguard Worker cmsHPROFILE hLab;
75*3ac0a46fSAndroid Build Coastguard Worker cmsCIELab Lab;
76*3ac0a46fSAndroid Build Coastguard Worker cmsCIEXYZ BlackXYZ;
77*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID = cmsGetProfileContextID(hInput);
78*3ac0a46fSAndroid Build Coastguard Worker
79*3ac0a46fSAndroid Build Coastguard Worker // If the profile does not support input direction, assume Black point 0
80*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {
81*3ac0a46fSAndroid Build Coastguard Worker
82*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
83*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
84*3ac0a46fSAndroid Build Coastguard Worker }
85*3ac0a46fSAndroid Build Coastguard Worker
86*3ac0a46fSAndroid Build Coastguard Worker // Create a formatter which has n channels and no floating point
87*3ac0a46fSAndroid Build Coastguard Worker dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE);
88*3ac0a46fSAndroid Build Coastguard Worker
89*3ac0a46fSAndroid Build Coastguard Worker // Try to get black by using black colorant
90*3ac0a46fSAndroid Build Coastguard Worker Space = cmsGetColorSpace(hInput);
91*3ac0a46fSAndroid Build Coastguard Worker
92*3ac0a46fSAndroid Build Coastguard Worker // This function returns darker colorant in 16 bits for several spaces
93*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) {
94*3ac0a46fSAndroid Build Coastguard Worker
95*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
96*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
97*3ac0a46fSAndroid Build Coastguard Worker }
98*3ac0a46fSAndroid Build Coastguard Worker
99*3ac0a46fSAndroid Build Coastguard Worker if (nChannels != T_CHANNELS(dwFormat)) {
100*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
101*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
102*3ac0a46fSAndroid Build Coastguard Worker }
103*3ac0a46fSAndroid Build Coastguard Worker
104*3ac0a46fSAndroid Build Coastguard Worker // Lab will be used as the output space, but lab2 will avoid recursion
105*3ac0a46fSAndroid Build Coastguard Worker hLab = cmsCreateLab2ProfileTHR(ContextID, NULL);
106*3ac0a46fSAndroid Build Coastguard Worker if (hLab == NULL) {
107*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
108*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
109*3ac0a46fSAndroid Build Coastguard Worker }
110*3ac0a46fSAndroid Build Coastguard Worker
111*3ac0a46fSAndroid Build Coastguard Worker // Create the transform
112*3ac0a46fSAndroid Build Coastguard Worker xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat,
113*3ac0a46fSAndroid Build Coastguard Worker hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
114*3ac0a46fSAndroid Build Coastguard Worker cmsCloseProfile(hLab);
115*3ac0a46fSAndroid Build Coastguard Worker
116*3ac0a46fSAndroid Build Coastguard Worker if (xform == NULL) {
117*3ac0a46fSAndroid Build Coastguard Worker
118*3ac0a46fSAndroid Build Coastguard Worker // Something went wrong. Get rid of open resources and return zero as black
119*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
120*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
121*3ac0a46fSAndroid Build Coastguard Worker }
122*3ac0a46fSAndroid Build Coastguard Worker
123*3ac0a46fSAndroid Build Coastguard Worker // Convert black to Lab
124*3ac0a46fSAndroid Build Coastguard Worker cmsDoTransform(xform, Black, &Lab, 1);
125*3ac0a46fSAndroid Build Coastguard Worker
126*3ac0a46fSAndroid Build Coastguard Worker // Force it to be neutral, check for inconsistences
127*3ac0a46fSAndroid Build Coastguard Worker Lab.a = Lab.b = 0;
128*3ac0a46fSAndroid Build Coastguard Worker if (Lab.L > 50 || Lab.L < 0) Lab.L = 0;
129*3ac0a46fSAndroid Build Coastguard Worker
130*3ac0a46fSAndroid Build Coastguard Worker // Free the resources
131*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(xform);
132*3ac0a46fSAndroid Build Coastguard Worker
133*3ac0a46fSAndroid Build Coastguard Worker // Convert from Lab (which is now clipped) to XYZ.
134*3ac0a46fSAndroid Build Coastguard Worker cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
135*3ac0a46fSAndroid Build Coastguard Worker
136*3ac0a46fSAndroid Build Coastguard Worker if (BlackPoint != NULL)
137*3ac0a46fSAndroid Build Coastguard Worker *BlackPoint = BlackXYZ;
138*3ac0a46fSAndroid Build Coastguard Worker
139*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
140*3ac0a46fSAndroid Build Coastguard Worker
141*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(dwFlags);
142*3ac0a46fSAndroid Build Coastguard Worker }
143*3ac0a46fSAndroid Build Coastguard Worker
144*3ac0a46fSAndroid Build Coastguard Worker // Get a black point of output CMYK profile, discounting any ink-limiting embedded
145*3ac0a46fSAndroid Build Coastguard Worker // in the profile. For doing that, we use perceptual intent in input direction:
146*3ac0a46fSAndroid Build Coastguard Worker // Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
147*3ac0a46fSAndroid Build Coastguard Worker static
BlackPointUsingPerceptualBlack(cmsCIEXYZ * BlackPoint,cmsHPROFILE hProfile)148*3ac0a46fSAndroid Build Coastguard Worker cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
149*3ac0a46fSAndroid Build Coastguard Worker {
150*3ac0a46fSAndroid Build Coastguard Worker cmsHTRANSFORM hRoundTrip;
151*3ac0a46fSAndroid Build Coastguard Worker cmsCIELab LabIn, LabOut;
152*3ac0a46fSAndroid Build Coastguard Worker cmsCIEXYZ BlackXYZ;
153*3ac0a46fSAndroid Build Coastguard Worker
154*3ac0a46fSAndroid Build Coastguard Worker // Is the intent supported by the profile?
155*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
156*3ac0a46fSAndroid Build Coastguard Worker
157*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
158*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
159*3ac0a46fSAndroid Build Coastguard Worker }
160*3ac0a46fSAndroid Build Coastguard Worker
161*3ac0a46fSAndroid Build Coastguard Worker hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL);
162*3ac0a46fSAndroid Build Coastguard Worker if (hRoundTrip == NULL) {
163*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
164*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
165*3ac0a46fSAndroid Build Coastguard Worker }
166*3ac0a46fSAndroid Build Coastguard Worker
167*3ac0a46fSAndroid Build Coastguard Worker LabIn.L = LabIn.a = LabIn.b = 0;
168*3ac0a46fSAndroid Build Coastguard Worker cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1);
169*3ac0a46fSAndroid Build Coastguard Worker
170*3ac0a46fSAndroid Build Coastguard Worker // Clip Lab to reasonable limits
171*3ac0a46fSAndroid Build Coastguard Worker if (LabOut.L > 50) LabOut.L = 50;
172*3ac0a46fSAndroid Build Coastguard Worker LabOut.a = LabOut.b = 0;
173*3ac0a46fSAndroid Build Coastguard Worker
174*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(hRoundTrip);
175*3ac0a46fSAndroid Build Coastguard Worker
176*3ac0a46fSAndroid Build Coastguard Worker // Convert it to XYZ
177*3ac0a46fSAndroid Build Coastguard Worker cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
178*3ac0a46fSAndroid Build Coastguard Worker
179*3ac0a46fSAndroid Build Coastguard Worker if (BlackPoint != NULL)
180*3ac0a46fSAndroid Build Coastguard Worker *BlackPoint = BlackXYZ;
181*3ac0a46fSAndroid Build Coastguard Worker
182*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
183*3ac0a46fSAndroid Build Coastguard Worker }
184*3ac0a46fSAndroid Build Coastguard Worker
185*3ac0a46fSAndroid Build Coastguard Worker // This function shouldn't exist at all -- there is such quantity of broken
186*3ac0a46fSAndroid Build Coastguard Worker // profiles on black point tag, that we must somehow fix chromaticity to
187*3ac0a46fSAndroid Build Coastguard Worker // avoid huge tint when doing Black point compensation. This function does
188*3ac0a46fSAndroid Build Coastguard Worker // just that. There is a special flag for using black point tag, but turned
189*3ac0a46fSAndroid Build Coastguard Worker // off by default because it is bogus on most profiles. The detection algorithm
190*3ac0a46fSAndroid Build Coastguard Worker // involves to turn BP to neutral and to use only L component.
cmsDetectBlackPoint(cmsCIEXYZ * BlackPoint,cmsHPROFILE hProfile,cmsUInt32Number Intent,cmsUInt32Number dwFlags)191*3ac0a46fSAndroid Build Coastguard Worker cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
192*3ac0a46fSAndroid Build Coastguard Worker {
193*3ac0a46fSAndroid Build Coastguard Worker cmsProfileClassSignature devClass;
194*3ac0a46fSAndroid Build Coastguard Worker
195*3ac0a46fSAndroid Build Coastguard Worker // Make sure the device class is adequate
196*3ac0a46fSAndroid Build Coastguard Worker devClass = cmsGetDeviceClass(hProfile);
197*3ac0a46fSAndroid Build Coastguard Worker if (devClass == cmsSigLinkClass ||
198*3ac0a46fSAndroid Build Coastguard Worker devClass == cmsSigAbstractClass ||
199*3ac0a46fSAndroid Build Coastguard Worker devClass == cmsSigNamedColorClass) {
200*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
201*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
202*3ac0a46fSAndroid Build Coastguard Worker }
203*3ac0a46fSAndroid Build Coastguard Worker
204*3ac0a46fSAndroid Build Coastguard Worker // Make sure intent is adequate
205*3ac0a46fSAndroid Build Coastguard Worker if (Intent != INTENT_PERCEPTUAL &&
206*3ac0a46fSAndroid Build Coastguard Worker Intent != INTENT_RELATIVE_COLORIMETRIC &&
207*3ac0a46fSAndroid Build Coastguard Worker Intent != INTENT_SATURATION) {
208*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
209*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
210*3ac0a46fSAndroid Build Coastguard Worker }
211*3ac0a46fSAndroid Build Coastguard Worker
212*3ac0a46fSAndroid Build Coastguard Worker // v4 + perceptual & saturation intents does have its own black point, and it is
213*3ac0a46fSAndroid Build Coastguard Worker // well specified enough to use it. Black point tag is deprecated in V4.
214*3ac0a46fSAndroid Build Coastguard Worker if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
215*3ac0a46fSAndroid Build Coastguard Worker (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
216*3ac0a46fSAndroid Build Coastguard Worker
217*3ac0a46fSAndroid Build Coastguard Worker // Matrix shaper share MRC & perceptual intents
218*3ac0a46fSAndroid Build Coastguard Worker if (cmsIsMatrixShaper(hProfile))
219*3ac0a46fSAndroid Build Coastguard Worker return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
220*3ac0a46fSAndroid Build Coastguard Worker
221*3ac0a46fSAndroid Build Coastguard Worker // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
222*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
223*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
224*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
225*3ac0a46fSAndroid Build Coastguard Worker
226*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
227*3ac0a46fSAndroid Build Coastguard Worker }
228*3ac0a46fSAndroid Build Coastguard Worker
229*3ac0a46fSAndroid Build Coastguard Worker
230*3ac0a46fSAndroid Build Coastguard Worker #ifdef CMS_USE_PROFILE_BLACK_POINT_TAG
231*3ac0a46fSAndroid Build Coastguard Worker
232*3ac0a46fSAndroid Build Coastguard Worker // v2, v4 rel/abs colorimetric
233*3ac0a46fSAndroid Build Coastguard Worker if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) &&
234*3ac0a46fSAndroid Build Coastguard Worker Intent == INTENT_RELATIVE_COLORIMETRIC) {
235*3ac0a46fSAndroid Build Coastguard Worker
236*3ac0a46fSAndroid Build Coastguard Worker cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
237*3ac0a46fSAndroid Build Coastguard Worker cmsCIELab Lab;
238*3ac0a46fSAndroid Build Coastguard Worker
239*3ac0a46fSAndroid Build Coastguard Worker // If black point is specified, then use it,
240*3ac0a46fSAndroid Build Coastguard Worker
241*3ac0a46fSAndroid Build Coastguard Worker BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag);
242*3ac0a46fSAndroid Build Coastguard Worker if (BlackPtr != NULL) {
243*3ac0a46fSAndroid Build Coastguard Worker
244*3ac0a46fSAndroid Build Coastguard Worker BlackXYZ = *BlackPtr;
245*3ac0a46fSAndroid Build Coastguard Worker _cmsReadMediaWhitePoint(&MediaWhite, hProfile);
246*3ac0a46fSAndroid Build Coastguard Worker
247*3ac0a46fSAndroid Build Coastguard Worker // Black point is absolute XYZ, so adapt to D50 to get PCS value
248*3ac0a46fSAndroid Build Coastguard Worker cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ);
249*3ac0a46fSAndroid Build Coastguard Worker
250*3ac0a46fSAndroid Build Coastguard Worker // Force a=b=0 to get rid of any chroma
251*3ac0a46fSAndroid Build Coastguard Worker cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint);
252*3ac0a46fSAndroid Build Coastguard Worker Lab.a = Lab.b = 0;
253*3ac0a46fSAndroid Build Coastguard Worker if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50
254*3ac0a46fSAndroid Build Coastguard Worker cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab);
255*3ac0a46fSAndroid Build Coastguard Worker
256*3ac0a46fSAndroid Build Coastguard Worker if (BlackPoint != NULL)
257*3ac0a46fSAndroid Build Coastguard Worker *BlackPoint = TrustedBlackPoint;
258*3ac0a46fSAndroid Build Coastguard Worker
259*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
260*3ac0a46fSAndroid Build Coastguard Worker }
261*3ac0a46fSAndroid Build Coastguard Worker }
262*3ac0a46fSAndroid Build Coastguard Worker #endif
263*3ac0a46fSAndroid Build Coastguard Worker
264*3ac0a46fSAndroid Build Coastguard Worker // That is about v2 profiles.
265*3ac0a46fSAndroid Build Coastguard Worker
266*3ac0a46fSAndroid Build Coastguard Worker // If output profile, discount ink-limiting and that's all
267*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
268*3ac0a46fSAndroid Build Coastguard Worker (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) &&
269*3ac0a46fSAndroid Build Coastguard Worker (cmsGetColorSpace(hProfile) == cmsSigCmykData))
270*3ac0a46fSAndroid Build Coastguard Worker return BlackPointUsingPerceptualBlack(BlackPoint, hProfile);
271*3ac0a46fSAndroid Build Coastguard Worker
272*3ac0a46fSAndroid Build Coastguard Worker // Nope, compute BP using current intent.
273*3ac0a46fSAndroid Build Coastguard Worker return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
274*3ac0a46fSAndroid Build Coastguard Worker }
275*3ac0a46fSAndroid Build Coastguard Worker
276*3ac0a46fSAndroid Build Coastguard Worker
277*3ac0a46fSAndroid Build Coastguard Worker
278*3ac0a46fSAndroid Build Coastguard Worker // ---------------------------------------------------------------------------------------------------------
279*3ac0a46fSAndroid Build Coastguard Worker
280*3ac0a46fSAndroid Build Coastguard Worker // Least Squares Fit of a Quadratic Curve to Data
281*3ac0a46fSAndroid Build Coastguard Worker // http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html
282*3ac0a46fSAndroid Build Coastguard Worker
283*3ac0a46fSAndroid Build Coastguard Worker static
RootOfLeastSquaresFitQuadraticCurve(int n,cmsFloat64Number x[],cmsFloat64Number y[])284*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[])
285*3ac0a46fSAndroid Build Coastguard Worker {
286*3ac0a46fSAndroid Build Coastguard Worker double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0;
287*3ac0a46fSAndroid Build Coastguard Worker double sum_y = 0, sum_yx = 0, sum_yx2 = 0;
288*3ac0a46fSAndroid Build Coastguard Worker double d, a, b, c;
289*3ac0a46fSAndroid Build Coastguard Worker int i;
290*3ac0a46fSAndroid Build Coastguard Worker cmsMAT3 m;
291*3ac0a46fSAndroid Build Coastguard Worker cmsVEC3 v, res;
292*3ac0a46fSAndroid Build Coastguard Worker
293*3ac0a46fSAndroid Build Coastguard Worker if (n < 4) return 0;
294*3ac0a46fSAndroid Build Coastguard Worker
295*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < n; i++) {
296*3ac0a46fSAndroid Build Coastguard Worker
297*3ac0a46fSAndroid Build Coastguard Worker double xn = x[i];
298*3ac0a46fSAndroid Build Coastguard Worker double yn = y[i];
299*3ac0a46fSAndroid Build Coastguard Worker
300*3ac0a46fSAndroid Build Coastguard Worker sum_x += xn;
301*3ac0a46fSAndroid Build Coastguard Worker sum_x2 += xn*xn;
302*3ac0a46fSAndroid Build Coastguard Worker sum_x3 += xn*xn*xn;
303*3ac0a46fSAndroid Build Coastguard Worker sum_x4 += xn*xn*xn*xn;
304*3ac0a46fSAndroid Build Coastguard Worker
305*3ac0a46fSAndroid Build Coastguard Worker sum_y += yn;
306*3ac0a46fSAndroid Build Coastguard Worker sum_yx += yn*xn;
307*3ac0a46fSAndroid Build Coastguard Worker sum_yx2 += yn*xn*xn;
308*3ac0a46fSAndroid Build Coastguard Worker }
309*3ac0a46fSAndroid Build Coastguard Worker
310*3ac0a46fSAndroid Build Coastguard Worker _cmsVEC3init(&m.v[0], n, sum_x, sum_x2);
311*3ac0a46fSAndroid Build Coastguard Worker _cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3);
312*3ac0a46fSAndroid Build Coastguard Worker _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4);
313*3ac0a46fSAndroid Build Coastguard Worker
314*3ac0a46fSAndroid Build Coastguard Worker _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2);
315*3ac0a46fSAndroid Build Coastguard Worker
316*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsMAT3solve(&res, &m, &v)) return 0;
317*3ac0a46fSAndroid Build Coastguard Worker
318*3ac0a46fSAndroid Build Coastguard Worker
319*3ac0a46fSAndroid Build Coastguard Worker a = res.n[2];
320*3ac0a46fSAndroid Build Coastguard Worker b = res.n[1];
321*3ac0a46fSAndroid Build Coastguard Worker c = res.n[0];
322*3ac0a46fSAndroid Build Coastguard Worker
323*3ac0a46fSAndroid Build Coastguard Worker if (fabs(a) < 1.0E-10) {
324*3ac0a46fSAndroid Build Coastguard Worker
325*3ac0a46fSAndroid Build Coastguard Worker if (fabs(b) < 1.0E-10) return 0;
326*3ac0a46fSAndroid Build Coastguard Worker return cmsmin(0, cmsmax(50, -c/b ));
327*3ac0a46fSAndroid Build Coastguard Worker }
328*3ac0a46fSAndroid Build Coastguard Worker else {
329*3ac0a46fSAndroid Build Coastguard Worker
330*3ac0a46fSAndroid Build Coastguard Worker d = b*b - 4.0 * a * c;
331*3ac0a46fSAndroid Build Coastguard Worker if (d <= 0) {
332*3ac0a46fSAndroid Build Coastguard Worker return 0;
333*3ac0a46fSAndroid Build Coastguard Worker }
334*3ac0a46fSAndroid Build Coastguard Worker else {
335*3ac0a46fSAndroid Build Coastguard Worker
336*3ac0a46fSAndroid Build Coastguard Worker double rt;
337*3ac0a46fSAndroid Build Coastguard Worker
338*3ac0a46fSAndroid Build Coastguard Worker if (fabs(a) < 1.0E-10) return 0;
339*3ac0a46fSAndroid Build Coastguard Worker
340*3ac0a46fSAndroid Build Coastguard Worker rt = (-b + sqrt(d)) / (2.0 * a);
341*3ac0a46fSAndroid Build Coastguard Worker
342*3ac0a46fSAndroid Build Coastguard Worker return cmsmax(0, cmsmin(50, rt));
343*3ac0a46fSAndroid Build Coastguard Worker }
344*3ac0a46fSAndroid Build Coastguard Worker }
345*3ac0a46fSAndroid Build Coastguard Worker
346*3ac0a46fSAndroid Build Coastguard Worker }
347*3ac0a46fSAndroid Build Coastguard Worker
348*3ac0a46fSAndroid Build Coastguard Worker
349*3ac0a46fSAndroid Build Coastguard Worker
350*3ac0a46fSAndroid Build Coastguard Worker // Calculates the black point of a destination profile.
351*3ac0a46fSAndroid Build Coastguard Worker // This algorithm comes from the Adobe paper disclosing its black point compensation method.
cmsDetectDestinationBlackPoint(cmsCIEXYZ * BlackPoint,cmsHPROFILE hProfile,cmsUInt32Number Intent,cmsUInt32Number dwFlags)352*3ac0a46fSAndroid Build Coastguard Worker cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
353*3ac0a46fSAndroid Build Coastguard Worker {
354*3ac0a46fSAndroid Build Coastguard Worker cmsColorSpaceSignature ColorSpace;
355*3ac0a46fSAndroid Build Coastguard Worker cmsHTRANSFORM hRoundTrip = NULL;
356*3ac0a46fSAndroid Build Coastguard Worker cmsCIELab InitialLab, destLab, Lab;
357*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number inRamp[256], outRamp[256];
358*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number MinL, MaxL;
359*3ac0a46fSAndroid Build Coastguard Worker cmsBool NearlyStraightMidrange = TRUE;
360*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number yRamp[256];
361*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number x[256], y[256];
362*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number lo, hi;
363*3ac0a46fSAndroid Build Coastguard Worker int n, l;
364*3ac0a46fSAndroid Build Coastguard Worker cmsProfileClassSignature devClass;
365*3ac0a46fSAndroid Build Coastguard Worker
366*3ac0a46fSAndroid Build Coastguard Worker // Make sure the device class is adequate
367*3ac0a46fSAndroid Build Coastguard Worker devClass = cmsGetDeviceClass(hProfile);
368*3ac0a46fSAndroid Build Coastguard Worker if (devClass == cmsSigLinkClass ||
369*3ac0a46fSAndroid Build Coastguard Worker devClass == cmsSigAbstractClass ||
370*3ac0a46fSAndroid Build Coastguard Worker devClass == cmsSigNamedColorClass) {
371*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
372*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
373*3ac0a46fSAndroid Build Coastguard Worker }
374*3ac0a46fSAndroid Build Coastguard Worker
375*3ac0a46fSAndroid Build Coastguard Worker // Make sure intent is adequate
376*3ac0a46fSAndroid Build Coastguard Worker if (Intent != INTENT_PERCEPTUAL &&
377*3ac0a46fSAndroid Build Coastguard Worker Intent != INTENT_RELATIVE_COLORIMETRIC &&
378*3ac0a46fSAndroid Build Coastguard Worker Intent != INTENT_SATURATION) {
379*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
380*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
381*3ac0a46fSAndroid Build Coastguard Worker }
382*3ac0a46fSAndroid Build Coastguard Worker
383*3ac0a46fSAndroid Build Coastguard Worker
384*3ac0a46fSAndroid Build Coastguard Worker // v4 + perceptual & saturation intents does have its own black point, and it is
385*3ac0a46fSAndroid Build Coastguard Worker // well specified enough to use it. Black point tag is deprecated in V4.
386*3ac0a46fSAndroid Build Coastguard Worker if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
387*3ac0a46fSAndroid Build Coastguard Worker (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
388*3ac0a46fSAndroid Build Coastguard Worker
389*3ac0a46fSAndroid Build Coastguard Worker // Matrix shaper share MRC & perceptual intents
390*3ac0a46fSAndroid Build Coastguard Worker if (cmsIsMatrixShaper(hProfile))
391*3ac0a46fSAndroid Build Coastguard Worker return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
392*3ac0a46fSAndroid Build Coastguard Worker
393*3ac0a46fSAndroid Build Coastguard Worker // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
394*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
395*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
396*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
397*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
398*3ac0a46fSAndroid Build Coastguard Worker }
399*3ac0a46fSAndroid Build Coastguard Worker
400*3ac0a46fSAndroid Build Coastguard Worker
401*3ac0a46fSAndroid Build Coastguard Worker // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document)
402*3ac0a46fSAndroid Build Coastguard Worker ColorSpace = cmsGetColorSpace(hProfile);
403*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) ||
404*3ac0a46fSAndroid Build Coastguard Worker (ColorSpace != cmsSigGrayData &&
405*3ac0a46fSAndroid Build Coastguard Worker ColorSpace != cmsSigRgbData &&
406*3ac0a46fSAndroid Build Coastguard Worker ColorSpace != cmsSigCmykData)) {
407*3ac0a46fSAndroid Build Coastguard Worker
408*3ac0a46fSAndroid Build Coastguard Worker // In this case, handle as input case
409*3ac0a46fSAndroid Build Coastguard Worker return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags);
410*3ac0a46fSAndroid Build Coastguard Worker }
411*3ac0a46fSAndroid Build Coastguard Worker
412*3ac0a46fSAndroid Build Coastguard Worker // It is one of the valid cases!, use Adobe algorithm
413*3ac0a46fSAndroid Build Coastguard Worker
414*3ac0a46fSAndroid Build Coastguard Worker
415*3ac0a46fSAndroid Build Coastguard Worker // Set a first guess, that should work on good profiles.
416*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
417*3ac0a46fSAndroid Build Coastguard Worker
418*3ac0a46fSAndroid Build Coastguard Worker cmsCIEXYZ IniXYZ;
419*3ac0a46fSAndroid Build Coastguard Worker
420*3ac0a46fSAndroid Build Coastguard Worker // calculate initial Lab as source black point
421*3ac0a46fSAndroid Build Coastguard Worker if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) {
422*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
423*3ac0a46fSAndroid Build Coastguard Worker }
424*3ac0a46fSAndroid Build Coastguard Worker
425*3ac0a46fSAndroid Build Coastguard Worker // convert the XYZ to lab
426*3ac0a46fSAndroid Build Coastguard Worker cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ);
427*3ac0a46fSAndroid Build Coastguard Worker
428*3ac0a46fSAndroid Build Coastguard Worker } else {
429*3ac0a46fSAndroid Build Coastguard Worker
430*3ac0a46fSAndroid Build Coastguard Worker // set the initial Lab to zero, that should be the black point for perceptual and saturation
431*3ac0a46fSAndroid Build Coastguard Worker InitialLab.L = 0;
432*3ac0a46fSAndroid Build Coastguard Worker InitialLab.a = 0;
433*3ac0a46fSAndroid Build Coastguard Worker InitialLab.b = 0;
434*3ac0a46fSAndroid Build Coastguard Worker }
435*3ac0a46fSAndroid Build Coastguard Worker
436*3ac0a46fSAndroid Build Coastguard Worker
437*3ac0a46fSAndroid Build Coastguard Worker // Step 2
438*3ac0a46fSAndroid Build Coastguard Worker // ======
439*3ac0a46fSAndroid Build Coastguard Worker
440*3ac0a46fSAndroid Build Coastguard Worker // Create a roundtrip. Define a Transform BT for all x in L*a*b*
441*3ac0a46fSAndroid Build Coastguard Worker hRoundTrip = CreateRoundtripXForm(hProfile, Intent);
442*3ac0a46fSAndroid Build Coastguard Worker if (hRoundTrip == NULL) return FALSE;
443*3ac0a46fSAndroid Build Coastguard Worker
444*3ac0a46fSAndroid Build Coastguard Worker // Compute ramps
445*3ac0a46fSAndroid Build Coastguard Worker
446*3ac0a46fSAndroid Build Coastguard Worker for (l=0; l < 256; l++) {
447*3ac0a46fSAndroid Build Coastguard Worker
448*3ac0a46fSAndroid Build Coastguard Worker Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0;
449*3ac0a46fSAndroid Build Coastguard Worker Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a));
450*3ac0a46fSAndroid Build Coastguard Worker Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b));
451*3ac0a46fSAndroid Build Coastguard Worker
452*3ac0a46fSAndroid Build Coastguard Worker cmsDoTransform(hRoundTrip, &Lab, &destLab, 1);
453*3ac0a46fSAndroid Build Coastguard Worker
454*3ac0a46fSAndroid Build Coastguard Worker inRamp[l] = Lab.L;
455*3ac0a46fSAndroid Build Coastguard Worker outRamp[l] = destLab.L;
456*3ac0a46fSAndroid Build Coastguard Worker }
457*3ac0a46fSAndroid Build Coastguard Worker
458*3ac0a46fSAndroid Build Coastguard Worker // Make monotonic
459*3ac0a46fSAndroid Build Coastguard Worker for (l = 254; l > 0; --l) {
460*3ac0a46fSAndroid Build Coastguard Worker outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]);
461*3ac0a46fSAndroid Build Coastguard Worker }
462*3ac0a46fSAndroid Build Coastguard Worker
463*3ac0a46fSAndroid Build Coastguard Worker // Check
464*3ac0a46fSAndroid Build Coastguard Worker if (! (outRamp[0] < outRamp[255])) {
465*3ac0a46fSAndroid Build Coastguard Worker
466*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(hRoundTrip);
467*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
468*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
469*3ac0a46fSAndroid Build Coastguard Worker }
470*3ac0a46fSAndroid Build Coastguard Worker
471*3ac0a46fSAndroid Build Coastguard Worker
472*3ac0a46fSAndroid Build Coastguard Worker // Test for mid range straight (only on relative colorimetric)
473*3ac0a46fSAndroid Build Coastguard Worker NearlyStraightMidrange = TRUE;
474*3ac0a46fSAndroid Build Coastguard Worker MinL = outRamp[0]; MaxL = outRamp[255];
475*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
476*3ac0a46fSAndroid Build Coastguard Worker
477*3ac0a46fSAndroid Build Coastguard Worker for (l=0; l < 256; l++) {
478*3ac0a46fSAndroid Build Coastguard Worker
479*3ac0a46fSAndroid Build Coastguard Worker if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) ||
480*3ac0a46fSAndroid Build Coastguard Worker (fabs(inRamp[l] - outRamp[l]) < 4.0 )))
481*3ac0a46fSAndroid Build Coastguard Worker NearlyStraightMidrange = FALSE;
482*3ac0a46fSAndroid Build Coastguard Worker }
483*3ac0a46fSAndroid Build Coastguard Worker
484*3ac0a46fSAndroid Build Coastguard Worker // If the mid range is straight (as determined above) then the
485*3ac0a46fSAndroid Build Coastguard Worker // DestinationBlackPoint shall be the same as initialLab.
486*3ac0a46fSAndroid Build Coastguard Worker // Otherwise, the DestinationBlackPoint shall be determined
487*3ac0a46fSAndroid Build Coastguard Worker // using curve fitting.
488*3ac0a46fSAndroid Build Coastguard Worker if (NearlyStraightMidrange) {
489*3ac0a46fSAndroid Build Coastguard Worker
490*3ac0a46fSAndroid Build Coastguard Worker cmsLab2XYZ(NULL, BlackPoint, &InitialLab);
491*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(hRoundTrip);
492*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
493*3ac0a46fSAndroid Build Coastguard Worker }
494*3ac0a46fSAndroid Build Coastguard Worker }
495*3ac0a46fSAndroid Build Coastguard Worker
496*3ac0a46fSAndroid Build Coastguard Worker
497*3ac0a46fSAndroid Build Coastguard Worker // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point,
498*3ac0a46fSAndroid Build Coastguard Worker // with a corner and a nearly straight line to the white point.
499*3ac0a46fSAndroid Build Coastguard Worker for (l=0; l < 256; l++) {
500*3ac0a46fSAndroid Build Coastguard Worker
501*3ac0a46fSAndroid Build Coastguard Worker yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL);
502*3ac0a46fSAndroid Build Coastguard Worker }
503*3ac0a46fSAndroid Build Coastguard Worker
504*3ac0a46fSAndroid Build Coastguard Worker // find the black point using the least squares error quadratic curve fitting
505*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
506*3ac0a46fSAndroid Build Coastguard Worker lo = 0.1;
507*3ac0a46fSAndroid Build Coastguard Worker hi = 0.5;
508*3ac0a46fSAndroid Build Coastguard Worker }
509*3ac0a46fSAndroid Build Coastguard Worker else {
510*3ac0a46fSAndroid Build Coastguard Worker
511*3ac0a46fSAndroid Build Coastguard Worker // Perceptual and saturation
512*3ac0a46fSAndroid Build Coastguard Worker lo = 0.03;
513*3ac0a46fSAndroid Build Coastguard Worker hi = 0.25;
514*3ac0a46fSAndroid Build Coastguard Worker }
515*3ac0a46fSAndroid Build Coastguard Worker
516*3ac0a46fSAndroid Build Coastguard Worker // Capture shadow points for the fitting.
517*3ac0a46fSAndroid Build Coastguard Worker n = 0;
518*3ac0a46fSAndroid Build Coastguard Worker for (l=0; l < 256; l++) {
519*3ac0a46fSAndroid Build Coastguard Worker
520*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number ff = yRamp[l];
521*3ac0a46fSAndroid Build Coastguard Worker
522*3ac0a46fSAndroid Build Coastguard Worker if (ff >= lo && ff < hi) {
523*3ac0a46fSAndroid Build Coastguard Worker x[n] = inRamp[l];
524*3ac0a46fSAndroid Build Coastguard Worker y[n] = yRamp[l];
525*3ac0a46fSAndroid Build Coastguard Worker n++;
526*3ac0a46fSAndroid Build Coastguard Worker }
527*3ac0a46fSAndroid Build Coastguard Worker }
528*3ac0a46fSAndroid Build Coastguard Worker
529*3ac0a46fSAndroid Build Coastguard Worker
530*3ac0a46fSAndroid Build Coastguard Worker // No suitable points
531*3ac0a46fSAndroid Build Coastguard Worker if (n < 3 ) {
532*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(hRoundTrip);
533*3ac0a46fSAndroid Build Coastguard Worker BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
534*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
535*3ac0a46fSAndroid Build Coastguard Worker }
536*3ac0a46fSAndroid Build Coastguard Worker
537*3ac0a46fSAndroid Build Coastguard Worker
538*3ac0a46fSAndroid Build Coastguard Worker // fit and get the vertex of quadratic curve
539*3ac0a46fSAndroid Build Coastguard Worker Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y);
540*3ac0a46fSAndroid Build Coastguard Worker
541*3ac0a46fSAndroid Build Coastguard Worker if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative
542*3ac0a46fSAndroid Build Coastguard Worker Lab.L = 0;
543*3ac0a46fSAndroid Build Coastguard Worker }
544*3ac0a46fSAndroid Build Coastguard Worker
545*3ac0a46fSAndroid Build Coastguard Worker Lab.a = InitialLab.a;
546*3ac0a46fSAndroid Build Coastguard Worker Lab.b = InitialLab.b;
547*3ac0a46fSAndroid Build Coastguard Worker
548*3ac0a46fSAndroid Build Coastguard Worker cmsLab2XYZ(NULL, BlackPoint, &Lab);
549*3ac0a46fSAndroid Build Coastguard Worker
550*3ac0a46fSAndroid Build Coastguard Worker cmsDeleteTransform(hRoundTrip);
551*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
552*3ac0a46fSAndroid Build Coastguard Worker }
553