Coverage Report

Created: 2026-09-14 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfRgbaYca.cpp
Line
Count
Source
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
//-----------------------------------------------------------------------------
7
//
8
//  Conversion between RGBA and YCA data.
9
//
10
//-----------------------------------------------------------------------------
11
12
#include <ImfRgbaYca.h>
13
#include <algorithm>
14
#include <assert.h>
15
16
using namespace IMATH_NAMESPACE;
17
using namespace std;
18
#include "ImfNamespace.h"
19
20
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
21
22
namespace RgbaYca
23
{
24
25
V3f
26
computeYw (const Chromaticities& cr)
27
309
{
28
309
    M44f m = RGBtoXYZ (cr, 1);
29
309
    return V3f (m[0][1], m[1][1], m[2][1]) / (m[0][1] + m[1][1] + m[2][1]);
30
309
}
31
32
void
33
RGBAtoYCA (
34
    const V3f& yw,
35
    int        n,
36
    bool       aIsValid,
37
    const Rgba rgbaIn[/*n*/],
38
    Rgba       ycaOut[/*n*/])
39
0
{
40
0
    for (int i = 0; i < n; ++i)
41
0
    {
42
0
        Rgba  in  = rgbaIn[i];
43
0
        Rgba& out = ycaOut[i];
44
45
        //
46
        // Conversion to YCA and subsequent chroma subsampling
47
        // work only if R, G and B are finite and non-negative.
48
        //
49
50
0
        if (!in.r.isFinite () || in.r < 0) in.r = 0;
51
52
0
        if (!in.g.isFinite () || in.g < 0) in.g = 0;
53
54
0
        if (!in.b.isFinite () || in.b < 0) in.b = 0;
55
56
0
        if (in.r == in.g && in.g == in.b)
57
0
        {
58
            //
59
            // Special case -- R, G and B are equal. To avoid rounding
60
            // errors, we explicitly set the output luminance channel
61
            // to G, and the chroma channels to 0.
62
            //
63
            // The special cases here and in YCAtoRGBA() ensure that
64
            // converting black-and white images from RGBA to YCA and
65
            // back is lossless.
66
            //
67
68
0
            out.r = 0;
69
0
            out.g = in.g;
70
0
            out.b = 0;
71
0
        }
72
0
        else
73
0
        {
74
0
            out.g = in.r * yw.x + in.g * yw.y + in.b * yw.z;
75
76
0
            float Y = out.g;
77
78
0
            if (abs (in.r - Y) < HALF_MAX * Y)
79
0
                out.r = (in.r - Y) / Y;
80
0
            else
81
0
                out.r = 0;
82
83
0
            if (abs (in.b - Y) < HALF_MAX * Y)
84
0
                out.b = (in.b - Y) / Y;
85
0
            else
86
0
                out.b = 0;
87
0
        }
88
89
0
        if (aIsValid)
90
0
            out.a = in.a;
91
0
        else
92
0
            out.a = 1;
93
0
    }
94
0
}
95
96
void
97
decimateChromaHoriz (int n, const Rgba ycaIn[/*n+N-1*/], Rgba ycaOut[/*n*/])
98
0
{
99
#ifdef DEBUG
100
    assert (ycaIn != ycaOut);
101
#endif
102
103
0
    int begin = N2;
104
0
    int end   = begin + n;
105
106
0
    for (int i = begin, j = 0; i < end; ++i, ++j)
107
0
    {
108
0
        if ((j & 1) == 0)
109
0
        {
110
0
            ycaOut[j].r =
111
0
                ycaIn[i - 13].r * 0.001064f + ycaIn[i - 11].r * -0.003771f +
112
0
                ycaIn[i - 9].r * 0.009801f + ycaIn[i - 7].r * -0.021586f +
113
0
                ycaIn[i - 5].r * 0.043978f + ycaIn[i - 3].r * -0.093067f +
114
0
                ycaIn[i - 1].r * 0.313659f + ycaIn[i].r * 0.499846f +
115
0
                ycaIn[i + 1].r * 0.313659f + ycaIn[i + 3].r * -0.093067f +
116
0
                ycaIn[i + 5].r * 0.043978f + ycaIn[i + 7].r * -0.021586f +
117
0
                ycaIn[i + 9].r * 0.009801f + ycaIn[i + 11].r * -0.003771f +
118
0
                ycaIn[i + 13].r * 0.001064f;
119
120
0
            ycaOut[j].b =
121
0
                ycaIn[i - 13].b * 0.001064f + ycaIn[i - 11].b * -0.003771f +
122
0
                ycaIn[i - 9].b * 0.009801f + ycaIn[i - 7].b * -0.021586f +
123
0
                ycaIn[i - 5].b * 0.043978f + ycaIn[i - 3].b * -0.093067f +
124
0
                ycaIn[i - 1].b * 0.313659f + ycaIn[i].b * 0.499846f +
125
0
                ycaIn[i + 1].b * 0.313659f + ycaIn[i + 3].b * -0.093067f +
126
0
                ycaIn[i + 5].b * 0.043978f + ycaIn[i + 7].b * -0.021586f +
127
0
                ycaIn[i + 9].b * 0.009801f + ycaIn[i + 11].b * -0.003771f +
128
0
                ycaIn[i + 13].b * 0.001064f;
129
0
        }
130
131
0
        ycaOut[j].g = ycaIn[i].g;
132
0
        ycaOut[j].a = ycaIn[i].a;
133
0
    }
134
0
}
135
136
void
137
decimateChromaVert (int n, const Rgba* const ycaIn[N], Rgba ycaOut[/*n*/])
138
0
{
139
0
    for (int i = 0; i < n; ++i)
140
0
    {
141
0
        if ((i & 1) == 0)
142
0
        {
143
0
            ycaOut[i].r =
144
0
                ycaIn[0][i].r * 0.001064f + ycaIn[2][i].r * -0.003771f +
145
0
                ycaIn[4][i].r * 0.009801f + ycaIn[6][i].r * -0.021586f +
146
0
                ycaIn[8][i].r * 0.043978f + ycaIn[10][i].r * -0.093067f +
147
0
                ycaIn[12][i].r * 0.313659f + ycaIn[13][i].r * 0.499846f +
148
0
                ycaIn[14][i].r * 0.313659f + ycaIn[16][i].r * -0.093067f +
149
0
                ycaIn[18][i].r * 0.043978f + ycaIn[20][i].r * -0.021586f +
150
0
                ycaIn[22][i].r * 0.009801f + ycaIn[24][i].r * -0.003771f +
151
0
                ycaIn[26][i].r * 0.001064f;
152
153
0
            ycaOut[i].b =
154
0
                ycaIn[0][i].b * 0.001064f + ycaIn[2][i].b * -0.003771f +
155
0
                ycaIn[4][i].b * 0.009801f + ycaIn[6][i].b * -0.021586f +
156
0
                ycaIn[8][i].b * 0.043978f + ycaIn[10][i].b * -0.093067f +
157
0
                ycaIn[12][i].b * 0.313659f + ycaIn[13][i].b * 0.499846f +
158
0
                ycaIn[14][i].b * 0.313659f + ycaIn[16][i].b * -0.093067f +
159
0
                ycaIn[18][i].b * 0.043978f + ycaIn[20][i].b * -0.021586f +
160
0
                ycaIn[22][i].b * 0.009801f + ycaIn[24][i].b * -0.003771f +
161
0
                ycaIn[26][i].b * 0.001064f;
162
0
        }
163
164
0
        ycaOut[i].g = ycaIn[13][i].g;
165
0
        ycaOut[i].a = ycaIn[13][i].a;
166
0
    }
167
0
}
168
169
void
170
roundYCA (
171
    int          n,
172
    unsigned int roundY,
173
    unsigned int roundC,
174
    const Rgba   ycaIn[/*n*/],
175
    Rgba         ycaOut[/*n*/])
176
0
{
177
0
    for (int i = 0; i < n; ++i)
178
0
    {
179
0
        ycaOut[i].g = ycaIn[i].g.round (roundY);
180
0
        ycaOut[i].a = ycaIn[i].a;
181
182
0
        if ((i & 1) == 0)
183
0
        {
184
0
            ycaOut[i].r = ycaIn[i].r.round (roundC);
185
0
            ycaOut[i].b = ycaIn[i].b.round (roundC);
186
0
        }
187
0
    }
188
0
}
189
190
void
191
reconstructChromaHoriz (int n, const Rgba ycaIn[/*n+N-1*/], Rgba ycaOut[/*n*/])
192
155k
{
193
#ifdef DEBUG
194
    assert (ycaIn != ycaOut);
195
#endif
196
197
155k
    int begin = N2;
198
155k
    int end   = begin + n;
199
200
16.3M
    for (int i = begin, j = 0; i < end; ++i, ++j)
201
16.2M
    {
202
16.2M
        if (j & 1)
203
8.10M
        {
204
8.10M
            ycaOut[j].r =
205
8.10M
                ycaIn[i - 13].r * 0.002128f + ycaIn[i - 11].r * -0.007540f +
206
8.10M
                ycaIn[i - 9].r * 0.019597f + ycaIn[i - 7].r * -0.043159f +
207
8.10M
                ycaIn[i - 5].r * 0.087929f + ycaIn[i - 3].r * -0.186077f +
208
8.10M
                ycaIn[i - 1].r * 0.627123f + ycaIn[i + 1].r * 0.627123f +
209
8.10M
                ycaIn[i + 3].r * -0.186077f + ycaIn[i + 5].r * 0.087929f +
210
8.10M
                ycaIn[i + 7].r * -0.043159f + ycaIn[i + 9].r * 0.019597f +
211
8.10M
                ycaIn[i + 11].r * -0.007540f + ycaIn[i + 13].r * 0.002128f;
212
213
8.10M
            ycaOut[j].b =
214
8.10M
                ycaIn[i - 13].b * 0.002128f + ycaIn[i - 11].b * -0.007540f +
215
8.10M
                ycaIn[i - 9].b * 0.019597f + ycaIn[i - 7].b * -0.043159f +
216
8.10M
                ycaIn[i - 5].b * 0.087929f + ycaIn[i - 3].b * -0.186077f +
217
8.10M
                ycaIn[i - 1].b * 0.627123f + ycaIn[i + 1].b * 0.627123f +
218
8.10M
                ycaIn[i + 3].b * -0.186077f + ycaIn[i + 5].b * 0.087929f +
219
8.10M
                ycaIn[i + 7].b * -0.043159f + ycaIn[i + 9].b * 0.019597f +
220
8.10M
                ycaIn[i + 11].b * -0.007540f + ycaIn[i + 13].b * 0.002128f;
221
8.10M
        }
222
8.11M
        else
223
8.11M
        {
224
8.11M
            ycaOut[j].r = ycaIn[i].r;
225
8.11M
            ycaOut[j].b = ycaIn[i].b;
226
8.11M
        }
227
228
16.2M
        ycaOut[j].g = ycaIn[i].g;
229
16.2M
        ycaOut[j].a = ycaIn[i].a;
230
16.2M
    }
231
155k
}
232
233
void
234
reconstructChromaVert (int n, const Rgba* const ycaIn[N], Rgba ycaOut[/*n*/])
235
126k
{
236
6.52M
    for (int i = 0; i < n; ++i)
237
6.39M
    {
238
6.39M
        ycaOut[i].r = ycaIn[0][i].r * 0.002128f + ycaIn[2][i].r * -0.007540f +
239
6.39M
                      ycaIn[4][i].r * 0.019597f + ycaIn[6][i].r * -0.043159f +
240
6.39M
                      ycaIn[8][i].r * 0.087929f + ycaIn[10][i].r * -0.186077f +
241
6.39M
                      ycaIn[12][i].r * 0.627123f + ycaIn[14][i].r * 0.627123f +
242
6.39M
                      ycaIn[16][i].r * -0.186077f + ycaIn[18][i].r * 0.087929f +
243
6.39M
                      ycaIn[20][i].r * -0.043159f + ycaIn[22][i].r * 0.019597f +
244
6.39M
                      ycaIn[24][i].r * -0.007540f + ycaIn[26][i].r * 0.002128f;
245
246
6.39M
        ycaOut[i].b = ycaIn[0][i].b * 0.002128f + ycaIn[2][i].b * -0.007540f +
247
6.39M
                      ycaIn[4][i].b * 0.019597f + ycaIn[6][i].b * -0.043159f +
248
6.39M
                      ycaIn[8][i].b * 0.087929f + ycaIn[10][i].b * -0.186077f +
249
6.39M
                      ycaIn[12][i].b * 0.627123f + ycaIn[14][i].b * 0.627123f +
250
6.39M
                      ycaIn[16][i].b * -0.186077f + ycaIn[18][i].b * 0.087929f +
251
6.39M
                      ycaIn[20][i].b * -0.043159f + ycaIn[22][i].b * 0.019597f +
252
6.39M
                      ycaIn[24][i].b * -0.007540f + ycaIn[26][i].b * 0.002128f;
253
254
6.39M
        ycaOut[i].g = ycaIn[13][i].g;
255
6.39M
        ycaOut[i].a = ycaIn[13][i].a;
256
6.39M
    }
257
126k
}
258
259
void
260
YCAtoRGBA (
261
    const IMATH_NAMESPACE::V3f& yw,
262
    int                         n,
263
    const Rgba                  ycaIn[/*n*/],
264
    Rgba                        rgbaOut[/*n*/])
265
253k
{
266
13.0M
    for (int i = 0; i < n; ++i)
267
12.7M
    {
268
12.7M
        const Rgba& in  = ycaIn[i];
269
12.7M
        Rgba&       out = rgbaOut[i];
270
271
12.7M
        if (in.r == 0 && in.b == 0)
272
7.16M
        {
273
            //
274
            // Special case -- both chroma channels are 0.  To avoid
275
            // rounding errors, we explicitly set the output R, G and B
276
            // channels equal to the input luminance.
277
            //
278
            // The special cases here and in RGBAtoYCA() ensure that
279
            // converting black-and white images from RGBA to YCA and
280
            // back is lossless.
281
            //
282
283
7.16M
            out.r = in.g;
284
7.16M
            out.g = in.g;
285
7.16M
            out.b = in.g;
286
7.16M
            out.a = in.a;
287
7.16M
        }
288
5.62M
        else
289
5.62M
        {
290
5.62M
            float Y = in.g;
291
5.62M
            float r = (in.r + 1) * Y;
292
5.62M
            float b = (in.b + 1) * Y;
293
5.62M
            float g = (Y - r * yw.x - b * yw.z) / yw.y;
294
295
5.62M
            out.r = r;
296
5.62M
            out.g = g;
297
5.62M
            out.b = b;
298
5.62M
            out.a = in.a;
299
5.62M
        }
300
12.7M
    }
301
253k
}
302
303
namespace
304
{
305
306
inline float
307
saturation (const Rgba& in)
308
36.1M
{
309
36.1M
    float rgbMax = max (in.r, max (in.g, in.b));
310
36.1M
    float rgbMin = min (in.r, min (in.g, in.b));
311
312
36.1M
    if (rgbMax > 0)
313
4.53M
        return 1 - rgbMin / rgbMax;
314
31.6M
    else
315
31.6M
        return 0;
316
36.1M
}
317
318
void
319
desaturate (const Rgba& in, float f, const V3f& yw, Rgba& out)
320
122k
{
321
122k
    float rgbMax = max (in.r, max (in.g, in.b));
322
323
122k
    out.r = max (float (rgbMax - (rgbMax - in.r) * f), 0.0f);
324
122k
    out.g = max (float (rgbMax - (rgbMax - in.g) * f), 0.0f);
325
122k
    out.b = max (float (rgbMax - (rgbMax - in.b) * f), 0.0f);
326
122k
    out.a = in.a;
327
328
122k
    float Yin  = in.r * yw.x + in.g * yw.y + in.b * yw.z;
329
122k
    float Yout = out.r * yw.x + out.g * yw.y + out.b * yw.z;
330
331
122k
    if (Yout > 0)
332
114k
    {
333
114k
        out.r *= Yin / Yout;
334
114k
        out.g *= Yin / Yout;
335
114k
        out.b *= Yin / Yout;
336
114k
    }
337
122k
}
338
339
} // namespace
340
341
void
342
fixSaturation (
343
    const IMATH_NAMESPACE::V3f& yw,
344
    int                         n,
345
    const Rgba* const           rgbaIn[3],
346
    Rgba                        rgbaOut[/*n*/])
347
249k
{
348
249k
    float neighborA2 = saturation (rgbaIn[0][0]);
349
249k
    float neighborA1 = neighborA2;
350
351
249k
    float neighborB2 = saturation (rgbaIn[2][0]);
352
249k
    float neighborB1 = neighborB2;
353
354
12.3M
    for (int i = 0; i < n; ++i)
355
12.0M
    {
356
12.0M
        float neighborA0 = neighborA1;
357
12.0M
        neighborA1       = neighborA2;
358
359
12.0M
        float neighborB0 = neighborB1;
360
12.0M
        neighborB1       = neighborB2;
361
362
12.0M
        if (i < n - 1)
363
11.8M
        {
364
11.8M
            neighborA2 = saturation (rgbaIn[0][i + 1]);
365
11.8M
            neighborB2 = saturation (rgbaIn[2][i + 1]);
366
11.8M
        }
367
368
        //
369
        // A0       A1       A2
370
        //      rgbaOut[i]
371
        // B0       B1       B2
372
        //
373
374
12.0M
        float sMean = min (
375
12.0M
            1.0f, 0.25f * (neighborA0 + neighborA2 + neighborB0 + neighborB2));
376
377
12.0M
        const Rgba& in  = rgbaIn[1][i];
378
12.0M
        Rgba&       out = rgbaOut[i];
379
380
12.0M
        float s = saturation (in);
381
382
12.0M
        if (s > sMean)
383
165k
        {
384
165k
            float sMax = min (1.0f, 1 - (1 - sMean) * 0.25f);
385
386
165k
            if (s > sMax)
387
122k
            {
388
122k
                desaturate (in, sMax / s, yw, out);
389
122k
                continue;
390
122k
            }
391
165k
        }
392
393
11.9M
        out = in;
394
11.9M
    }
395
249k
}
396
397
} // namespace RgbaYca
398
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT