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 // CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.
30*3ac0a46fSAndroid Build Coastguard Worker
31*3ac0a46fSAndroid Build Coastguard Worker // ---------- Implementation --------------------------------------------
32*3ac0a46fSAndroid Build Coastguard Worker
33*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
34*3ac0a46fSAndroid Build Coastguard Worker
35*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number XYZ[3];
36*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number RGB[3];
37*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number RGBc[3];
38*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number RGBp[3];
39*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number RGBpa[3];
40*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M;
41*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number abC[2];
42*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number abs[2];
43*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number abM[2];
44*3ac0a46fSAndroid Build Coastguard Worker
45*3ac0a46fSAndroid Build Coastguard Worker } CAM02COLOR;
46*3ac0a46fSAndroid Build Coastguard Worker
47*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
48*3ac0a46fSAndroid Build Coastguard Worker
49*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR adoptedWhite;
50*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number LA, Yb;
51*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number F, c, Nc;
52*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number surround;
53*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number n, Nbb, Ncb, z, FL, D;
54*3ac0a46fSAndroid Build Coastguard Worker
55*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID;
56*3ac0a46fSAndroid Build Coastguard Worker
57*3ac0a46fSAndroid Build Coastguard Worker } cmsCIECAM02;
58*3ac0a46fSAndroid Build Coastguard Worker
59*3ac0a46fSAndroid Build Coastguard Worker
60*3ac0a46fSAndroid Build Coastguard Worker static
compute_n(cmsCIECAM02 * pMod)61*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number compute_n(cmsCIECAM02* pMod)
62*3ac0a46fSAndroid Build Coastguard Worker {
63*3ac0a46fSAndroid Build Coastguard Worker return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
64*3ac0a46fSAndroid Build Coastguard Worker }
65*3ac0a46fSAndroid Build Coastguard Worker
66*3ac0a46fSAndroid Build Coastguard Worker static
compute_z(cmsCIECAM02 * pMod)67*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number compute_z(cmsCIECAM02* pMod)
68*3ac0a46fSAndroid Build Coastguard Worker {
69*3ac0a46fSAndroid Build Coastguard Worker return (1.48 + pow(pMod -> n, 0.5));
70*3ac0a46fSAndroid Build Coastguard Worker }
71*3ac0a46fSAndroid Build Coastguard Worker
72*3ac0a46fSAndroid Build Coastguard Worker static
computeNbb(cmsCIECAM02 * pMod)73*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number computeNbb(cmsCIECAM02* pMod)
74*3ac0a46fSAndroid Build Coastguard Worker {
75*3ac0a46fSAndroid Build Coastguard Worker return (0.725 * pow((1.0 / pMod -> n), 0.2));
76*3ac0a46fSAndroid Build Coastguard Worker }
77*3ac0a46fSAndroid Build Coastguard Worker
78*3ac0a46fSAndroid Build Coastguard Worker static
computeFL(cmsCIECAM02 * pMod)79*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number computeFL(cmsCIECAM02* pMod)
80*3ac0a46fSAndroid Build Coastguard Worker {
81*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number k, FL;
82*3ac0a46fSAndroid Build Coastguard Worker
83*3ac0a46fSAndroid Build Coastguard Worker k = 1.0 / ((5.0 * pMod->LA) + 1.0);
84*3ac0a46fSAndroid Build Coastguard Worker FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *
85*3ac0a46fSAndroid Build Coastguard Worker (pow((1.0 - pow(k, 4.0)), 2.0)) *
86*3ac0a46fSAndroid Build Coastguard Worker (pow((5.0 * pMod->LA), (1.0 / 3.0)));
87*3ac0a46fSAndroid Build Coastguard Worker
88*3ac0a46fSAndroid Build Coastguard Worker return FL;
89*3ac0a46fSAndroid Build Coastguard Worker }
90*3ac0a46fSAndroid Build Coastguard Worker
91*3ac0a46fSAndroid Build Coastguard Worker static
computeD(cmsCIECAM02 * pMod)92*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number computeD(cmsCIECAM02* pMod)
93*3ac0a46fSAndroid Build Coastguard Worker {
94*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number D;
95*3ac0a46fSAndroid Build Coastguard Worker
96*3ac0a46fSAndroid Build Coastguard Worker D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));
97*3ac0a46fSAndroid Build Coastguard Worker
98*3ac0a46fSAndroid Build Coastguard Worker return D;
99*3ac0a46fSAndroid Build Coastguard Worker }
100*3ac0a46fSAndroid Build Coastguard Worker
101*3ac0a46fSAndroid Build Coastguard Worker
102*3ac0a46fSAndroid Build Coastguard Worker static
XYZtoCAT02(CAM02COLOR clr)103*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR XYZtoCAT02(CAM02COLOR clr)
104*3ac0a46fSAndroid Build Coastguard Worker {
105*3ac0a46fSAndroid Build Coastguard Worker clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624);
106*3ac0a46fSAndroid Build Coastguard Worker clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061);
107*3ac0a46fSAndroid Build Coastguard Worker clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834);
108*3ac0a46fSAndroid Build Coastguard Worker
109*3ac0a46fSAndroid Build Coastguard Worker return clr;
110*3ac0a46fSAndroid Build Coastguard Worker }
111*3ac0a46fSAndroid Build Coastguard Worker
112*3ac0a46fSAndroid Build Coastguard Worker static
ChromaticAdaptation(CAM02COLOR clr,cmsCIECAM02 * pMod)113*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
114*3ac0a46fSAndroid Build Coastguard Worker {
115*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
116*3ac0a46fSAndroid Build Coastguard Worker
117*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < 3; i++) {
118*3ac0a46fSAndroid Build Coastguard Worker clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *
119*3ac0a46fSAndroid Build Coastguard Worker (pMod->D / pMod -> adoptedWhite.RGB[i])) +
120*3ac0a46fSAndroid Build Coastguard Worker (1.0 - pMod->D)) * clr.RGB[i];
121*3ac0a46fSAndroid Build Coastguard Worker }
122*3ac0a46fSAndroid Build Coastguard Worker
123*3ac0a46fSAndroid Build Coastguard Worker return clr;
124*3ac0a46fSAndroid Build Coastguard Worker }
125*3ac0a46fSAndroid Build Coastguard Worker
126*3ac0a46fSAndroid Build Coastguard Worker
127*3ac0a46fSAndroid Build Coastguard Worker static
CAT02toHPE(CAM02COLOR clr)128*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR CAT02toHPE(CAM02COLOR clr)
129*3ac0a46fSAndroid Build Coastguard Worker {
130*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number M[9];
131*3ac0a46fSAndroid Build Coastguard Worker
132*3ac0a46fSAndroid Build Coastguard Worker M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628));
133*3ac0a46fSAndroid Build Coastguard Worker M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698));
134*3ac0a46fSAndroid Build Coastguard Worker M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326));
135*3ac0a46fSAndroid Build Coastguard Worker M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628));
136*3ac0a46fSAndroid Build Coastguard Worker M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698));
137*3ac0a46fSAndroid Build Coastguard Worker M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326));
138*3ac0a46fSAndroid Build Coastguard Worker M[6] =(-0.009628);
139*3ac0a46fSAndroid Build Coastguard Worker M[7] =(-0.005698);
140*3ac0a46fSAndroid Build Coastguard Worker M[8] =( 1.015326);
141*3ac0a46fSAndroid Build Coastguard Worker
142*3ac0a46fSAndroid Build Coastguard Worker clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]);
143*3ac0a46fSAndroid Build Coastguard Worker clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]);
144*3ac0a46fSAndroid Build Coastguard Worker clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]);
145*3ac0a46fSAndroid Build Coastguard Worker
146*3ac0a46fSAndroid Build Coastguard Worker return clr;
147*3ac0a46fSAndroid Build Coastguard Worker }
148*3ac0a46fSAndroid Build Coastguard Worker
149*3ac0a46fSAndroid Build Coastguard Worker static
NonlinearCompression(CAM02COLOR clr,cmsCIECAM02 * pMod)150*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod)
151*3ac0a46fSAndroid Build Coastguard Worker {
152*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
153*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number temp;
154*3ac0a46fSAndroid Build Coastguard Worker
155*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < 3; i++) {
156*3ac0a46fSAndroid Build Coastguard Worker if (clr.RGBp[i] < 0) {
157*3ac0a46fSAndroid Build Coastguard Worker
158*3ac0a46fSAndroid Build Coastguard Worker temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42);
159*3ac0a46fSAndroid Build Coastguard Worker clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1;
160*3ac0a46fSAndroid Build Coastguard Worker }
161*3ac0a46fSAndroid Build Coastguard Worker else {
162*3ac0a46fSAndroid Build Coastguard Worker temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42);
163*3ac0a46fSAndroid Build Coastguard Worker clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1;
164*3ac0a46fSAndroid Build Coastguard Worker }
165*3ac0a46fSAndroid Build Coastguard Worker }
166*3ac0a46fSAndroid Build Coastguard Worker
167*3ac0a46fSAndroid Build Coastguard Worker clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] +
168*3ac0a46fSAndroid Build Coastguard Worker (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb;
169*3ac0a46fSAndroid Build Coastguard Worker
170*3ac0a46fSAndroid Build Coastguard Worker return clr;
171*3ac0a46fSAndroid Build Coastguard Worker }
172*3ac0a46fSAndroid Build Coastguard Worker
173*3ac0a46fSAndroid Build Coastguard Worker static
ComputeCorrelates(CAM02COLOR clr,cmsCIECAM02 * pMod)174*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
175*3ac0a46fSAndroid Build Coastguard Worker {
176*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number a, b, temp, e, t, r2d, d2r;
177*3ac0a46fSAndroid Build Coastguard Worker
178*3ac0a46fSAndroid Build Coastguard Worker a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0);
179*3ac0a46fSAndroid Build Coastguard Worker b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0;
180*3ac0a46fSAndroid Build Coastguard Worker
181*3ac0a46fSAndroid Build Coastguard Worker r2d = (180.0 / 3.141592654);
182*3ac0a46fSAndroid Build Coastguard Worker if (a == 0) {
183*3ac0a46fSAndroid Build Coastguard Worker if (b == 0) clr.h = 0;
184*3ac0a46fSAndroid Build Coastguard Worker else if (b > 0) clr.h = 90;
185*3ac0a46fSAndroid Build Coastguard Worker else clr.h = 270;
186*3ac0a46fSAndroid Build Coastguard Worker }
187*3ac0a46fSAndroid Build Coastguard Worker else if (a > 0) {
188*3ac0a46fSAndroid Build Coastguard Worker temp = b / a;
189*3ac0a46fSAndroid Build Coastguard Worker if (b > 0) clr.h = (r2d * atan(temp));
190*3ac0a46fSAndroid Build Coastguard Worker else if (b == 0) clr.h = 0;
191*3ac0a46fSAndroid Build Coastguard Worker else clr.h = (r2d * atan(temp)) + 360;
192*3ac0a46fSAndroid Build Coastguard Worker }
193*3ac0a46fSAndroid Build Coastguard Worker else {
194*3ac0a46fSAndroid Build Coastguard Worker temp = b / a;
195*3ac0a46fSAndroid Build Coastguard Worker clr.h = (r2d * atan(temp)) + 180;
196*3ac0a46fSAndroid Build Coastguard Worker }
197*3ac0a46fSAndroid Build Coastguard Worker
198*3ac0a46fSAndroid Build Coastguard Worker d2r = (3.141592654 / 180.0);
199*3ac0a46fSAndroid Build Coastguard Worker e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
200*3ac0a46fSAndroid Build Coastguard Worker (cos((clr.h * d2r + 2.0)) + 3.8);
201*3ac0a46fSAndroid Build Coastguard Worker
202*3ac0a46fSAndroid Build Coastguard Worker if (clr.h < 20.14) {
203*3ac0a46fSAndroid Build Coastguard Worker temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8);
204*3ac0a46fSAndroid Build Coastguard Worker clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp;
205*3ac0a46fSAndroid Build Coastguard Worker }
206*3ac0a46fSAndroid Build Coastguard Worker else if (clr.h < 90.0) {
207*3ac0a46fSAndroid Build Coastguard Worker temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7);
208*3ac0a46fSAndroid Build Coastguard Worker clr.H = (100*((clr.h - 20.14)/0.8)) / temp;
209*3ac0a46fSAndroid Build Coastguard Worker }
210*3ac0a46fSAndroid Build Coastguard Worker else if (clr.h < 164.25) {
211*3ac0a46fSAndroid Build Coastguard Worker temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0);
212*3ac0a46fSAndroid Build Coastguard Worker clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp);
213*3ac0a46fSAndroid Build Coastguard Worker }
214*3ac0a46fSAndroid Build Coastguard Worker else if (clr.h < 237.53) {
215*3ac0a46fSAndroid Build Coastguard Worker temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2);
216*3ac0a46fSAndroid Build Coastguard Worker clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp);
217*3ac0a46fSAndroid Build Coastguard Worker }
218*3ac0a46fSAndroid Build Coastguard Worker else {
219*3ac0a46fSAndroid Build Coastguard Worker temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8);
220*3ac0a46fSAndroid Build Coastguard Worker clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp);
221*3ac0a46fSAndroid Build Coastguard Worker }
222*3ac0a46fSAndroid Build Coastguard Worker
223*3ac0a46fSAndroid Build Coastguard Worker clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A),
224*3ac0a46fSAndroid Build Coastguard Worker (pMod->c * pMod->z));
225*3ac0a46fSAndroid Build Coastguard Worker
226*3ac0a46fSAndroid Build Coastguard Worker clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) *
227*3ac0a46fSAndroid Build Coastguard Worker (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25);
228*3ac0a46fSAndroid Build Coastguard Worker
229*3ac0a46fSAndroid Build Coastguard Worker t = (e * pow(((a * a) + (b * b)), 0.5)) /
230*3ac0a46fSAndroid Build Coastguard Worker (clr.RGBpa[0] + clr.RGBpa[1] +
231*3ac0a46fSAndroid Build Coastguard Worker ((21.0 / 20.0) * clr.RGBpa[2]));
232*3ac0a46fSAndroid Build Coastguard Worker
233*3ac0a46fSAndroid Build Coastguard Worker clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) *
234*3ac0a46fSAndroid Build Coastguard Worker pow((1.64 - pow(0.29, pMod->n)), 0.73);
235*3ac0a46fSAndroid Build Coastguard Worker
236*3ac0a46fSAndroid Build Coastguard Worker clr.M = clr.C * pow(pMod->FL, 0.25);
237*3ac0a46fSAndroid Build Coastguard Worker clr.s = 100.0 * pow((clr.M / clr.Q), 0.5);
238*3ac0a46fSAndroid Build Coastguard Worker
239*3ac0a46fSAndroid Build Coastguard Worker return clr;
240*3ac0a46fSAndroid Build Coastguard Worker }
241*3ac0a46fSAndroid Build Coastguard Worker
242*3ac0a46fSAndroid Build Coastguard Worker
243*3ac0a46fSAndroid Build Coastguard Worker static
InverseCorrelates(CAM02COLOR clr,cmsCIECAM02 * pMod)244*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
245*3ac0a46fSAndroid Build Coastguard Worker {
246*3ac0a46fSAndroid Build Coastguard Worker
247*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r;
248*3ac0a46fSAndroid Build Coastguard Worker d2r = 3.141592654 / 180.0;
249*3ac0a46fSAndroid Build Coastguard Worker
250*3ac0a46fSAndroid Build Coastguard Worker t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) *
251*3ac0a46fSAndroid Build Coastguard Worker (pow((1.64 - pow(0.29, pMod->n)), 0.73)))),
252*3ac0a46fSAndroid Build Coastguard Worker (1.0 / 0.9) );
253*3ac0a46fSAndroid Build Coastguard Worker e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
254*3ac0a46fSAndroid Build Coastguard Worker (cos((clr.h * d2r + 2.0)) + 3.8);
255*3ac0a46fSAndroid Build Coastguard Worker
256*3ac0a46fSAndroid Build Coastguard Worker clr.A = pMod->adoptedWhite.A * pow(
257*3ac0a46fSAndroid Build Coastguard Worker (clr.J / 100.0),
258*3ac0a46fSAndroid Build Coastguard Worker (1.0 / (pMod->c * pMod->z)));
259*3ac0a46fSAndroid Build Coastguard Worker
260*3ac0a46fSAndroid Build Coastguard Worker p1 = e / t;
261*3ac0a46fSAndroid Build Coastguard Worker p2 = (clr.A / pMod->Nbb) + 0.305;
262*3ac0a46fSAndroid Build Coastguard Worker p3 = 21.0 / 20.0;
263*3ac0a46fSAndroid Build Coastguard Worker
264*3ac0a46fSAndroid Build Coastguard Worker hr = clr.h * d2r;
265*3ac0a46fSAndroid Build Coastguard Worker
266*3ac0a46fSAndroid Build Coastguard Worker if (fabs(sin(hr)) >= fabs(cos(hr))) {
267*3ac0a46fSAndroid Build Coastguard Worker p4 = p1 / sin(hr);
268*3ac0a46fSAndroid Build Coastguard Worker clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
269*3ac0a46fSAndroid Build Coastguard Worker (p4 + (2.0 + p3) * (220.0 / 1403.0) *
270*3ac0a46fSAndroid Build Coastguard Worker (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
271*3ac0a46fSAndroid Build Coastguard Worker p3 * (6300.0 / 1403.0));
272*3ac0a46fSAndroid Build Coastguard Worker clr.a = clr.b * (cos(hr) / sin(hr));
273*3ac0a46fSAndroid Build Coastguard Worker }
274*3ac0a46fSAndroid Build Coastguard Worker else {
275*3ac0a46fSAndroid Build Coastguard Worker p5 = p1 / cos(hr);
276*3ac0a46fSAndroid Build Coastguard Worker clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
277*3ac0a46fSAndroid Build Coastguard Worker (p5 + (2.0 + p3) * (220.0 / 1403.0) -
278*3ac0a46fSAndroid Build Coastguard Worker ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
279*3ac0a46fSAndroid Build Coastguard Worker (sin(hr) / cos(hr)));
280*3ac0a46fSAndroid Build Coastguard Worker clr.b = clr.a * (sin(hr) / cos(hr));
281*3ac0a46fSAndroid Build Coastguard Worker }
282*3ac0a46fSAndroid Build Coastguard Worker
283*3ac0a46fSAndroid Build Coastguard Worker clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
284*3ac0a46fSAndroid Build Coastguard Worker ((451.0 / 1403.0) * clr.a) +
285*3ac0a46fSAndroid Build Coastguard Worker ((288.0 / 1403.0) * clr.b);
286*3ac0a46fSAndroid Build Coastguard Worker clr.RGBpa[1] = ((460.0 / 1403.0) * p2) -
287*3ac0a46fSAndroid Build Coastguard Worker ((891.0 / 1403.0) * clr.a) -
288*3ac0a46fSAndroid Build Coastguard Worker ((261.0 / 1403.0) * clr.b);
289*3ac0a46fSAndroid Build Coastguard Worker clr.RGBpa[2] = ((460.0 / 1403.0) * p2) -
290*3ac0a46fSAndroid Build Coastguard Worker ((220.0 / 1403.0) * clr.a) -
291*3ac0a46fSAndroid Build Coastguard Worker ((6300.0 / 1403.0) * clr.b);
292*3ac0a46fSAndroid Build Coastguard Worker
293*3ac0a46fSAndroid Build Coastguard Worker return clr;
294*3ac0a46fSAndroid Build Coastguard Worker }
295*3ac0a46fSAndroid Build Coastguard Worker
296*3ac0a46fSAndroid Build Coastguard Worker static
InverseNonlinearity(CAM02COLOR clr,cmsCIECAM02 * pMod)297*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod)
298*3ac0a46fSAndroid Build Coastguard Worker {
299*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
300*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number c1;
301*3ac0a46fSAndroid Build Coastguard Worker
302*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < 3; i++) {
303*3ac0a46fSAndroid Build Coastguard Worker if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;
304*3ac0a46fSAndroid Build Coastguard Worker else c1 = 1;
305*3ac0a46fSAndroid Build Coastguard Worker clr.RGBp[i] = c1 * (100.0 / pMod->FL) *
306*3ac0a46fSAndroid Build Coastguard Worker pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) /
307*3ac0a46fSAndroid Build Coastguard Worker (400.0 - fabs(clr.RGBpa[i] - 0.1))),
308*3ac0a46fSAndroid Build Coastguard Worker (1.0 / 0.42));
309*3ac0a46fSAndroid Build Coastguard Worker }
310*3ac0a46fSAndroid Build Coastguard Worker
311*3ac0a46fSAndroid Build Coastguard Worker return clr;
312*3ac0a46fSAndroid Build Coastguard Worker }
313*3ac0a46fSAndroid Build Coastguard Worker
314*3ac0a46fSAndroid Build Coastguard Worker static
HPEtoCAT02(CAM02COLOR clr)315*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
316*3ac0a46fSAndroid Build Coastguard Worker {
317*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number M[9];
318*3ac0a46fSAndroid Build Coastguard Worker
319*3ac0a46fSAndroid Build Coastguard Worker M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));
320*3ac0a46fSAndroid Build Coastguard Worker M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));
321*3ac0a46fSAndroid Build Coastguard Worker M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624);
322*3ac0a46fSAndroid Build Coastguard Worker M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950));
323*3ac0a46fSAndroid Build Coastguard Worker M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054));
324*3ac0a46fSAndroid Build Coastguard Worker M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061);
325*3ac0a46fSAndroid Build Coastguard Worker M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950));
326*3ac0a46fSAndroid Build Coastguard Worker M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));
327*3ac0a46fSAndroid Build Coastguard Worker M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);
328*3ac0a46fSAndroid Build Coastguard Worker
329*3ac0a46fSAndroid Build Coastguard Worker clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);
330*3ac0a46fSAndroid Build Coastguard Worker clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);
331*3ac0a46fSAndroid Build Coastguard Worker clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]);
332*3ac0a46fSAndroid Build Coastguard Worker return clr;
333*3ac0a46fSAndroid Build Coastguard Worker }
334*3ac0a46fSAndroid Build Coastguard Worker
335*3ac0a46fSAndroid Build Coastguard Worker
336*3ac0a46fSAndroid Build Coastguard Worker static
InverseChromaticAdaptation(CAM02COLOR clr,cmsCIECAM02 * pMod)337*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
338*3ac0a46fSAndroid Build Coastguard Worker {
339*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
340*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < 3; i++) {
341*3ac0a46fSAndroid Build Coastguard Worker clr.RGB[i] = clr.RGBc[i] /
342*3ac0a46fSAndroid Build Coastguard Worker ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D);
343*3ac0a46fSAndroid Build Coastguard Worker }
344*3ac0a46fSAndroid Build Coastguard Worker return clr;
345*3ac0a46fSAndroid Build Coastguard Worker }
346*3ac0a46fSAndroid Build Coastguard Worker
347*3ac0a46fSAndroid Build Coastguard Worker
348*3ac0a46fSAndroid Build Coastguard Worker static
CAT02toXYZ(CAM02COLOR clr)349*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
350*3ac0a46fSAndroid Build Coastguard Worker {
351*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745);
352*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098);
353*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326);
354*3ac0a46fSAndroid Build Coastguard Worker
355*3ac0a46fSAndroid Build Coastguard Worker return clr;
356*3ac0a46fSAndroid Build Coastguard Worker }
357*3ac0a46fSAndroid Build Coastguard Worker
358*3ac0a46fSAndroid Build Coastguard Worker
cmsCIECAM02Init(cmsContext ContextID,const cmsViewingConditions * pVC)359*3ac0a46fSAndroid Build Coastguard Worker cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC)
360*3ac0a46fSAndroid Build Coastguard Worker {
361*3ac0a46fSAndroid Build Coastguard Worker cmsCIECAM02* lpMod;
362*3ac0a46fSAndroid Build Coastguard Worker
363*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(pVC != NULL);
364*3ac0a46fSAndroid Build Coastguard Worker
365*3ac0a46fSAndroid Build Coastguard Worker if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
366*3ac0a46fSAndroid Build Coastguard Worker return NULL;
367*3ac0a46fSAndroid Build Coastguard Worker }
368*3ac0a46fSAndroid Build Coastguard Worker
369*3ac0a46fSAndroid Build Coastguard Worker lpMod ->ContextID = ContextID;
370*3ac0a46fSAndroid Build Coastguard Worker
371*3ac0a46fSAndroid Build Coastguard Worker lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
372*3ac0a46fSAndroid Build Coastguard Worker lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
373*3ac0a46fSAndroid Build Coastguard Worker lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
374*3ac0a46fSAndroid Build Coastguard Worker
375*3ac0a46fSAndroid Build Coastguard Worker lpMod -> LA = pVC ->La;
376*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Yb = pVC ->Yb;
377*3ac0a46fSAndroid Build Coastguard Worker lpMod -> D = pVC ->D_value;
378*3ac0a46fSAndroid Build Coastguard Worker lpMod -> surround = pVC ->surround;
379*3ac0a46fSAndroid Build Coastguard Worker
380*3ac0a46fSAndroid Build Coastguard Worker switch (lpMod -> surround) {
381*3ac0a46fSAndroid Build Coastguard Worker
382*3ac0a46fSAndroid Build Coastguard Worker
383*3ac0a46fSAndroid Build Coastguard Worker case CUTSHEET_SURROUND:
384*3ac0a46fSAndroid Build Coastguard Worker lpMod->F = 0.8;
385*3ac0a46fSAndroid Build Coastguard Worker lpMod->c = 0.41;
386*3ac0a46fSAndroid Build Coastguard Worker lpMod->Nc = 0.8;
387*3ac0a46fSAndroid Build Coastguard Worker break;
388*3ac0a46fSAndroid Build Coastguard Worker
389*3ac0a46fSAndroid Build Coastguard Worker case DARK_SURROUND:
390*3ac0a46fSAndroid Build Coastguard Worker lpMod -> F = 0.8;
391*3ac0a46fSAndroid Build Coastguard Worker lpMod -> c = 0.525;
392*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Nc = 0.8;
393*3ac0a46fSAndroid Build Coastguard Worker break;
394*3ac0a46fSAndroid Build Coastguard Worker
395*3ac0a46fSAndroid Build Coastguard Worker case DIM_SURROUND:
396*3ac0a46fSAndroid Build Coastguard Worker lpMod -> F = 0.9;
397*3ac0a46fSAndroid Build Coastguard Worker lpMod -> c = 0.59;
398*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Nc = 0.95;
399*3ac0a46fSAndroid Build Coastguard Worker break;
400*3ac0a46fSAndroid Build Coastguard Worker
401*3ac0a46fSAndroid Build Coastguard Worker default:
402*3ac0a46fSAndroid Build Coastguard Worker // Average surround
403*3ac0a46fSAndroid Build Coastguard Worker lpMod -> F = 1.0;
404*3ac0a46fSAndroid Build Coastguard Worker lpMod -> c = 0.69;
405*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Nc = 1.0;
406*3ac0a46fSAndroid Build Coastguard Worker }
407*3ac0a46fSAndroid Build Coastguard Worker
408*3ac0a46fSAndroid Build Coastguard Worker lpMod -> n = compute_n(lpMod);
409*3ac0a46fSAndroid Build Coastguard Worker lpMod -> z = compute_z(lpMod);
410*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Nbb = computeNbb(lpMod);
411*3ac0a46fSAndroid Build Coastguard Worker lpMod -> FL = computeFL(lpMod);
412*3ac0a46fSAndroid Build Coastguard Worker
413*3ac0a46fSAndroid Build Coastguard Worker if (lpMod -> D == D_CALCULATE) {
414*3ac0a46fSAndroid Build Coastguard Worker lpMod -> D = computeD(lpMod);
415*3ac0a46fSAndroid Build Coastguard Worker }
416*3ac0a46fSAndroid Build Coastguard Worker
417*3ac0a46fSAndroid Build Coastguard Worker lpMod -> Ncb = lpMod -> Nbb;
418*3ac0a46fSAndroid Build Coastguard Worker
419*3ac0a46fSAndroid Build Coastguard Worker lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
420*3ac0a46fSAndroid Build Coastguard Worker lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
421*3ac0a46fSAndroid Build Coastguard Worker lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
422*3ac0a46fSAndroid Build Coastguard Worker lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
423*3ac0a46fSAndroid Build Coastguard Worker
424*3ac0a46fSAndroid Build Coastguard Worker return (cmsHANDLE) lpMod;
425*3ac0a46fSAndroid Build Coastguard Worker
426*3ac0a46fSAndroid Build Coastguard Worker }
427*3ac0a46fSAndroid Build Coastguard Worker
cmsCIECAM02Done(cmsHANDLE hModel)428*3ac0a46fSAndroid Build Coastguard Worker void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel)
429*3ac0a46fSAndroid Build Coastguard Worker {
430*3ac0a46fSAndroid Build Coastguard Worker cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
431*3ac0a46fSAndroid Build Coastguard Worker
432*3ac0a46fSAndroid Build Coastguard Worker if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);
433*3ac0a46fSAndroid Build Coastguard Worker }
434*3ac0a46fSAndroid Build Coastguard Worker
435*3ac0a46fSAndroid Build Coastguard Worker
cmsCIECAM02Forward(cmsHANDLE hModel,const cmsCIEXYZ * pIn,cmsJCh * pOut)436*3ac0a46fSAndroid Build Coastguard Worker void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut)
437*3ac0a46fSAndroid Build Coastguard Worker {
438*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR clr;
439*3ac0a46fSAndroid Build Coastguard Worker cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
440*3ac0a46fSAndroid Build Coastguard Worker
441*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(lpMod != NULL);
442*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(pIn != NULL);
443*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(pOut != NULL);
444*3ac0a46fSAndroid Build Coastguard Worker
445*3ac0a46fSAndroid Build Coastguard Worker memset(&clr, 0, sizeof(clr));
446*3ac0a46fSAndroid Build Coastguard Worker
447*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[0] = pIn ->X;
448*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[1] = pIn ->Y;
449*3ac0a46fSAndroid Build Coastguard Worker clr.XYZ[2] = pIn ->Z;
450*3ac0a46fSAndroid Build Coastguard Worker
451*3ac0a46fSAndroid Build Coastguard Worker clr = XYZtoCAT02(clr);
452*3ac0a46fSAndroid Build Coastguard Worker clr = ChromaticAdaptation(clr, lpMod);
453*3ac0a46fSAndroid Build Coastguard Worker clr = CAT02toHPE(clr);
454*3ac0a46fSAndroid Build Coastguard Worker clr = NonlinearCompression(clr, lpMod);
455*3ac0a46fSAndroid Build Coastguard Worker clr = ComputeCorrelates(clr, lpMod);
456*3ac0a46fSAndroid Build Coastguard Worker
457*3ac0a46fSAndroid Build Coastguard Worker pOut ->J = clr.J;
458*3ac0a46fSAndroid Build Coastguard Worker pOut ->C = clr.C;
459*3ac0a46fSAndroid Build Coastguard Worker pOut ->h = clr.h;
460*3ac0a46fSAndroid Build Coastguard Worker }
461*3ac0a46fSAndroid Build Coastguard Worker
cmsCIECAM02Reverse(cmsHANDLE hModel,const cmsJCh * pIn,cmsCIEXYZ * pOut)462*3ac0a46fSAndroid Build Coastguard Worker void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut)
463*3ac0a46fSAndroid Build Coastguard Worker {
464*3ac0a46fSAndroid Build Coastguard Worker CAM02COLOR clr;
465*3ac0a46fSAndroid Build Coastguard Worker cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
466*3ac0a46fSAndroid Build Coastguard Worker
467*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(lpMod != NULL);
468*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(pIn != NULL);
469*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(pOut != NULL);
470*3ac0a46fSAndroid Build Coastguard Worker
471*3ac0a46fSAndroid Build Coastguard Worker memset(&clr, 0, sizeof(clr));
472*3ac0a46fSAndroid Build Coastguard Worker
473*3ac0a46fSAndroid Build Coastguard Worker clr.J = pIn -> J;
474*3ac0a46fSAndroid Build Coastguard Worker clr.C = pIn -> C;
475*3ac0a46fSAndroid Build Coastguard Worker clr.h = pIn -> h;
476*3ac0a46fSAndroid Build Coastguard Worker
477*3ac0a46fSAndroid Build Coastguard Worker clr = InverseCorrelates(clr, lpMod);
478*3ac0a46fSAndroid Build Coastguard Worker clr = InverseNonlinearity(clr, lpMod);
479*3ac0a46fSAndroid Build Coastguard Worker clr = HPEtoCAT02(clr);
480*3ac0a46fSAndroid Build Coastguard Worker clr = InverseChromaticAdaptation(clr, lpMod);
481*3ac0a46fSAndroid Build Coastguard Worker clr = CAT02toXYZ(clr);
482*3ac0a46fSAndroid Build Coastguard Worker
483*3ac0a46fSAndroid Build Coastguard Worker pOut ->X = clr.XYZ[0];
484*3ac0a46fSAndroid Build Coastguard Worker pOut ->Y = clr.XYZ[1];
485*3ac0a46fSAndroid Build Coastguard Worker pOut ->Z = clr.XYZ[2];
486*3ac0a46fSAndroid Build Coastguard Worker }
487