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