25 // This file is available under and governed by the GNU General Public |
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. |
26 // License version 2 only, as published by the Free Software Foundation. |
27 // However, the following notice accompanied the original version of this |
27 // However, the following notice accompanied the original version of this |
28 // file: |
28 // file: |
29 // |
29 // |
30 // |
30 //--------------------------------------------------------------------------------- |
31 // Little cms |
31 // |
32 // Copyright (C) 1998-2007 Marti Maria |
32 // Little Color Management System |
|
33 // Copyright (c) 1998-2010 Marti Maria Saguer |
33 // |
34 // |
34 // Permission is hereby granted, free of charge, to any person obtaining |
35 // Permission is hereby granted, free of charge, to any person obtaining |
35 // a copy of this software and associated documentation files (the "Software"), |
36 // a copy of this software and associated documentation files (the "Software"), |
36 // to deal in the Software without restriction, including without limitation |
37 // to deal in the Software without restriction, including without limitation |
37 // the rights to use, copy, modify, merge, publish, distribute, sublicense, |
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense, |
46 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
47 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
48 // 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 // 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 // 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 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
51 |
52 // |
52 |
53 //--------------------------------------------------------------------------------- |
|
54 // |
|
55 |
|
56 #include "lcms2_internal.h" |
53 |
57 |
54 // CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. |
58 // CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. |
55 |
59 |
56 #include "lcms.h" |
|
57 |
|
58 |
|
59 LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC); |
|
60 LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel); |
|
61 LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); |
|
62 LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); |
|
63 |
|
64 |
|
65 // ---------- Implementation -------------------------------------------- |
60 // ---------- Implementation -------------------------------------------- |
66 |
61 |
67 typedef struct { |
62 typedef struct { |
68 |
63 |
69 double XYZ[3]; |
64 cmsFloat64Number XYZ[3]; |
70 double RGB[3]; |
65 cmsFloat64Number RGB[3]; |
71 double RGBc[3]; |
66 cmsFloat64Number RGBc[3]; |
72 double RGBp[3]; |
67 cmsFloat64Number RGBp[3]; |
73 double RGBpa[3]; |
68 cmsFloat64Number RGBpa[3]; |
74 double a, b, h, e, H, A, J, Q, s, t, C, M; |
69 cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; |
75 double abC[2]; |
70 cmsFloat64Number abC[2]; |
76 double abs[2]; |
71 cmsFloat64Number abs[2]; |
77 double abM[2]; |
72 cmsFloat64Number abM[2]; |
78 |
73 |
79 } CAM02COLOR, *LPCAM02COLOR; |
74 } CAM02COLOR; |
80 |
75 |
81 typedef struct { |
76 typedef struct { |
82 |
77 |
83 CAM02COLOR adoptedWhite; |
78 CAM02COLOR adoptedWhite; |
84 double LA, Yb; |
79 cmsFloat64Number LA, Yb; |
85 double F, c, Nc; |
80 cmsFloat64Number F, c, Nc; |
86 int surround; |
81 cmsUInt32Number surround; |
87 double n, Nbb, Ncb, z, FL, D; |
82 cmsFloat64Number n, Nbb, Ncb, z, FL, D; |
88 |
83 |
89 } cmsCIECAM02, *LPcmsCIECAM02; |
84 cmsContext ContextID; |
90 |
85 |
91 |
86 } cmsCIECAM02; |
92 static |
87 |
93 double compute_n(LPcmsCIECAM02 pMod) |
88 |
94 { |
89 static |
95 return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); |
90 cmsFloat64Number compute_n(cmsCIECAM02* pMod) |
96 } |
91 { |
97 |
92 return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); |
98 static |
93 } |
99 double compute_z(LPcmsCIECAM02 pMod) |
94 |
100 { |
95 static |
101 return(1.48 + pow(pMod -> n, 0.5)); |
96 cmsFloat64Number compute_z(cmsCIECAM02* pMod) |
102 } |
97 { |
103 |
98 return (1.48 + pow(pMod -> n, 0.5)); |
104 static |
99 } |
105 double computeNbb(LPcmsCIECAM02 pMod) |
100 |
106 { |
101 static |
107 return(0.725 * pow((1.0 / pMod -> n), 0.2)); |
102 cmsFloat64Number computeNbb(cmsCIECAM02* pMod) |
108 } |
103 { |
109 |
104 return (0.725 * pow((1.0 / pMod -> n), 0.2)); |
110 static |
105 } |
111 double computeFL(LPcmsCIECAM02 pMod) |
106 |
112 { |
107 static |
113 double k, FL; |
108 cmsFloat64Number computeFL(cmsCIECAM02* pMod) |
|
109 { |
|
110 cmsFloat64Number k, FL; |
114 |
111 |
115 k = 1.0 / ((5.0 * pMod->LA) + 1.0); |
112 k = 1.0 / ((5.0 * pMod->LA) + 1.0); |
116 FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * |
113 FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * |
117 (pow((1.0 - pow(k, 4.0)), 2.0)) * |
114 (pow((1.0 - pow(k, 4.0)), 2.0)) * |
118 (pow((5.0 * pMod->LA), (1.0 / 3.0))); |
115 (pow((5.0 * pMod->LA), (1.0 / 3.0))); |
119 |
116 |
120 return FL; |
117 return FL; |
121 } |
118 } |
122 |
119 |
123 static |
120 static |
124 double computeD(LPcmsCIECAM02 pMod) |
121 cmsFloat64Number computeD(cmsCIECAM02* pMod) |
125 { |
122 { |
126 double D; |
123 cmsFloat64Number D; |
127 |
124 |
128 D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); |
125 D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); |
129 |
126 |
130 return D; |
127 return D; |
131 } |
128 } |
272 return clr; |
268 return clr; |
273 } |
269 } |
274 |
270 |
275 |
271 |
276 static |
272 static |
277 CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod) |
273 CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) |
278 { |
274 { |
279 |
275 |
280 double t, e, p1, p2, p3, p4, p5, hr, d2r; |
276 cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; |
281 d2r = 3.141592654 / 180.0; |
277 d2r = 3.141592654 / 180.0; |
282 |
278 |
283 t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * |
279 t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * |
284 (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), |
280 (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), |
285 (1.0 / 0.9) ); |
281 (1.0 / 0.9) ); |
360 M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; |
356 M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; |
361 |
357 |
362 clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); |
358 clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); |
363 clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); |
359 clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); |
364 clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); |
360 clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); |
365 return (clr); |
361 return clr; |
366 } |
362 } |
367 |
363 |
368 |
364 |
369 static |
365 static |
370 CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod) |
366 CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) |
371 { |
367 { |
372 int i; |
368 cmsUInt32Number i; |
373 for (i = 0; i < 3; i++) { |
369 for (i = 0; i < 3; i++) { |
374 clr.RGB[i] = clr.RGBc[i] / |
370 clr.RGB[i] = clr.RGBc[i] / |
375 ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); |
371 ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); |
376 } |
372 } |
377 return(clr); |
373 return clr; |
378 } |
374 } |
379 |
375 |
380 |
376 |
381 static |
377 static |
382 CAM02COLOR CAT02toXYZ(CAM02COLOR clr) |
378 CAM02COLOR CAT02toXYZ(CAM02COLOR clr) |
383 { |
379 { |
384 clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); |
380 clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); |
385 clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); |
381 clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); |
386 clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); |
382 clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); |
387 |
383 |
388 return(clr); |
384 return clr; |
389 } |
385 } |
390 |
386 |
391 |
387 |
392 |
388 cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) |
393 |
389 { |
394 LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC) |
390 cmsCIECAM02* lpMod; |
395 { |
391 |
396 LPcmsCIECAM02 lpMod; |
392 _cmsAssert(pVC != NULL); |
397 |
393 |
398 |
394 if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { |
399 if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) { |
395 return NULL; |
400 return (LCMSHANDLE) NULL; |
396 } |
401 } |
397 |
402 |
398 lpMod ->ContextID = ContextID; |
403 |
|
404 ZeroMemory(lpMod, sizeof(cmsCIECAM02)); |
|
405 |
399 |
406 lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; |
400 lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; |
407 lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; |
401 lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; |
408 lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; |
402 lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; |
409 |
403 |
412 lpMod -> D = pVC ->D_value; |
406 lpMod -> D = pVC ->D_value; |
413 lpMod -> surround = pVC ->surround; |
407 lpMod -> surround = pVC ->surround; |
414 |
408 |
415 switch (lpMod -> surround) { |
409 switch (lpMod -> surround) { |
416 |
410 |
417 case AVG_SURROUND_4: |
411 |
418 lpMod->F = 1.0; // Not included in CAM02 |
412 case CUTSHEET_SURROUND: |
419 lpMod->c = 0.69; |
413 lpMod->F = 0.8; |
420 lpMod->Nc = 1.0; |
414 lpMod->c = 0.41; |
421 break; |
415 lpMod->Nc = 0.8; |
422 |
416 break; |
423 case CUTSHEET_SURROUND: |
417 |
424 lpMod->F = 0.8; |
418 case DARK_SURROUND: |
425 lpMod->c = 0.41; |
419 lpMod -> F = 0.8; |
426 lpMod->Nc = 0.8; |
420 lpMod -> c = 0.525; |
427 break; |
421 lpMod -> Nc = 0.8; |
428 |
422 break; |
429 case DARK_SURROUND: |
423 |
430 lpMod -> F = 0.8; |
424 case DIM_SURROUND: |
431 lpMod -> c = 0.525; |
425 lpMod -> F = 0.9; |
432 lpMod -> Nc = 0.8; |
426 lpMod -> c = 0.59; |
433 break; |
427 lpMod -> Nc = 0.95; |
434 |
428 break; |
435 |
429 |
436 case DIM_SURROUND: |
430 default: |
437 lpMod -> F = 0.9; |
431 // Average surround |
438 lpMod -> c = 0.59; |
432 lpMod -> F = 1.0; |
439 lpMod -> Nc = 0.95; |
433 lpMod -> c = 0.69; |
440 break; |
434 lpMod -> Nc = 1.0; |
441 |
|
442 default: |
|
443 // Average surround |
|
444 lpMod -> F = 1.0; |
|
445 lpMod -> c = 0.69; |
|
446 lpMod -> Nc = 1.0; |
|
447 } |
435 } |
448 |
436 |
449 lpMod -> n = compute_n(lpMod); |
437 lpMod -> n = compute_n(lpMod); |
450 lpMod -> z = compute_z(lpMod); |
438 lpMod -> z = compute_z(lpMod); |
451 lpMod -> Nbb = computeNbb(lpMod); |
439 lpMod -> Nbb = computeNbb(lpMod); |
452 lpMod -> FL = computeFL(lpMod); |
440 lpMod -> FL = computeFL(lpMod); |
453 |
441 |
454 if (lpMod -> D == D_CALCULATE || |
442 if (lpMod -> D == D_CALCULATE) { |
455 lpMod -> D == D_CALCULATE_DISCOUNT) { |
443 lpMod -> D = computeD(lpMod); |
456 |
|
457 lpMod -> D = computeD(lpMod); |
|
458 } |
444 } |
459 |
445 |
460 lpMod -> Ncb = lpMod -> Nbb; |
446 lpMod -> Ncb = lpMod -> Nbb; |
461 |
447 |
462 lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); |
448 lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); |
463 lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); |
449 lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); |
464 lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); |
450 lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); |
465 lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); |
451 lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); |
466 |
452 |
467 return (LCMSHANDLE) lpMod; |
453 return (cmsHANDLE) lpMod; |
468 |
454 |
469 } |
455 } |
470 |
456 |
471 void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel) |
457 void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) |
472 { |
458 { |
473 LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel; |
459 cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; |
474 if (lpMod) _cmsFree(lpMod); |
460 |
475 } |
461 if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); |
476 |
462 } |
477 |
463 |
478 void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut) |
464 |
|
465 void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) |
479 { |
466 { |
480 CAM02COLOR clr; |
467 CAM02COLOR clr; |
481 LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel; |
468 cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; |
|
469 |
|
470 _cmsAssert(lpMod != NULL); |
|
471 _cmsAssert(pIn != NULL); |
|
472 _cmsAssert(pOut != NULL); |
482 |
473 |
483 clr.XYZ[0] = pIn ->X; |
474 clr.XYZ[0] = pIn ->X; |
484 clr.XYZ[1] = pIn ->Y; |
475 clr.XYZ[1] = pIn ->Y; |
485 clr.XYZ[2] = pIn ->Z; |
476 clr.XYZ[2] = pIn ->Z; |
486 |
477 |