|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
3 * |
|
4 * This code is free software; you can redistribute it and/or modify it |
|
5 * under the terms of the GNU General Public License version 2 only, as |
|
6 * published by the Free Software Foundation. Sun designates this |
|
7 * particular file as subject to the "Classpath" exception as provided |
|
8 * by Sun in the LICENSE file that accompanied this code. |
|
9 * |
|
10 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
13 * version 2 for more details (a copy is included in the LICENSE file that |
|
14 * accompanied this code). |
|
15 * |
|
16 * You should have received a copy of the GNU General Public License version |
|
17 * 2 along with this work; if not, write to the Free Software Foundation, |
|
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
19 * |
|
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
21 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
22 * have any questions. |
|
23 */ |
|
24 |
|
25 // This file is available under and governed by the GNU General Public |
|
26 // License version 2 only, as published by the Free Software Foundation. |
|
27 // However, the following notice accompanied the original version of this |
|
28 // file: |
|
29 // |
|
30 // |
|
31 // Little cms |
|
32 // Copyright (C) 1998-2006 Marti Maria |
|
33 // |
|
34 // Permission is hereby granted, free of charge, to any person obtaining |
|
35 // a copy of this software and associated documentation files (the "Software"), |
|
36 // to deal in the Software without restriction, including without limitation |
|
37 // the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
38 // and/or sell copies of the Software, and to permit persons to whom the Software |
|
39 // is furnished to do so, subject to the following conditions: |
|
40 // |
|
41 // The above copyright notice and this permission notice shall be included in |
|
42 // all copies or substantial portions of the Software. |
|
43 // |
|
44 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
45 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO |
|
46 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
47 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
48 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
49 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
50 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
51 |
|
52 |
|
53 #include "lcms.h" |
|
54 |
|
55 |
|
56 // --------------------------------------------------------------------------------- |
|
57 |
|
58 static volatile int GlobalBlackPreservationStrategy = 0; |
|
59 |
|
60 // Quantize a value 0 <= i < MaxSamples |
|
61 |
|
62 WORD _cmsQuantizeVal(double i, int MaxSamples) |
|
63 { |
|
64 double x; |
|
65 |
|
66 x = ((double) i * 65535.) / (double) (MaxSamples - 1); |
|
67 |
|
68 return (WORD) floor(x + .5); |
|
69 } |
|
70 |
|
71 |
|
72 // Is a table linear? |
|
73 |
|
74 int cmsIsLinear(WORD Table[], int nEntries) |
|
75 { |
|
76 register int i; |
|
77 int diff; |
|
78 |
|
79 for (i=0; i < nEntries; i++) { |
|
80 |
|
81 diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries)); |
|
82 if (diff > 3) |
|
83 return 0; |
|
84 } |
|
85 |
|
86 return 1; |
|
87 } |
|
88 |
|
89 |
|
90 |
|
91 // pow() restricted to integer |
|
92 |
|
93 static |
|
94 int ipow(int base, int exp) |
|
95 { |
|
96 int res = base; |
|
97 |
|
98 while (--exp) |
|
99 res *= base; |
|
100 |
|
101 return res; |
|
102 } |
|
103 |
|
104 |
|
105 // Given n, 0<=n<=clut^dim, returns the colorant. |
|
106 |
|
107 static |
|
108 int ComponentOf(int n, int clut, int nColorant) |
|
109 { |
|
110 if (nColorant <= 0) |
|
111 return (n % clut); |
|
112 |
|
113 n /= ipow(clut, nColorant); |
|
114 |
|
115 return (n % clut); |
|
116 } |
|
117 |
|
118 |
|
119 |
|
120 // This routine does a sweep on whole input space, and calls its callback |
|
121 // function on knots. returns TRUE if all ok, FALSE otherwise. |
|
122 |
|
123 BOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags) |
|
124 { |
|
125 int i, t, nTotalPoints, Colorant, index; |
|
126 WORD In[MAXCHANNELS], Out[MAXCHANNELS]; |
|
127 |
|
128 nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan); |
|
129 |
|
130 index = 0; |
|
131 for (i = 0; i < nTotalPoints; i++) { |
|
132 |
|
133 for (t=0; t < (int) Lut -> InputChan; t++) { |
|
134 |
|
135 Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 )); |
|
136 In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints); |
|
137 } |
|
138 |
|
139 |
|
140 if (dwFlags & SAMPLER_HASTL1) { |
|
141 |
|
142 for (t=0; t < (int) Lut -> InputChan; t++) |
|
143 In[t] = cmsReverseLinearInterpLUT16(In[t], |
|
144 Lut -> L1[t], |
|
145 &Lut -> In16params); |
|
146 } |
|
147 |
|
148 |
|
149 // if (dwFlags & SAMPLER_INSPECT) { |
|
150 |
|
151 for (t=0; t < (int) Lut -> OutputChan; t++) |
|
152 Out[t] = Lut->T[index + t]; |
|
153 // } |
|
154 |
|
155 |
|
156 if (!Sampler(In, Out, Cargo)) |
|
157 return FALSE; |
|
158 |
|
159 if (!(dwFlags & SAMPLER_INSPECT)) { |
|
160 |
|
161 if (dwFlags & SAMPLER_HASTL2) { |
|
162 |
|
163 for (t=0; t < (int) Lut -> OutputChan; t++) |
|
164 Out[t] = cmsReverseLinearInterpLUT16(Out[t], |
|
165 Lut -> L2[t], |
|
166 &Lut -> Out16params); |
|
167 } |
|
168 |
|
169 |
|
170 for (t=0; t < (int) Lut -> OutputChan; t++) |
|
171 Lut->T[index + t] = Out[t]; |
|
172 |
|
173 } |
|
174 |
|
175 index += Lut -> OutputChan; |
|
176 |
|
177 } |
|
178 |
|
179 return TRUE; |
|
180 } |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 // choose reasonable resolution |
|
188 int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags) |
|
189 { |
|
190 int nChannels; |
|
191 |
|
192 // Already specified? |
|
193 if (dwFlags & 0x00FF0000) { |
|
194 // Yes, grab'em |
|
195 return (dwFlags >> 16) & 0xFF; |
|
196 } |
|
197 |
|
198 nChannels = _cmsChannelsOf(Colorspace); |
|
199 |
|
200 // HighResPrecalc is maximum resolution |
|
201 |
|
202 if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { |
|
203 |
|
204 if (nChannels > 4) |
|
205 return 7; // 7 for Hifi |
|
206 |
|
207 if (nChannels == 4) // 23 for CMYK |
|
208 return 23; |
|
209 |
|
210 return 49; // 49 for RGB and others |
|
211 } |
|
212 |
|
213 |
|
214 // LowResPrecal is stripped resolution |
|
215 |
|
216 if (dwFlags & cmsFLAGS_LOWRESPRECALC) { |
|
217 |
|
218 if (nChannels > 4) |
|
219 return 6; // 6 for Hifi |
|
220 |
|
221 if (nChannels == 1) |
|
222 return 33; // For monochrome |
|
223 |
|
224 return 17; // 17 for remaining |
|
225 } |
|
226 |
|
227 // Default values |
|
228 |
|
229 if (nChannels > 4) |
|
230 return 7; // 7 for Hifi |
|
231 |
|
232 if (nChannels == 4) |
|
233 return 17; // 17 for CMYK |
|
234 |
|
235 return 33; // 33 for RGB |
|
236 |
|
237 } |
|
238 |
|
239 // Sampler implemented by another transform. This is a clean way to |
|
240 // precalculate the devicelink 3D CLUT for almost any transform |
|
241 |
|
242 static |
|
243 int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
|
244 { |
|
245 cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1); |
|
246 return TRUE; |
|
247 } |
|
248 |
|
249 // This routine does compute the devicelink CLUT containing whole |
|
250 // transform. Handles any channel number. |
|
251 |
|
252 LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags) |
|
253 { |
|
254 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h; |
|
255 LPLUT Grid; |
|
256 int nGridPoints; |
|
257 DWORD dwFormatIn, dwFormatOut; |
|
258 int ChannelsIn, ChannelsOut; |
|
259 LPLUT SaveGamutLUT; |
|
260 |
|
261 // Remove any gamut checking |
|
262 SaveGamutLUT = p ->Gamut; |
|
263 p ->Gamut = NULL; |
|
264 |
|
265 ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace); |
|
266 ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace); |
|
267 |
|
268 nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); |
|
269 |
|
270 Grid = cmsAllocLUT(); |
|
271 if (!Grid) return NULL; |
|
272 |
|
273 Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut); |
|
274 |
|
275 // Compute device link on 16-bit basis |
|
276 dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2)); |
|
277 dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2)); |
|
278 |
|
279 p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn); |
|
280 p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut); |
|
281 |
|
282 // Fix gamut & gamma possible mismatches. |
|
283 |
|
284 if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) { |
|
285 |
|
286 cmsHTRANSFORM hOne[1]; |
|
287 hOne[0] = h; |
|
288 |
|
289 _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid); |
|
290 } |
|
291 |
|
292 |
|
293 // Attention to this typecast! we can take the luxury to |
|
294 // do this since cmsHTRANSFORM is only an alias to a pointer |
|
295 // to the transform struct. |
|
296 |
|
297 if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) { |
|
298 |
|
299 cmsFreeLUT(Grid); |
|
300 return NULL; |
|
301 } |
|
302 |
|
303 |
|
304 p ->Gamut = SaveGamutLUT; |
|
305 return Grid; |
|
306 } |
|
307 |
|
308 |
|
309 |
|
310 // Sampler for Black-preserving CMYK->CMYK transforms |
|
311 |
|
312 typedef struct { |
|
313 cmsHTRANSFORM cmyk2cmyk; |
|
314 cmsHTRANSFORM cmyk2Lab; |
|
315 LPGAMMATABLE KTone; |
|
316 L16PARAMS KToneParams; |
|
317 LPLUT LabK2cmyk; |
|
318 double MaxError; |
|
319 |
|
320 cmsHTRANSFORM hRoundTrip; |
|
321 int MaxTAC; |
|
322 |
|
323 cmsHTRANSFORM hProofOutput; |
|
324 |
|
325 } BPCARGO, *LPBPCARGO; |
|
326 |
|
327 |
|
328 |
|
329 // Preserve black only if that is the only ink used |
|
330 static |
|
331 int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
|
332 { |
|
333 BPCARGO* bp = (LPBPCARGO) Cargo; |
|
334 |
|
335 // If going across black only, keep black only |
|
336 if (In[0] == 0 && In[1] == 0 && In[2] == 0) { |
|
337 |
|
338 // TAC does not apply because it is black ink! |
|
339 Out[0] = Out[1] = Out[2] = 0; |
|
340 Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); |
|
341 return 1; |
|
342 } |
|
343 |
|
344 // Keep normal transform for other colors |
|
345 cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); |
|
346 return 1; |
|
347 } |
|
348 |
|
349 |
|
350 |
|
351 // That is our K-preserving callback. |
|
352 static |
|
353 int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
|
354 { |
|
355 |
|
356 WORD LabK[4]; |
|
357 double SumCMY, SumCMYK, Error; |
|
358 cmsCIELab ColorimetricLab, BlackPreservingLab; |
|
359 BPCARGO* bp = (LPBPCARGO) Cargo; |
|
360 |
|
361 // Get the K across Tone curve |
|
362 LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); |
|
363 |
|
364 // If going across black only, keep black only |
|
365 if (In[0] == 0 && In[1] == 0 && In[2] == 0) { |
|
366 |
|
367 Out[0] = Out[1] = Out[2] = 0; |
|
368 Out[3] = LabK[3]; |
|
369 return 1; |
|
370 } |
|
371 |
|
372 // Try the original transform, maybe K is already ok (valid on K=0) |
|
373 cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); |
|
374 if (Out[3] == LabK[3]) return 1; |
|
375 |
|
376 |
|
377 // No, mesure and keep Lab measurement for further usage |
|
378 cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); |
|
379 |
|
380 // Is not black only and the transform doesn't keep black. |
|
381 // Obtain the Lab of CMYK. After that we have Lab + K |
|
382 cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1); |
|
383 |
|
384 // Obtain the corresponding CMY using reverse interpolation. |
|
385 // As a seed, we use the colorimetric CMY |
|
386 cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out); |
|
387 |
|
388 // Estimate the error |
|
389 cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); |
|
390 Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); |
|
391 |
|
392 |
|
393 // Apply TAC if needed |
|
394 |
|
395 SumCMY = Out[0] + Out[1] + Out[2]; |
|
396 SumCMYK = SumCMY + Out[3]; |
|
397 |
|
398 if (SumCMYK > bp ->MaxTAC) { |
|
399 |
|
400 double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); |
|
401 if (Ratio < 0) |
|
402 Ratio = 0; |
|
403 |
|
404 Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C |
|
405 Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M |
|
406 Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y |
|
407 } |
|
408 |
|
409 return 1; |
|
410 } |
|
411 |
|
412 |
|
413 // Sample whole gamut to estimate maximum TAC |
|
414 |
|
415 #ifdef _MSC_VER |
|
416 #pragma warning(disable : 4100) |
|
417 #endif |
|
418 |
|
419 static |
|
420 int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo) |
|
421 { |
|
422 BPCARGO* bp = (LPBPCARGO) Cargo; |
|
423 WORD RoundTrip[4]; |
|
424 int Sum; |
|
425 |
|
426 cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); |
|
427 |
|
428 Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3]; |
|
429 |
|
430 if (Sum > bp ->MaxTAC) |
|
431 bp ->MaxTAC = Sum; |
|
432 |
|
433 return 1; |
|
434 } |
|
435 |
|
436 |
|
437 // Estimate the maximum error |
|
438 static |
|
439 int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) |
|
440 { |
|
441 BPCARGO* bp = (LPBPCARGO) Cargo; |
|
442 WORD ColorimetricOut[4]; |
|
443 cmsCIELab ColorimetricLab, BlackPreservingLab; |
|
444 double Error; |
|
445 |
|
446 if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1; |
|
447 |
|
448 cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1); |
|
449 |
|
450 cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1); |
|
451 cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); |
|
452 |
|
453 Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); |
|
454 |
|
455 if (Error > bp ->MaxError) |
|
456 bp ->MaxError = Error; |
|
457 |
|
458 return 1; |
|
459 } |
|
460 |
|
461 // Setup the K preservation strategy |
|
462 int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n) |
|
463 { |
|
464 int OldVal = GlobalBlackPreservationStrategy; |
|
465 |
|
466 if (n >= 0) |
|
467 GlobalBlackPreservationStrategy = n; |
|
468 |
|
469 return OldVal; |
|
470 } |
|
471 |
|
472 |
|
473 // Get a pointer to callback on depending of strategy |
|
474 static |
|
475 _cmsSAMPLER _cmsGetBlackPreservationSampler(void) |
|
476 { |
|
477 switch (GlobalBlackPreservationStrategy) { |
|
478 |
|
479 case 0: return BlackPreservingGrayOnlySampler; |
|
480 default: return BlackPreservingSampler; |
|
481 } |
|
482 |
|
483 } |
|
484 |
|
485 // This is the black-preserving devicelink generator |
|
486 LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags) |
|
487 { |
|
488 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK; |
|
489 BPCARGO Cargo; |
|
490 LPLUT Grid; |
|
491 DWORD LocalFlags; |
|
492 cmsHPROFILE hLab = cmsCreateLabProfile(NULL); |
|
493 int nGridPoints; |
|
494 icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual |
|
495 icSigAToB1Tag, // Relative colorimetric |
|
496 icSigAToB2Tag, // Saturation |
|
497 icSigAToB1Tag }; // Absolute colorimetric |
|
498 // (Relative/WhitePoint) |
|
499 |
|
500 nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); |
|
501 |
|
502 // Get a copy of inteserting flags for this kind of xform |
|
503 LocalFlags = cmsFLAGS_NOTPRECALC; |
|
504 if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) |
|
505 LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; |
|
506 |
|
507 |
|
508 // Fill in cargo struct |
|
509 Cargo.cmyk2cmyk = hCMYK2CMYK; |
|
510 |
|
511 // Compute tone curve |
|
512 Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256); |
|
513 if (Cargo.KTone == NULL) return NULL; |
|
514 cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams); |
|
515 |
|
516 |
|
517 // Create a CMYK->Lab "normal" transform on input, without K-preservation |
|
518 Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16, |
|
519 hLab, TYPE_Lab_16, p->Intent, LocalFlags); |
|
520 |
|
521 // We are going to use the reverse of proof direction |
|
522 Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]); |
|
523 |
|
524 // Is there any table available? |
|
525 if (Cargo.LabK2cmyk == NULL) { |
|
526 |
|
527 Grid = NULL; |
|
528 goto Cleanup; |
|
529 } |
|
530 |
|
531 // Setup a roundtrip on output profile for TAC estimation |
|
532 Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, |
|
533 p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC); |
|
534 |
|
535 |
|
536 // Setup a proof CMYK->Lab on output |
|
537 Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, |
|
538 hLab, TYPE_Lab_DBL, p->Intent, LocalFlags); |
|
539 |
|
540 |
|
541 // Create an empty LUT for holding K-preserving xform |
|
542 Grid = cmsAllocLUT(); |
|
543 if (!Grid) goto Cleanup; |
|
544 |
|
545 Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4); |
|
546 |
|
547 // Setup formatters |
|
548 p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16); |
|
549 p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16); |
|
550 |
|
551 |
|
552 |
|
553 // Step #1, estimate TAC |
|
554 Cargo.MaxTAC = 0; |
|
555 if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) { |
|
556 |
|
557 cmsFreeLUT(Grid); |
|
558 Grid = NULL; |
|
559 goto Cleanup; |
|
560 } |
|
561 |
|
562 |
|
563 // Step #2, compute approximation |
|
564 if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) { |
|
565 |
|
566 cmsFreeLUT(Grid); |
|
567 Grid = NULL; |
|
568 goto Cleanup; |
|
569 } |
|
570 |
|
571 // Step #3, estimate error |
|
572 Cargo.MaxError = 0; |
|
573 cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT); |
|
574 |
|
575 |
|
576 Cleanup: |
|
577 |
|
578 if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab); |
|
579 if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip); |
|
580 if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput); |
|
581 |
|
582 if (hLab) cmsCloseProfile(hLab); |
|
583 if (Cargo.KTone) cmsFreeGamma(Cargo.KTone); |
|
584 if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk); |
|
585 |
|
586 return Grid; |
|
587 } |
|
588 |
|
589 |
|
590 |
|
591 // Fix broken LUT. just to obtain other CMS compatibility |
|
592 |
|
593 static |
|
594 void PatchLUT(LPLUT Grid, WORD At[], WORD Value[], |
|
595 int nChannelsOut, int nChannelsIn) |
|
596 { |
|
597 LPL16PARAMS p16 = &Grid -> CLut16params; |
|
598 double px, py, pz, pw; |
|
599 int x0, y0, z0, w0; |
|
600 int i, index; |
|
601 |
|
602 |
|
603 if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization |
|
604 |
|
605 px = ((double) At[0] * (p16->Domain)) / 65535.0; |
|
606 py = ((double) At[1] * (p16->Domain)) / 65535.0; |
|
607 pz = ((double) At[2] * (p16->Domain)) / 65535.0; |
|
608 pw = ((double) At[3] * (p16->Domain)) / 65535.0; |
|
609 |
|
610 x0 = (int) floor(px); |
|
611 y0 = (int) floor(py); |
|
612 z0 = (int) floor(pz); |
|
613 w0 = (int) floor(pw); |
|
614 |
|
615 if (nChannelsIn == 4) { |
|
616 |
|
617 if (((px - x0) != 0) || |
|
618 ((py - y0) != 0) || |
|
619 ((pz - z0) != 0) || |
|
620 ((pw - w0) != 0)) return; // Not on exact node |
|
621 |
|
622 index = p16 -> opta4 * x0 + |
|
623 p16 -> opta3 * y0 + |
|
624 p16 -> opta2 * z0 + |
|
625 p16 -> opta1 * w0; |
|
626 } |
|
627 else |
|
628 if (nChannelsIn == 3) { |
|
629 |
|
630 if (((px - x0) != 0) || |
|
631 ((py - y0) != 0) || |
|
632 ((pz - z0) != 0)) return; // Not on exact node |
|
633 |
|
634 index = p16 -> opta3 * x0 + |
|
635 p16 -> opta2 * y0 + |
|
636 p16 -> opta1 * z0; |
|
637 } |
|
638 else |
|
639 if (nChannelsIn == 1) { |
|
640 |
|
641 if (((px - x0) != 0)) return; // Not on exact node |
|
642 |
|
643 index = p16 -> opta1 * x0; |
|
644 } |
|
645 else { |
|
646 cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); |
|
647 return; |
|
648 } |
|
649 |
|
650 for (i=0; i < nChannelsOut; i++) |
|
651 Grid -> T[index + i] = Value[i]; |
|
652 |
|
653 } |
|
654 |
|
655 |
|
656 |
|
657 BOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p) |
|
658 { |
|
659 |
|
660 WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut; |
|
661 int nOuts, nIns; |
|
662 |
|
663 |
|
664 if (!p -> DeviceLink) return FALSE; |
|
665 |
|
666 if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE; |
|
667 if ((p ->PreviewProfile != NULL) && |
|
668 (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE; |
|
669 |
|
670 |
|
671 if (!_cmsEndPointsBySpace(p -> EntryColorSpace, |
|
672 &WhitePointIn, &BlackPointIn, &nIns)) return FALSE; |
|
673 |
|
674 |
|
675 if (!_cmsEndPointsBySpace(p -> ExitColorSpace, |
|
676 &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE; |
|
677 |
|
678 // Fix white only |
|
679 |
|
680 PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns); |
|
681 // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns); |
|
682 |
|
683 return TRUE; |
|
684 } |