Coverage Report

Created: 2023-12-08 06:53

/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