/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfRgbaYca.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | ////////////////////////////////////////////////////////////////////////////// |
2 | | // |
3 | | // Copyright (c) 2004, Industrial Light & Magic, a division of Lucasfilm |
4 | | // Entertainment Company Ltd. Portions contributed and copyright held by |
5 | | // others as indicated. All rights reserved. |
6 | | // |
7 | | // Redistribution and use in source and binary forms, with or without |
8 | | // modification, are permitted provided that the following conditions are |
9 | | // met: |
10 | | // |
11 | | // * Redistributions of source code must retain the above |
12 | | // copyright notice, this list of conditions and the following |
13 | | // disclaimer. |
14 | | // |
15 | | // * Redistributions in binary form must reproduce the above |
16 | | // copyright notice, this list of conditions and the following |
17 | | // disclaimer in the documentation and/or other materials provided with |
18 | | // the distribution. |
19 | | // |
20 | | // * Neither the name of Industrial Light & Magic nor the names of |
21 | | // any other contributors to this software may be used to endorse or |
22 | | // promote products derived from this software without specific prior |
23 | | // written permission. |
24 | | // |
25 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
26 | | // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
27 | | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
28 | | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
29 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
30 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
31 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
32 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
33 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
34 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
35 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | | // |
37 | | ////////////////////////////////////////////////////////////////////////////// |
38 | | |
39 | | //----------------------------------------------------------------------------- |
40 | | // |
41 | | // Conversion between RGBA and YCA data. |
42 | | // |
43 | | //----------------------------------------------------------------------------- |
44 | | |
45 | | #include <ImfRgbaYca.h> |
46 | | #include <assert.h> |
47 | | #include <algorithm> |
48 | | |
49 | | using namespace IMATH_NAMESPACE; |
50 | | using namespace std; |
51 | | #include "ImfNamespace.h" |
52 | | |
53 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
54 | | |
55 | | namespace RgbaYca { |
56 | | |
57 | | |
58 | | V3f |
59 | | computeYw (const Chromaticities &cr) |
60 | 0 | { |
61 | 0 | M44f m = RGBtoXYZ (cr, 1); |
62 | 0 | return V3f (m[0][1], m[1][1], m[2][1]) / (m[0][1] + m[1][1] + m[2][1]); |
63 | 0 | } |
64 | | |
65 | | |
66 | | void |
67 | | RGBAtoYCA (const V3f &yw, |
68 | | int n, |
69 | | bool aIsValid, |
70 | | const Rgba rgbaIn[/*n*/], |
71 | | Rgba ycaOut[/*n*/]) |
72 | 0 | { |
73 | 0 | for (int i = 0; i < n; ++i) |
74 | 0 | { |
75 | 0 | Rgba in = rgbaIn[i]; |
76 | 0 | Rgba &out = ycaOut[i]; |
77 | | |
78 | | // |
79 | | // Conversion to YCA and subsequent chroma subsampling |
80 | | // work only if R, G and B are finite and non-negative. |
81 | | // |
82 | |
|
83 | 0 | if (!in.r.isFinite() || in.r < 0) |
84 | 0 | in.r = 0; |
85 | |
|
86 | 0 | if (!in.g.isFinite() || in.g < 0) |
87 | 0 | in.g = 0; |
88 | |
|
89 | 0 | if (!in.b.isFinite() || in.b < 0) |
90 | 0 | in.b = 0; |
91 | |
|
92 | 0 | if (in.r == in.g && in.g == in.b) |
93 | 0 | { |
94 | | // |
95 | | // Special case -- R, G and B are equal. To avoid rounding |
96 | | // errors, we explicitly set the output luminance channel |
97 | | // to G, and the chroma channels to 0. |
98 | | // |
99 | | // The special cases here and in YCAtoRGBA() ensure that |
100 | | // converting black-and white images from RGBA to YCA and |
101 | | // back is lossless. |
102 | | // |
103 | |
|
104 | 0 | out.r = 0; |
105 | 0 | out.g = in.g; |
106 | 0 | out.b = 0; |
107 | 0 | } |
108 | 0 | else |
109 | 0 | { |
110 | 0 | out.g = in.r * yw.x + in.g * yw.y + in.b * yw.z; |
111 | |
|
112 | 0 | float Y = out.g; |
113 | |
|
114 | 0 | if (abs (in.r - Y) < HALF_MAX * Y) |
115 | 0 | out.r = (in.r - Y) / Y; |
116 | 0 | else |
117 | 0 | out.r = 0; |
118 | |
|
119 | 0 | if (abs (in.b - Y) < HALF_MAX * Y) |
120 | 0 | out.b = (in.b - Y) / Y; |
121 | 0 | else |
122 | 0 | out.b = 0; |
123 | 0 | } |
124 | |
|
125 | 0 | if (aIsValid) |
126 | 0 | out.a = in.a; |
127 | 0 | else |
128 | 0 | out.a = 1; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | |
133 | | void |
134 | | decimateChromaHoriz (int n, |
135 | | const Rgba ycaIn[/*n+N-1*/], |
136 | | Rgba ycaOut[/*n*/]) |
137 | 0 | { |
138 | | #ifdef DEBUG |
139 | | assert (ycaIn != ycaOut); |
140 | | #endif |
141 | |
|
142 | 0 | int begin = N2; |
143 | 0 | int end = begin + n; |
144 | |
|
145 | 0 | for (int i = begin, j = 0; i < end; ++i, ++j) |
146 | 0 | { |
147 | 0 | if ((j & 1) == 0) |
148 | 0 | { |
149 | 0 | ycaOut[j].r = ycaIn[i - 13].r * 0.001064f + |
150 | 0 | ycaIn[i - 11].r * -0.003771f + |
151 | 0 | ycaIn[i - 9].r * 0.009801f + |
152 | 0 | ycaIn[i - 7].r * -0.021586f + |
153 | 0 | ycaIn[i - 5].r * 0.043978f + |
154 | 0 | ycaIn[i - 3].r * -0.093067f + |
155 | 0 | ycaIn[i - 1].r * 0.313659f + |
156 | 0 | ycaIn[i ].r * 0.499846f + |
157 | 0 | ycaIn[i + 1].r * 0.313659f + |
158 | 0 | ycaIn[i + 3].r * -0.093067f + |
159 | 0 | ycaIn[i + 5].r * 0.043978f + |
160 | 0 | ycaIn[i + 7].r * -0.021586f + |
161 | 0 | ycaIn[i + 9].r * 0.009801f + |
162 | 0 | ycaIn[i + 11].r * -0.003771f + |
163 | 0 | ycaIn[i + 13].r * 0.001064f; |
164 | |
|
165 | 0 | ycaOut[j].b = ycaIn[i - 13].b * 0.001064f + |
166 | 0 | ycaIn[i - 11].b * -0.003771f + |
167 | 0 | ycaIn[i - 9].b * 0.009801f + |
168 | 0 | ycaIn[i - 7].b * -0.021586f + |
169 | 0 | ycaIn[i - 5].b * 0.043978f + |
170 | 0 | ycaIn[i - 3].b * -0.093067f + |
171 | 0 | ycaIn[i - 1].b * 0.313659f + |
172 | 0 | ycaIn[i ].b * 0.499846f + |
173 | 0 | ycaIn[i + 1].b * 0.313659f + |
174 | 0 | ycaIn[i + 3].b * -0.093067f + |
175 | 0 | ycaIn[i + 5].b * 0.043978f + |
176 | 0 | ycaIn[i + 7].b * -0.021586f + |
177 | 0 | ycaIn[i + 9].b * 0.009801f + |
178 | 0 | ycaIn[i + 11].b * -0.003771f + |
179 | 0 | ycaIn[i + 13].b * 0.001064f; |
180 | 0 | } |
181 | |
|
182 | 0 | ycaOut[j].g = ycaIn[i].g; |
183 | 0 | ycaOut[j].a = ycaIn[i].a; |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | |
188 | | void |
189 | | decimateChromaVert (int n, |
190 | | const Rgba * const ycaIn[N], |
191 | | Rgba ycaOut[/*n*/]) |
192 | 0 | { |
193 | 0 | for (int i = 0; i < n; ++i) |
194 | 0 | { |
195 | 0 | if ((i & 1) == 0) |
196 | 0 | { |
197 | 0 | ycaOut[i].r = ycaIn[ 0][i].r * 0.001064f + |
198 | 0 | ycaIn[ 2][i].r * -0.003771f + |
199 | 0 | ycaIn[ 4][i].r * 0.009801f + |
200 | 0 | ycaIn[ 6][i].r * -0.021586f + |
201 | 0 | ycaIn[ 8][i].r * 0.043978f + |
202 | 0 | ycaIn[10][i].r * -0.093067f + |
203 | 0 | ycaIn[12][i].r * 0.313659f + |
204 | 0 | ycaIn[13][i].r * 0.499846f + |
205 | 0 | ycaIn[14][i].r * 0.313659f + |
206 | 0 | ycaIn[16][i].r * -0.093067f + |
207 | 0 | ycaIn[18][i].r * 0.043978f + |
208 | 0 | ycaIn[20][i].r * -0.021586f + |
209 | 0 | ycaIn[22][i].r * 0.009801f + |
210 | 0 | ycaIn[24][i].r * -0.003771f + |
211 | 0 | ycaIn[26][i].r * 0.001064f; |
212 | |
|
213 | 0 | ycaOut[i].b = ycaIn[ 0][i].b * 0.001064f + |
214 | 0 | ycaIn[ 2][i].b * -0.003771f + |
215 | 0 | ycaIn[ 4][i].b * 0.009801f + |
216 | 0 | ycaIn[ 6][i].b * -0.021586f + |
217 | 0 | ycaIn[ 8][i].b * 0.043978f + |
218 | 0 | ycaIn[10][i].b * -0.093067f + |
219 | 0 | ycaIn[12][i].b * 0.313659f + |
220 | 0 | ycaIn[13][i].b * 0.499846f + |
221 | 0 | ycaIn[14][i].b * 0.313659f + |
222 | 0 | ycaIn[16][i].b * -0.093067f + |
223 | 0 | ycaIn[18][i].b * 0.043978f + |
224 | 0 | ycaIn[20][i].b * -0.021586f + |
225 | 0 | ycaIn[22][i].b * 0.009801f + |
226 | 0 | ycaIn[24][i].b * -0.003771f + |
227 | 0 | ycaIn[26][i].b * 0.001064f; |
228 | 0 | } |
229 | |
|
230 | 0 | ycaOut[i].g = ycaIn[13][i].g; |
231 | 0 | ycaOut[i].a = ycaIn[13][i].a; |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | |
236 | | void |
237 | | roundYCA (int n, |
238 | | unsigned int roundY, |
239 | | unsigned int roundC, |
240 | | const Rgba ycaIn[/*n*/], |
241 | | Rgba ycaOut[/*n*/]) |
242 | 0 | { |
243 | 0 | for (int i = 0; i < n; ++i) |
244 | 0 | { |
245 | 0 | ycaOut[i].g = ycaIn[i].g.round (roundY); |
246 | 0 | ycaOut[i].a = ycaIn[i].a; |
247 | |
|
248 | 0 | if ((i & 1) == 0) |
249 | 0 | { |
250 | 0 | ycaOut[i].r = ycaIn[i].r.round (roundC); |
251 | 0 | ycaOut[i].b = ycaIn[i].b.round (roundC); |
252 | 0 | } |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | | |
257 | | void |
258 | | reconstructChromaHoriz (int n, |
259 | | const Rgba ycaIn[/*n+N-1*/], |
260 | | Rgba ycaOut[/*n*/]) |
261 | 0 | { |
262 | | #ifdef DEBUG |
263 | | assert (ycaIn != ycaOut); |
264 | | #endif |
265 | |
|
266 | 0 | int begin = N2; |
267 | 0 | int end = begin + n; |
268 | |
|
269 | 0 | for (int i = begin, j = 0; i < end; ++i, ++j) |
270 | 0 | { |
271 | 0 | if (j & 1) |
272 | 0 | { |
273 | 0 | ycaOut[j].r = ycaIn[i - 13].r * 0.002128f + |
274 | 0 | ycaIn[i - 11].r * -0.007540f + |
275 | 0 | ycaIn[i - 9].r * 0.019597f + |
276 | 0 | ycaIn[i - 7].r * -0.043159f + |
277 | 0 | ycaIn[i - 5].r * 0.087929f + |
278 | 0 | ycaIn[i - 3].r * -0.186077f + |
279 | 0 | ycaIn[i - 1].r * 0.627123f + |
280 | 0 | ycaIn[i + 1].r * 0.627123f + |
281 | 0 | ycaIn[i + 3].r * -0.186077f + |
282 | 0 | ycaIn[i + 5].r * 0.087929f + |
283 | 0 | ycaIn[i + 7].r * -0.043159f + |
284 | 0 | ycaIn[i + 9].r * 0.019597f + |
285 | 0 | ycaIn[i + 11].r * -0.007540f + |
286 | 0 | ycaIn[i + 13].r * 0.002128f; |
287 | |
|
288 | 0 | ycaOut[j].b = ycaIn[i - 13].b * 0.002128f + |
289 | 0 | ycaIn[i - 11].b * -0.007540f + |
290 | 0 | ycaIn[i - 9].b * 0.019597f + |
291 | 0 | ycaIn[i - 7].b * -0.043159f + |
292 | 0 | ycaIn[i - 5].b * 0.087929f + |
293 | 0 | ycaIn[i - 3].b * -0.186077f + |
294 | 0 | ycaIn[i - 1].b * 0.627123f + |
295 | 0 | ycaIn[i + 1].b * 0.627123f + |
296 | 0 | ycaIn[i + 3].b * -0.186077f + |
297 | 0 | ycaIn[i + 5].b * 0.087929f + |
298 | 0 | ycaIn[i + 7].b * -0.043159f + |
299 | 0 | ycaIn[i + 9].b * 0.019597f + |
300 | 0 | ycaIn[i + 11].b * -0.007540f + |
301 | 0 | ycaIn[i + 13].b * 0.002128f; |
302 | 0 | } |
303 | 0 | else |
304 | 0 | { |
305 | 0 | ycaOut[j].r = ycaIn[i].r; |
306 | 0 | ycaOut[j].b = ycaIn[i].b; |
307 | 0 | } |
308 | |
|
309 | 0 | ycaOut[j].g = ycaIn[i].g; |
310 | 0 | ycaOut[j].a = ycaIn[i].a; |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | |
315 | | void |
316 | | reconstructChromaVert (int n, |
317 | | const Rgba * const ycaIn[N], |
318 | | Rgba ycaOut[/*n*/]) |
319 | 0 | { |
320 | 0 | for (int i = 0; i < n; ++i) |
321 | 0 | { |
322 | 0 | ycaOut[i].r = ycaIn[ 0][i].r * 0.002128f + |
323 | 0 | ycaIn[ 2][i].r * -0.007540f + |
324 | 0 | ycaIn[ 4][i].r * 0.019597f + |
325 | 0 | ycaIn[ 6][i].r * -0.043159f + |
326 | 0 | ycaIn[ 8][i].r * 0.087929f + |
327 | 0 | ycaIn[10][i].r * -0.186077f + |
328 | 0 | ycaIn[12][i].r * 0.627123f + |
329 | 0 | ycaIn[14][i].r * 0.627123f + |
330 | 0 | ycaIn[16][i].r * -0.186077f + |
331 | 0 | ycaIn[18][i].r * 0.087929f + |
332 | 0 | ycaIn[20][i].r * -0.043159f + |
333 | 0 | ycaIn[22][i].r * 0.019597f + |
334 | 0 | ycaIn[24][i].r * -0.007540f + |
335 | 0 | ycaIn[26][i].r * 0.002128f; |
336 | |
|
337 | 0 | ycaOut[i].b = ycaIn[ 0][i].b * 0.002128f + |
338 | 0 | ycaIn[ 2][i].b * -0.007540f + |
339 | 0 | ycaIn[ 4][i].b * 0.019597f + |
340 | 0 | ycaIn[ 6][i].b * -0.043159f + |
341 | 0 | ycaIn[ 8][i].b * 0.087929f + |
342 | 0 | ycaIn[10][i].b * -0.186077f + |
343 | 0 | ycaIn[12][i].b * 0.627123f + |
344 | 0 | ycaIn[14][i].b * 0.627123f + |
345 | 0 | ycaIn[16][i].b * -0.186077f + |
346 | 0 | ycaIn[18][i].b * 0.087929f + |
347 | 0 | ycaIn[20][i].b * -0.043159f + |
348 | 0 | ycaIn[22][i].b * 0.019597f + |
349 | 0 | ycaIn[24][i].b * -0.007540f + |
350 | 0 | ycaIn[26][i].b * 0.002128f; |
351 | |
|
352 | 0 | ycaOut[i].g = ycaIn[13][i].g; |
353 | 0 | ycaOut[i].a = ycaIn[13][i].a; |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | | |
358 | | void |
359 | | YCAtoRGBA (const IMATH_NAMESPACE::V3f &yw, |
360 | | int n, |
361 | | const Rgba ycaIn[/*n*/], |
362 | | Rgba rgbaOut[/*n*/]) |
363 | 0 | { |
364 | 0 | for (int i = 0; i < n; ++i) |
365 | 0 | { |
366 | 0 | const Rgba &in = ycaIn[i]; |
367 | 0 | Rgba &out = rgbaOut[i]; |
368 | |
|
369 | 0 | if (in.r == 0 && in.b == 0) |
370 | 0 | { |
371 | | // |
372 | | // Special case -- both chroma channels are 0. To avoid |
373 | | // rounding errors, we explicitly set the output R, G and B |
374 | | // channels equal to the input luminance. |
375 | | // |
376 | | // The special cases here and in RGBAtoYCA() ensure that |
377 | | // converting black-and white images from RGBA to YCA and |
378 | | // back is lossless. |
379 | | // |
380 | |
|
381 | 0 | out.r = in.g; |
382 | 0 | out.g = in.g; |
383 | 0 | out.b = in.g; |
384 | 0 | out.a = in.a; |
385 | 0 | } |
386 | 0 | else |
387 | 0 | { |
388 | 0 | float Y = in.g; |
389 | 0 | float r = (in.r + 1) * Y; |
390 | 0 | float b = (in.b + 1) * Y; |
391 | 0 | float g = (Y - r * yw.x - b * yw.z) / yw.y; |
392 | |
|
393 | 0 | out.r = r; |
394 | 0 | out.g = g; |
395 | 0 | out.b = b; |
396 | 0 | out.a = in.a; |
397 | 0 | } |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | |
402 | | namespace { |
403 | | |
404 | | inline float |
405 | | saturation (const Rgba &in) |
406 | 0 | { |
407 | 0 | float rgbMax = max (in.r, max (in.g, in.b)); |
408 | 0 | float rgbMin = min (in.r, min (in.g, in.b)); |
409 | |
|
410 | 0 | if (rgbMax > 0) |
411 | 0 | return 1 - rgbMin / rgbMax; |
412 | 0 | else |
413 | 0 | return 0; |
414 | 0 | } |
415 | | |
416 | | |
417 | | void |
418 | | desaturate (const Rgba &in, float f, const V3f &yw, Rgba &out) |
419 | 0 | { |
420 | 0 | float rgbMax = max (in.r, max (in.g, in.b)); |
421 | |
|
422 | 0 | out.r = max (float (rgbMax - (rgbMax - in.r) * f), 0.0f); |
423 | 0 | out.g = max (float (rgbMax - (rgbMax - in.g) * f), 0.0f); |
424 | 0 | out.b = max (float (rgbMax - (rgbMax - in.b) * f), 0.0f); |
425 | 0 | out.a = in.a; |
426 | |
|
427 | 0 | float Yin = in.r * yw.x + in.g * yw.y + in.b * yw.z; |
428 | 0 | float Yout = out.r * yw.x + out.g * yw.y + out.b * yw.z; |
429 | |
|
430 | 0 | if (Yout > 0) |
431 | 0 | { |
432 | 0 | out.r *= Yin / Yout; |
433 | 0 | out.g *= Yin / Yout; |
434 | 0 | out.b *= Yin / Yout; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | } // namespace |
439 | | |
440 | | |
441 | | void |
442 | | fixSaturation (const IMATH_NAMESPACE::V3f &yw, |
443 | | int n, |
444 | | const Rgba * const rgbaIn[3], |
445 | | Rgba rgbaOut[/*n*/]) |
446 | 0 | { |
447 | 0 | float neighborA2 = saturation (rgbaIn[0][0]); |
448 | 0 | float neighborA1 = neighborA2; |
449 | |
|
450 | 0 | float neighborB2 = saturation (rgbaIn[2][0]); |
451 | 0 | float neighborB1 = neighborB2; |
452 | |
|
453 | 0 | for (int i = 0; i < n; ++i) |
454 | 0 | { |
455 | 0 | float neighborA0 = neighborA1; |
456 | 0 | neighborA1 = neighborA2; |
457 | |
|
458 | 0 | float neighborB0 = neighborB1; |
459 | 0 | neighborB1 = neighborB2; |
460 | |
|
461 | 0 | if (i < n - 1) |
462 | 0 | { |
463 | 0 | neighborA2 = saturation (rgbaIn[0][i + 1]); |
464 | 0 | neighborB2 = saturation (rgbaIn[2][i + 1]); |
465 | 0 | } |
466 | | |
467 | | // |
468 | | // A0 A1 A2 |
469 | | // rgbaOut[i] |
470 | | // B0 B1 B2 |
471 | | // |
472 | |
|
473 | 0 | float sMean = min (1.0f, 0.25f * (neighborA0 + neighborA2 + |
474 | 0 | neighborB0 + neighborB2)); |
475 | |
|
476 | 0 | const Rgba &in = rgbaIn[1][i]; |
477 | 0 | Rgba &out = rgbaOut[i]; |
478 | |
|
479 | 0 | float s = saturation (in); |
480 | |
|
481 | 0 | if (s > sMean) |
482 | 0 | { |
483 | 0 | float sMax = min (1.0f, 1 - (1 - sMean) * 0.25f); |
484 | |
|
485 | 0 | if (s > sMax) |
486 | 0 | { |
487 | 0 | desaturate (in, sMax / s, yw, out); |
488 | 0 | continue; |
489 | 0 | } |
490 | 0 | } |
491 | | |
492 | 0 | out = in; |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | } // namespace RgbaYca |
497 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |