blob: 023a732dfeb90ebc8985075e4091af9cf4aa6cc1 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
58static volatile int GlobalBlackPreservationStrategy = 0;
59
60// Quantize a value 0 <= i < MaxSamples
61
62WORD _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
74int 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
93static
94int 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
107static
108int 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
123BOOL 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
188int _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
242static
243int 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
252LPLUT _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
312typedef 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
330static
331int 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.
352static
353int 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
419static
420int 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
438static
439int 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
462int 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
474static
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
486LPLUT _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
576Cleanup:
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
593static
594void 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
657BOOL _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}