1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 #include "lcms.h"
24
25
26
27
28 /*
29 This module provides conversion stages for handling intents.
30
31 The chain of evaluation in a transform is:
32
33 PCS1 PCS2 PCS3 PCS4
34
35 |From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To |
36 |Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output |
37
38 -------- ------- ------------- --------- ---------- ------------- ------- ---------
39
40 AToB0 prew0 gamut BToA0
41 Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting
42 Intent Intent 1 intent intent Intent 2 Intent
43
44
45 Some of these LUT may be missing
46
47 There are two intents involved here, the intent of the transform itself, and the
48 intent the proof is being done, if is the case. Since the first intent is to be
49 applied to preview, is the proofing intent. The second intent identifies the
50 transform intent. Input data of any stage is taked as relative colorimetric
51 always.
52
53
54 NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should
55 scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of
56 perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black
57 point not zero at all, I'm implementing that as a black point compensation from whatever
58 black from perceptal intent to the reference media black for v4 profiles.
59
60 */
61
62
63
64
65 int cdecl cmsChooseCnvrt(int Absolute,
66 int Phase1, LPcmsCIEXYZ BlackPointIn,
67 LPcmsCIEXYZ WhitePointIn,
68 LPcmsCIEXYZ IlluminantIn,
69 LPMAT3 ChromaticAdaptationMatrixIn,
70
71 int Phase2, LPcmsCIEXYZ BlackPointOut,
72 LPcmsCIEXYZ WhitePointOut,
73 LPcmsCIEXYZ IlluminantOut,
74 LPMAT3 ChromaticAdaptationMatrixOut,
75
76 int DoBlackPointCompensation,
77 double AdaptationState,
78 _cmsADJFN *fn1,
79 LPWMAT3 wm, LPWVEC3 wof);
80
81
82 // -------------------------------------------------------------------------
83
84 // D50 - Widely used
85
cmsD50_XYZ(void)86 LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void)
87 {
88 static cmsCIEXYZ D50XYZ = {D50X, D50Y, D50Z};
89
90 return &D50XYZ;
91 }
92
cmsD50_xyY(void)93 LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void)
94 {
95 static cmsCIExyY D50xyY;
96 cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
97
98 return &D50xyY;
99 }
100
101
102 // ---------------- From LUT to LUT --------------------------
103
104
105 // Calculate m, offset Relativ -> Absolute undoing any chromatic
106 // adaptation done by the profile.
107
108 #ifdef _MSC_VER
109 #pragma warning(disable : 4100 4505)
110 #endif
111
112
113
114 // join scalings to obtain:
115 // relative input to absolute and then to relative output
116
117 static
Rel2RelStepAbsCoefs(double AdaptationState,LPcmsCIEXYZ BlackPointIn,LPcmsCIEXYZ WhitePointIn,LPcmsCIEXYZ IlluminantIn,LPMAT3 ChromaticAdaptationMatrixIn,LPcmsCIEXYZ BlackPointOut,LPcmsCIEXYZ WhitePointOut,LPcmsCIEXYZ IlluminantOut,LPMAT3 ChromaticAdaptationMatrixOut,LPMAT3 m,LPVEC3 of)118 void Rel2RelStepAbsCoefs(double AdaptationState,
119
120 LPcmsCIEXYZ BlackPointIn,
121 LPcmsCIEXYZ WhitePointIn,
122 LPcmsCIEXYZ IlluminantIn,
123 LPMAT3 ChromaticAdaptationMatrixIn,
124
125 LPcmsCIEXYZ BlackPointOut,
126 LPcmsCIEXYZ WhitePointOut,
127 LPcmsCIEXYZ IlluminantOut,
128 LPMAT3 ChromaticAdaptationMatrixOut,
129
130 LPMAT3 m, LPVEC3 of)
131 {
132
133 VEC3 WtPtIn, WtPtInAdapted;
134 VEC3 WtPtOut, WtPtOutAdapted;
135 MAT3 Scale, m1, m2, m3;
136
137 VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z);
138 MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn);
139
140 VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z);
141 MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut);
142
143 VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0);
144 VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0);
145 VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]);
146
147
148 // Adaptation state
149
150 if (AdaptationState == 1.0) {
151
152 // Observer is fully adapted. Keep chromatic adaptation
153
154 CopyMemory(m, &Scale, sizeof(MAT3));
155
156 }
157 else {
158
159 // Observer is not adapted, undo the chromatic adaptation
160 m1 = *ChromaticAdaptationMatrixIn;
161 MAT3inverse(&m1, &m2);
162
163 MAT3per(&m3, &m2, &Scale);
164 MAT3per(m, &m3, ChromaticAdaptationMatrixOut);
165 }
166
167
168 VEC3init(of, 0.0, 0.0, 0.0);
169
170 }
171
172
173 // The (in)famous black point compensation. Right now implemented as
174 // a linear scaling in XYZ
175
176 static
ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn,LPcmsCIEXYZ WhitePointIn,LPcmsCIEXYZ IlluminantIn,LPcmsCIEXYZ BlackPointOut,LPcmsCIEXYZ WhitePointOut,LPcmsCIEXYZ IlluminantOut,LPMAT3 m,LPVEC3 of)177 void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn,
178 LPcmsCIEXYZ WhitePointIn,
179 LPcmsCIEXYZ IlluminantIn,
180 LPcmsCIEXYZ BlackPointOut,
181 LPcmsCIEXYZ WhitePointOut,
182 LPcmsCIEXYZ IlluminantOut,
183 LPMAT3 m, LPVEC3 of)
184 {
185
186
187 cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut;
188 double ax, ay, az, bx, by, bz, tx, ty, tz;
189
190 // At first, convert both black points to relative.
191
192 cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn);
193 cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut);
194
195 // Now we need to compute a matrix plus an offset m and of such of
196 // [m]*bpin + off = bpout
197 // [m]*D50 + off = D50
198 //
199 // This is a linear scaling in the form ax+b, where
200 // a = (bpout - D50) / (bpin - D50)
201 // b = - D50* (bpout - bpin) / (bpin - D50)
202
203
204 tx = RelativeBlackPointIn.X - IlluminantIn ->X;
205 ty = RelativeBlackPointIn.Y - IlluminantIn ->Y;
206 tz = RelativeBlackPointIn.Z - IlluminantIn ->Z;
207
208 ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx;
209 ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty;
210 az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz;
211
212 bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx;
213 by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty;
214 bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz;
215
216
217 MAT3identity(m);
218
219 m->v[VX].n[0] = ax;
220 m->v[VY].n[1] = ay;
221 m->v[VZ].n[2] = az;
222
223 VEC3init(of, bx, by, bz);
224
225 }
226
227 // Return TRUE if both m and of are empy -- "m" being identity and "of" being 0
228
229 static
IdentityParameters(LPWMAT3 m,LPWVEC3 of)230 LCMSBOOL IdentityParameters(LPWMAT3 m, LPWVEC3 of)
231 {
232 WVEC3 wv0;
233
234 VEC3initF(&wv0, 0, 0, 0);
235
236 if (!MAT3isIdentity(m, 0.00001)) return FALSE;
237 if (!VEC3equal(of, &wv0, 0.00001)) return FALSE;
238
239 return TRUE;
240 }
241
242
243
244
245 // ----------------------------------------- Inter PCS conversions
246
247 // XYZ to XYZ linear scaling. Aso used on Black point compensation
248
249 static
XYZ2XYZ(WORD In[],WORD Out[],LPWMAT3 m,LPWVEC3 of)250 void XYZ2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
251 {
252
253 WVEC3 a, r;
254
255 a.n[0] = In[0] << 1;
256 a.n[1] = In[1] << 1;
257 a.n[2] = In[2] << 1;
258
259 MAT3evalW(&r, m, &a);
260
261 Out[0] = _cmsClampWord((r.n[VX] + of->n[VX]) >> 1);
262 Out[1] = _cmsClampWord((r.n[VY] + of->n[VY]) >> 1);
263 Out[2] = _cmsClampWord((r.n[VZ] + of->n[VZ]) >> 1);
264 }
265
266
267 // XYZ to Lab, scaling first
268
269 static
XYZ2Lab(WORD In[],WORD Out[],LPWMAT3 m,LPWVEC3 of)270 void XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
271 {
272 WORD XYZ[3];
273
274 XYZ2XYZ(In, XYZ, m, of);
275 cmsXYZ2LabEncoded(XYZ, Out);
276 }
277
278 // Lab to XYZ, then scalling
279
280 static
Lab2XYZ(WORD In[],WORD Out[],LPWMAT3 m,LPWVEC3 of)281 void Lab2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
282 {
283 WORD XYZ[3];
284
285 cmsLab2XYZEncoded(In, XYZ);
286 XYZ2XYZ(XYZ, Out, m, of);
287 }
288
289 // Lab to XYZ, scalling and then, back to Lab
290
291 static
Lab2XYZ2Lab(WORD In[],WORD Out[],LPWMAT3 m,LPWVEC3 of)292 void Lab2XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
293 {
294 WORD XYZ[3], XYZ2[3];
295
296 cmsLab2XYZEncoded(In, XYZ);
297 XYZ2XYZ(XYZ, XYZ2, m, of);
298 cmsXYZ2LabEncoded(XYZ2, Out);
299 }
300
301 // ------------------------------------------------------------------
302
303 // Dispatcher for XYZ Relative LUT
304
305 static
FromXYZRelLUT(int Absolute,LPcmsCIEXYZ BlackPointIn,LPcmsCIEXYZ WhitePointIn,LPcmsCIEXYZ IlluminantIn,LPMAT3 ChromaticAdaptationMatrixIn,int Phase2,LPcmsCIEXYZ BlackPointOut,LPcmsCIEXYZ WhitePointOut,LPcmsCIEXYZ IlluminantOut,LPMAT3 ChromaticAdaptationMatrixOut,int DoBlackPointCompensation,double AdaptationState,_cmsADJFN * fn1,LPMAT3 m,LPVEC3 of)306 int FromXYZRelLUT(int Absolute,
307 LPcmsCIEXYZ BlackPointIn,
308 LPcmsCIEXYZ WhitePointIn,
309 LPcmsCIEXYZ IlluminantIn,
310 LPMAT3 ChromaticAdaptationMatrixIn,
311
312 int Phase2, LPcmsCIEXYZ BlackPointOut,
313 LPcmsCIEXYZ WhitePointOut,
314 LPcmsCIEXYZ IlluminantOut,
315 LPMAT3 ChromaticAdaptationMatrixOut,
316
317 int DoBlackPointCompensation,
318 double AdaptationState,
319 _cmsADJFN *fn1,
320 LPMAT3 m, LPVEC3 of)
321
322 {
323 switch (Phase2) {
324
325 // From relative XYZ to Relative XYZ.
326
327 case XYZRel:
328
329 if (Absolute)
330 {
331 // From input relative to absolute, and then
332 // back to output relative
333
334 Rel2RelStepAbsCoefs(AdaptationState,
335 BlackPointIn,
336 WhitePointIn,
337 IlluminantIn,
338 ChromaticAdaptationMatrixIn,
339 BlackPointOut,
340 WhitePointOut,
341 IlluminantOut,
342 ChromaticAdaptationMatrixOut,
343 m, of);
344 *fn1 = XYZ2XYZ;
345
346 }
347 else
348 {
349 // XYZ Relative to XYZ relative, no op required
350 *fn1 = NULL;
351 if (DoBlackPointCompensation) {
352
353 *fn1 = XYZ2XYZ;
354 ComputeBlackPointCompensationFactors(BlackPointIn,
355 WhitePointIn,
356 IlluminantIn,
357 BlackPointOut,
358 WhitePointOut,
359 IlluminantOut,
360 m, of);
361
362 }
363 }
364 break;
365
366
367 // From relative XYZ to Relative Lab
368
369 case LabRel:
370
371 // First pass XYZ to absolute, then to relative and
372 // finally to Lab. I use here D50 for output in order
373 // to prepare the "to Lab" conversion.
374
375 if (Absolute)
376 {
377
378 Rel2RelStepAbsCoefs(AdaptationState,
379 BlackPointIn,
380 WhitePointIn,
381 IlluminantIn,
382 ChromaticAdaptationMatrixIn,
383 BlackPointOut,
384 WhitePointOut,
385 IlluminantOut,
386 ChromaticAdaptationMatrixOut,
387 m, of);
388
389 *fn1 = XYZ2Lab;
390
391 }
392 else
393 {
394 // Just Convert to Lab
395
396 MAT3identity(m);
397 VEC3init(of, 0, 0, 0);
398 *fn1 = XYZ2Lab;
399
400 if (DoBlackPointCompensation) {
401
402 ComputeBlackPointCompensationFactors(BlackPointIn,
403 WhitePointIn,
404 IlluminantIn,
405 BlackPointOut,
406 WhitePointOut,
407 IlluminantOut,
408 m, of);
409 }
410 }
411 break;
412
413
414 default: return FALSE;
415 }
416
417 return TRUE;
418 }
419
420
421
422
423 // From Lab Relative type LUT
424
425 static
FromLabRelLUT(int Absolute,LPcmsCIEXYZ BlackPointIn,LPcmsCIEXYZ WhitePointIn,LPcmsCIEXYZ IlluminantIn,LPMAT3 ChromaticAdaptationMatrixIn,int Phase2,LPcmsCIEXYZ BlackPointOut,LPcmsCIEXYZ WhitePointOut,LPcmsCIEXYZ IlluminantOut,LPMAT3 ChromaticAdaptationMatrixOut,int DoBlackPointCompensation,double AdaptationState,_cmsADJFN * fn1,LPMAT3 m,LPVEC3 of)426 int FromLabRelLUT(int Absolute,
427 LPcmsCIEXYZ BlackPointIn,
428 LPcmsCIEXYZ WhitePointIn,
429 LPcmsCIEXYZ IlluminantIn,
430 LPMAT3 ChromaticAdaptationMatrixIn,
431
432 int Phase2, LPcmsCIEXYZ BlackPointOut,
433 LPcmsCIEXYZ WhitePointOut,
434 LPcmsCIEXYZ IlluminantOut,
435 LPMAT3 ChromaticAdaptationMatrixOut,
436
437 int DoBlackPointCompensation,
438 double AdaptationState,
439
440 _cmsADJFN *fn1,
441 LPMAT3 m, LPVEC3 of)
442 {
443
444 switch (Phase2) {
445
446 // From Lab Relative to XYZ Relative, very usual case
447
448 case XYZRel:
449
450 if (Absolute) { // Absolute intent
451
452 // From lab relative, to XYZ absolute, and then,
453 // back to XYZ relative
454
455 Rel2RelStepAbsCoefs(AdaptationState,
456 BlackPointIn,
457 WhitePointIn,
458 cmsD50_XYZ(),
459 ChromaticAdaptationMatrixIn,
460 BlackPointOut,
461 WhitePointOut,
462 IlluminantOut,
463 ChromaticAdaptationMatrixOut,
464 m, of);
465
466 *fn1 = Lab2XYZ;
467
468 }
469 else
470 {
471 // From Lab relative, to XYZ relative.
472
473 *fn1 = Lab2XYZ;
474 if (DoBlackPointCompensation) {
475
476 ComputeBlackPointCompensationFactors(BlackPointIn,
477 WhitePointIn,
478 IlluminantIn,
479 BlackPointOut,
480 WhitePointOut,
481 IlluminantOut,
482 m, of);
483
484 }
485 }
486 break;
487
488
489
490 case LabRel:
491
492 if (Absolute) {
493
494 // First pass to XYZ using the input illuminant
495 // * InIlluminant / D50, then to absolute. Then
496 // to relative, but for input
497
498 Rel2RelStepAbsCoefs(AdaptationState,
499 BlackPointIn,
500 WhitePointIn, IlluminantIn,
501 ChromaticAdaptationMatrixIn,
502 BlackPointOut,
503 WhitePointOut, cmsD50_XYZ(),
504 ChromaticAdaptationMatrixOut,
505 m, of);
506 *fn1 = Lab2XYZ2Lab;
507 }
508 else
509 { // Lab -> Lab relative don't need any adjust unless
510 // black point compensation
511
512 *fn1 = NULL;
513 if (DoBlackPointCompensation) {
514
515 *fn1 = Lab2XYZ2Lab;
516 ComputeBlackPointCompensationFactors(BlackPointIn,
517 WhitePointIn,
518 IlluminantIn,
519 BlackPointOut,
520 WhitePointOut,
521 IlluminantOut,
522 m, of);
523
524
525 }
526 }
527 break;
528
529
530 default: return FALSE;
531 }
532
533 return TRUE;
534 }
535
536
537 // This function does calculate the necessary conversion operations
538 // needed from transpassing data from a LUT to a LUT. The conversion
539 // is modeled as a pointer of function and two coefficients, a and b
540 // The function is actually called only if not null pointer is provided,
541 // and the two paramaters are passed in. There are several types of
542 // conversions, but basically they do a linear scalling and a interchange
543
544
545
546 // Main dispatcher
547
cmsChooseCnvrt(int Absolute,int Phase1,LPcmsCIEXYZ BlackPointIn,LPcmsCIEXYZ WhitePointIn,LPcmsCIEXYZ IlluminantIn,LPMAT3 ChromaticAdaptationMatrixIn,int Phase2,LPcmsCIEXYZ BlackPointOut,LPcmsCIEXYZ WhitePointOut,LPcmsCIEXYZ IlluminantOut,LPMAT3 ChromaticAdaptationMatrixOut,int DoBlackPointCompensation,double AdaptationState,_cmsADJFN * fn1,LPWMAT3 wm,LPWVEC3 wof)548 int cmsChooseCnvrt(int Absolute,
549 int Phase1, LPcmsCIEXYZ BlackPointIn,
550 LPcmsCIEXYZ WhitePointIn,
551 LPcmsCIEXYZ IlluminantIn,
552 LPMAT3 ChromaticAdaptationMatrixIn,
553
554 int Phase2, LPcmsCIEXYZ BlackPointOut,
555 LPcmsCIEXYZ WhitePointOut,
556 LPcmsCIEXYZ IlluminantOut,
557 LPMAT3 ChromaticAdaptationMatrixOut,
558
559 int DoBlackPointCompensation,
560 double AdaptationState,
561 _cmsADJFN *fn1,
562 LPWMAT3 wm, LPWVEC3 wof)
563 {
564
565 int rc;
566 MAT3 m;
567 VEC3 of;
568
569
570 MAT3identity(&m);
571 VEC3init(&of, 0, 0, 0);
572
573 switch (Phase1) {
574
575 // Input LUT is giving XYZ relative values.
576
577 case XYZRel: rc = FromXYZRelLUT(Absolute,
578 BlackPointIn,
579 WhitePointIn,
580 IlluminantIn,
581 ChromaticAdaptationMatrixIn,
582 Phase2,
583 BlackPointOut,
584 WhitePointOut,
585 IlluminantOut,
586 ChromaticAdaptationMatrixOut,
587 DoBlackPointCompensation,
588 AdaptationState,
589 fn1, &m, &of);
590 break;
591
592
593
594 // Input LUT is giving Lab relative values
595
596 case LabRel: rc = FromLabRelLUT(Absolute,
597 BlackPointIn,
598 WhitePointIn,
599 IlluminantIn,
600 ChromaticAdaptationMatrixIn,
601 Phase2,
602 BlackPointOut,
603 WhitePointOut,
604 IlluminantOut,
605 ChromaticAdaptationMatrixOut,
606 DoBlackPointCompensation,
607 AdaptationState,
608 fn1, &m, &of);
609 break;
610
611
612
613
614 // Unrecognized combination
615
616 default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error");
617 return FALSE;
618
619 }
620
621 MAT3toFix(wm, &m);
622 VEC3toFix(wof, &of);
623
624 // Do some optimization -- discard conversion if identity parameters.
625
626 if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) {
627
628 if (IdentityParameters(wm, wof))
629 *fn1 = NULL;
630 }
631
632
633 return rc;
634 }
635
636
637
638