blob: 80a9596e3c0d52e51d31688e1109f6b7994e1efa [file] [log] [blame]
Nick Deakin65f492a2022-11-29 22:47:40 -05001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cmath>
18#include <gtest/gtest.h>
19#include <gmock/gmock.h>
20#include <jpegrecoverymap/recoverymapmath.h>
21
22namespace android::recoverymap {
23
24class RecoveryMapMathTest : public testing::Test {
25public:
26 RecoveryMapMathTest();
27 ~RecoveryMapMathTest();
28
29 float ComparisonEpsilon() { return 1e-4f; }
30 float LuminanceEpsilon() { return 1e-2f; }
31
32 Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
33 return {{{ static_cast<float>(y) / 255.0f,
34 (static_cast<float>(u) - 128.0f) / 255.0f,
35 (static_cast<float>(v) - 128.0f) / 255.0f }}};
36 }
37
38 Color P010(uint16_t y, uint16_t u, uint16_t v) {
Nick Deakin38125332022-12-12 15:48:24 -050039 return {{{ (static_cast<float>(y) - 64.0f) / 876.0f,
40 (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
41 (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f }}};
Nick Deakin65f492a2022-11-29 22:47:40 -050042 }
43
44 float Map(uint8_t e) {
Nick Deakind19e5762023-02-10 15:39:08 -050045 return static_cast<float>(e) / 255.0f;
Nick Deakin65f492a2022-11-29 22:47:40 -050046 }
47
48 Color ColorMin(Color e1, Color e2) {
49 return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}};
50 }
51
52 Color ColorMax(Color e1, Color e2) {
53 return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}};
54 }
55
56 Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
57 Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
58
59 Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
60 Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
61 Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
62
63 Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
64 Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
65
66 Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; }
67 Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; }
68 Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; }
69
70 Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
71 Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
72 Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
73
74 float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
75 Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
76 Color rgb = srgbInvOetf(rgb_gamma);
77 float luminance_scaled = luminanceFn(rgb);
78 return luminance_scaled * kSdrWhiteNits;
79 }
80
81 float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
82 ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
83 float scale_factor) {
84 Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
85 Color rgb = hdrInvOetf(rgb_gamma);
86 rgb = gamutConversionFn(rgb);
87 float luminance_scaled = luminanceFn(rgb);
88 return luminance_scaled * scale_factor;
89 }
90
Nick Deakind19e5762023-02-10 15:39:08 -050091 Color Recover(Color yuv_gamma, float recovery, jr_metadata_ptr metadata) {
Nick Deakin65f492a2022-11-29 22:47:40 -050092 Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
93 Color rgb = srgbInvOetf(rgb_gamma);
Nick Deakind19e5762023-02-10 15:39:08 -050094 return applyRecovery(rgb, recovery, metadata);
Nick Deakin65f492a2022-11-29 22:47:40 -050095 }
96
97 jpegr_uncompressed_struct Yuv420Image() {
98 static uint8_t pixels[] = {
99 // Y
100 0x00, 0x10, 0x20, 0x30,
101 0x01, 0x11, 0x21, 0x31,
102 0x02, 0x12, 0x22, 0x32,
103 0x03, 0x13, 0x23, 0x33,
104 // U
105 0xA0, 0xA1,
106 0xA2, 0xA3,
107 // V
108 0xB0, 0xB1,
109 0xB2, 0xB3,
110 };
111 return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
112 }
113
114 Color (*Yuv420Colors())[4] {
115 static Color colors[4][4] = {
116 {
117 Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
118 Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
119 }, {
120 Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
121 Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
122 }, {
123 Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
124 Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
125 }, {
126 Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
127 Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
128 },
129 };
130 return colors;
131 }
132
133 jpegr_uncompressed_struct P010Image() {
134 static uint16_t pixels[] = {
135 // Y
136 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
137 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
138 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
139 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
140 // UV
141 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
142 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
143 };
144 return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
145 }
146
147 Color (*P010Colors())[4] {
148 static Color colors[4][4] = {
149 {
150 P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
151 P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
152 }, {
153 P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
154 P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
155 }, {
156 P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
157 P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
158 }, {
159 P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
160 P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
161 },
162 };
163 return colors;
164 }
165
166 jpegr_uncompressed_struct MapImage() {
167 static uint8_t pixels[] = {
168 0x00, 0x10, 0x20, 0x30,
169 0x01, 0x11, 0x21, 0x31,
170 0x02, 0x12, 0x22, 0x32,
171 0x03, 0x13, 0x23, 0x33,
172 };
173 return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED };
174 }
175
176 float (*MapValues())[4] {
177 static float values[4][4] = {
178 {
179 Map(0x00), Map(0x10), Map(0x20), Map(0x30),
180 }, {
181 Map(0x01), Map(0x11), Map(0x21), Map(0x31),
182 }, {
183 Map(0x02), Map(0x12), Map(0x22), Map(0x32),
184 }, {
185 Map(0x03), Map(0x13), Map(0x23), Map(0x33),
186 },
187 };
188 return values;
189 }
190
191protected:
192 virtual void SetUp();
193 virtual void TearDown();
194};
195
196RecoveryMapMathTest::RecoveryMapMathTest() {}
197RecoveryMapMathTest::~RecoveryMapMathTest() {}
198
199void RecoveryMapMathTest::SetUp() {}
200void RecoveryMapMathTest::TearDown() {}
201
202#define EXPECT_RGB_EQ(e1, e2) \
203 EXPECT_FLOAT_EQ((e1).r, (e2).r); \
204 EXPECT_FLOAT_EQ((e1).g, (e2).g); \
205 EXPECT_FLOAT_EQ((e1).b, (e2).b)
206
207#define EXPECT_RGB_NEAR(e1, e2) \
208 EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
209 EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
210 EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
211
212#define EXPECT_RGB_CLOSE(e1, e2) \
213 EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
214 EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
215 EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
216
217#define EXPECT_YUV_EQ(e1, e2) \
218 EXPECT_FLOAT_EQ((e1).y, (e2).y); \
219 EXPECT_FLOAT_EQ((e1).u, (e2).u); \
220 EXPECT_FLOAT_EQ((e1).v, (e2).v)
221
222#define EXPECT_YUV_NEAR(e1, e2) \
223 EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
224 EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
225 EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
226
227#define EXPECT_YUV_BETWEEN(e, min, max) \
228 EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \
229 EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \
230 EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v)))
231
232// TODO: a bunch of these tests can be parameterized.
233
234TEST_F(RecoveryMapMathTest, ColorConstruct) {
235 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
236
237 EXPECT_FLOAT_EQ(e1.r, 0.1f);
238 EXPECT_FLOAT_EQ(e1.g, 0.2f);
239 EXPECT_FLOAT_EQ(e1.b, 0.3f);
240
241 EXPECT_FLOAT_EQ(e1.y, 0.1f);
242 EXPECT_FLOAT_EQ(e1.u, 0.2f);
243 EXPECT_FLOAT_EQ(e1.v, 0.3f);
244}
245
246TEST_F(RecoveryMapMathTest, ColorAddColor) {
247 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
248
249 Color e2 = e1 + e1;
250 EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
251 EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
252 EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
253
254 e2 += e1;
255 EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
256 EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
257 EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
258}
259
260TEST_F(RecoveryMapMathTest, ColorAddFloat) {
261 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
262
263 Color e2 = e1 + 0.1f;
264 EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
265 EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
266 EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
267
268 e2 += 0.1f;
269 EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
270 EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
271 EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
272}
273
274TEST_F(RecoveryMapMathTest, ColorSubtractColor) {
275 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
276
277 Color e2 = e1 - e1;
278 EXPECT_FLOAT_EQ(e2.r, 0.0f);
279 EXPECT_FLOAT_EQ(e2.g, 0.0f);
280 EXPECT_FLOAT_EQ(e2.b, 0.0f);
281
282 e2 -= e1;
283 EXPECT_FLOAT_EQ(e2.r, -e1.r);
284 EXPECT_FLOAT_EQ(e2.g, -e1.g);
285 EXPECT_FLOAT_EQ(e2.b, -e1.b);
286}
287
288TEST_F(RecoveryMapMathTest, ColorSubtractFloat) {
289 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
290
291 Color e2 = e1 - 0.1f;
292 EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
293 EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
294 EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
295
296 e2 -= 0.1f;
297 EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
298 EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
299 EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
300}
301
302TEST_F(RecoveryMapMathTest, ColorMultiplyFloat) {
303 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
304
305 Color e2 = e1 * 2.0f;
306 EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
307 EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
308 EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
309
310 e2 *= 2.0f;
311 EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
312 EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
313 EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
314}
315
316TEST_F(RecoveryMapMathTest, ColorDivideFloat) {
317 Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
318
319 Color e2 = e1 / 2.0f;
320 EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
321 EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
322 EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
323
324 e2 /= 2.0f;
325 EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
326 EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
327 EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
328}
329
330TEST_F(RecoveryMapMathTest, SrgbLuminance) {
331 EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
332 EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
333 EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
334 EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
335 EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
336}
337
338TEST_F(RecoveryMapMathTest, SrgbYuvToRgb) {
339 Color rgb_black = srgbYuvToRgb(YuvBlack());
340 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
341
342 Color rgb_white = srgbYuvToRgb(YuvWhite());
343 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
344
345 Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
346 EXPECT_RGB_NEAR(rgb_r, RgbRed());
347
348 Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
349 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
350
351 Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
352 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
353}
354
355TEST_F(RecoveryMapMathTest, SrgbRgbToYuv) {
356 Color yuv_black = srgbRgbToYuv(RgbBlack());
357 EXPECT_YUV_NEAR(yuv_black, YuvBlack());
358
359 Color yuv_white = srgbRgbToYuv(RgbWhite());
360 EXPECT_YUV_NEAR(yuv_white, YuvWhite());
361
362 Color yuv_r = srgbRgbToYuv(RgbRed());
363 EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
364
365 Color yuv_g = srgbRgbToYuv(RgbGreen());
366 EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
367
368 Color yuv_b = srgbRgbToYuv(RgbBlue());
369 EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
370}
371
372TEST_F(RecoveryMapMathTest, SrgbRgbYuvRoundtrip) {
373 Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
374 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
375
376 Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
377 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
378
379 Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
380 EXPECT_RGB_NEAR(rgb_r, RgbRed());
381
382 Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
383 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
384
385 Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
386 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
387}
388
389TEST_F(RecoveryMapMathTest, SrgbTransferFunction) {
390 EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
391 EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
392 EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
393 EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
394 EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
395}
396
397TEST_F(RecoveryMapMathTest, P3Luminance) {
398 EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
399 EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
400 EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
401 EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
402 EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
403}
404
405TEST_F(RecoveryMapMathTest, Bt2100Luminance) {
406 EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
407 EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
408 EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
409 EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
410 EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
411}
412
413TEST_F(RecoveryMapMathTest, Bt2100YuvToRgb) {
414 Color rgb_black = bt2100YuvToRgb(YuvBlack());
415 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
416
417 Color rgb_white = bt2100YuvToRgb(YuvWhite());
418 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
419
420 Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
421 EXPECT_RGB_NEAR(rgb_r, RgbRed());
422
423 Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
424 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
425
426 Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
427 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
428}
429
430TEST_F(RecoveryMapMathTest, Bt2100RgbToYuv) {
431 Color yuv_black = bt2100RgbToYuv(RgbBlack());
432 EXPECT_YUV_NEAR(yuv_black, YuvBlack());
433
434 Color yuv_white = bt2100RgbToYuv(RgbWhite());
435 EXPECT_YUV_NEAR(yuv_white, YuvWhite());
436
437 Color yuv_r = bt2100RgbToYuv(RgbRed());
438 EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
439
440 Color yuv_g = bt2100RgbToYuv(RgbGreen());
441 EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
442
443 Color yuv_b = bt2100RgbToYuv(RgbBlue());
444 EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
445}
446
447TEST_F(RecoveryMapMathTest, Bt2100RgbYuvRoundtrip) {
448 Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
449 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
450
451 Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
452 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
453
454 Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
455 EXPECT_RGB_NEAR(rgb_r, RgbRed());
456
457 Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
458 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
459
460 Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
461 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
462}
463
464TEST_F(RecoveryMapMathTest, HlgOetf) {
465 EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
466 EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
467 EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
468 EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
469 EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
470
471 Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
472 Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
473 EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
474}
475
476TEST_F(RecoveryMapMathTest, HlgInvOetf) {
477 EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
478 EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
479 EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
480 EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
481 EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
482
483 Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
484 Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
485 EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
486}
487
488TEST_F(RecoveryMapMathTest, HlgTransferFunctionRoundtrip) {
489 EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
490 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
491 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
492 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
493 EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
494}
495
496TEST_F(RecoveryMapMathTest, PqOetf) {
497 EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
498 EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
499 EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
500 EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
501 EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
502
503 Color e = {{{ 0.01f, 0.5f, 0.99f }}};
504 Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
505 EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
506}
507
508TEST_F(RecoveryMapMathTest, PqInvOetf) {
509 EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
510 EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
511 EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
512 EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
513 EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
514
515 Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
516 Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
517 EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
518}
519
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800520TEST_F(RecoveryMapMathTest, PqInvOetfLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500521 for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
522 float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800523 EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
524 }
525}
526
527TEST_F(RecoveryMapMathTest, HlgInvOetfLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500528 for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
529 float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800530 EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
531 }
532}
533
534TEST_F(RecoveryMapMathTest, pqOetfLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500535 for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
536 float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800537 EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
538 }
539}
540
541TEST_F(RecoveryMapMathTest, hlgOetfLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500542 for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
543 float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800544 EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
545 }
546}
547
548TEST_F(RecoveryMapMathTest, srgbInvOetfLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500549 for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
550 float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800551 EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
552 }
553}
554
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800555TEST_F(RecoveryMapMathTest, applyRecoveryLUT) {
Nick Deakind19e5762023-02-10 15:39:08 -0500556 for (int boost = 1; boost <= 10; boost++) {
557 jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
558 .minContentBoost = 1.0f / static_cast<float>(boost) };
559 RecoveryLUT recoveryLUT(&metadata);
560 for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
561 float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
562 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800563 applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
Nick Deakind19e5762023-02-10 15:39:08 -0500564 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800565 applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
Nick Deakind19e5762023-02-10 15:39:08 -0500566 EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800567 applyRecoveryLUT(RgbRed(), value, recoveryLUT));
Nick Deakind19e5762023-02-10 15:39:08 -0500568 EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800569 applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
Nick Deakind19e5762023-02-10 15:39:08 -0500570 EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
571 applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
572 }
573 }
574
575 for (int boost = 1; boost <= 10; boost++) {
576 jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
577 .minContentBoost = 1.0f };
578 RecoveryLUT recoveryLUT(&metadata);
579 for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
580 float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
581 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
582 applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
583 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
584 applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
585 EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
586 applyRecoveryLUT(RgbRed(), value, recoveryLUT));
587 EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
588 applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
589 EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
590 applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
591 }
592 }
593
594 for (int boost = 1; boost <= 10; boost++) {
595 jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
596 .minContentBoost = 1.0f / pow(static_cast<float>(boost),
597 1.0f / 3.0f) };
598 RecoveryLUT recoveryLUT(&metadata);
599 for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
600 float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
601 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
602 applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
603 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
604 applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
605 EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
606 applyRecoveryLUT(RgbRed(), value, recoveryLUT));
607 EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
608 applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
609 EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
Harish Mahendrakarf25991f2022-12-16 11:57:44 -0800610 applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
611 }
612 }
613}
614
Nick Deakin65f492a2022-11-29 22:47:40 -0500615TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) {
616 EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
617 EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
618 EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
619 EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
620 EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
621}
622
623TEST_F(RecoveryMapMathTest, ColorConversionLookup) {
624 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED),
625 nullptr);
626 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709),
627 identityConversion);
628 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3),
629 p3ToBt709);
630 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100),
631 bt2100ToBt709);
632
633 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED),
634 nullptr);
635 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709),
636 bt709ToP3);
637 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3),
638 identityConversion);
639 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100),
640 bt2100ToP3);
641
642 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED),
643 nullptr);
644 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709),
645 bt709ToBt2100);
646 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3),
647 p3ToBt2100);
648 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100),
649 identityConversion);
650
651 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED),
652 nullptr);
653 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709),
654 nullptr);
655 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3),
656 nullptr);
657 EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100),
658 nullptr);
659}
660
661TEST_F(RecoveryMapMathTest, EncodeRecovery) {
Nick Deakind19e5762023-02-10 15:39:08 -0500662 jpegr_metadata metadata = { .maxContentBoost = 4.0f,
663 .minContentBoost = 1.0f / 4.0f };
Nick Deakin65f492a2022-11-29 22:47:40 -0500664
Nick Deakind19e5762023-02-10 15:39:08 -0500665 EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127);
666 EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127);
667 EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
668 EXPECT_EQ(encodeRecovery(0.5f, 0.0f, &metadata), 0);
Nick Deakin65f492a2022-11-29 22:47:40 -0500669
Nick Deakind19e5762023-02-10 15:39:08 -0500670 EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 127);
671 EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 255);
672 EXPECT_EQ(encodeRecovery(1.0f, 5.0f, &metadata), 255);
673 EXPECT_EQ(encodeRecovery(4.0f, 1.0f, &metadata), 0);
674 EXPECT_EQ(encodeRecovery(4.0f, 0.5f, &metadata), 0);
675 EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 191);
676 EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 63);
Nick Deakin65f492a2022-11-29 22:47:40 -0500677
Nick Deakind19e5762023-02-10 15:39:08 -0500678 metadata.maxContentBoost = 2.0f;
679 metadata.minContentBoost = 1.0f / 2.0f;
680
681 EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 255);
682 EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 0);
683 EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, &metadata), 191);
684 EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, &metadata), 63);
685
686 metadata.maxContentBoost = 8.0f;
687 metadata.minContentBoost = 1.0f / 8.0f;
688
689 EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
690 EXPECT_EQ(encodeRecovery(8.0f, 1.0f, &metadata), 0);
691 EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, &metadata), 191);
692 EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, &metadata), 63);
693
694 metadata.maxContentBoost = 8.0f;
695 metadata.minContentBoost = 1.0f;
696
697 EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 0);
698 EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
699
700 EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 0);
701 EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
702 EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 170);
703 EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 85);
704
705 metadata.maxContentBoost = 8.0f;
706 metadata.minContentBoost = 0.5f;
707
708 EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 63);
709 EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
710
711 EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 63);
712 EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
713 EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 191);
714 EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 127);
715 EXPECT_EQ(encodeRecovery(1.0f, 0.7071f, &metadata), 31);
716 EXPECT_EQ(encodeRecovery(1.0f, 0.5f, &metadata), 0);
Nick Deakin65f492a2022-11-29 22:47:40 -0500717}
718
719TEST_F(RecoveryMapMathTest, ApplyRecovery) {
Nick Deakind19e5762023-02-10 15:39:08 -0500720 jpegr_metadata metadata = { .maxContentBoost = 4.0f,
721 .minContentBoost = 1.0f / 4.0f };
Nick Deakin65f492a2022-11-29 22:47:40 -0500722
Nick Deakind19e5762023-02-10 15:39:08 -0500723 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack());
724 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack());
725 EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, &metadata), RgbBlack());
Nick Deakin65f492a2022-11-29 22:47:40 -0500726
Nick Deakind19e5762023-02-10 15:39:08 -0500727 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f);
728 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f);
729 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
730 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f);
731 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f);
Nick Deakin65f492a2022-11-29 22:47:40 -0500732
Nick Deakind19e5762023-02-10 15:39:08 -0500733 metadata.maxContentBoost = 2.0f;
734 metadata.minContentBoost = 1.0f / 2.0f;
735
736 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
737 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f);
738 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
739 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f);
740 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f);
741
742 metadata.maxContentBoost = 8.0f;
743 metadata.minContentBoost = 1.0f / 8.0f;
744
745 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
746 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f);
747 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
748 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f);
749 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
750
751 metadata.maxContentBoost = 8.0f;
752 metadata.minContentBoost = 1.0f;
753
754 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite());
755 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
756 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
757 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
758
759 metadata.maxContentBoost = 8.0f;
760 metadata.minContentBoost = 0.5f;
761
762 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
763 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite());
764 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
765 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
766 EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
Nick Deakin65f492a2022-11-29 22:47:40 -0500767
768 Color e = {{{ 0.0f, 0.5f, 1.0f }}};
Nick Deakind19e5762023-02-10 15:39:08 -0500769 metadata.maxContentBoost = 4.0f;
770 metadata.minContentBoost = 1.0f / 4.0f;
Nick Deakin65f492a2022-11-29 22:47:40 -0500771
Nick Deakind19e5762023-02-10 15:39:08 -0500772 EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, &metadata), e / 4.0f);
773 EXPECT_RGB_NEAR(applyRecovery(e, 0.25f, &metadata), e / 2.0f);
774 EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, &metadata), e);
775 EXPECT_RGB_NEAR(applyRecovery(e, 0.75f, &metadata), e * 2.0f);
776 EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, &metadata), e * 4.0f);
Nick Deakin65f492a2022-11-29 22:47:40 -0500777}
778
779TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
780 jpegr_uncompressed_struct image = Yuv420Image();
781 Color (*colors)[4] = Yuv420Colors();
782
783 for (size_t y = 0; y < 4; ++y) {
784 for (size_t x = 0; x < 4; ++x) {
785 EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
786 }
787 }
788}
789
790TEST_F(RecoveryMapMathTest, GetP010Pixel) {
791 jpegr_uncompressed_struct image = P010Image();
792 Color (*colors)[4] = P010Colors();
793
794 for (size_t y = 0; y < 4; ++y) {
795 for (size_t x = 0; x < 4; ++x) {
796 EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
797 }
798 }
799}
800
801TEST_F(RecoveryMapMathTest, SampleYuv420) {
802 jpegr_uncompressed_struct image = Yuv420Image();
803 Color (*colors)[4] = Yuv420Colors();
804
805 static const size_t kMapScaleFactor = 2;
806 for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
807 for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
808 Color min = {{{ 1.0f, 1.0f, 1.0f }}};
809 Color max = {{{ -1.0f, -1.0f, -1.0f }}};
810
811 for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
812 for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
813 Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
814 min = ColorMin(min, e);
815 max = ColorMax(max, e);
816 }
817 }
818
819 // Instead of reimplementing the sampling algorithm, confirm that the
820 // sample output is within the range of the min and max of the nearest
821 // points.
822 EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
823 }
824 }
825}
826
827TEST_F(RecoveryMapMathTest, SampleP010) {
828 jpegr_uncompressed_struct image = P010Image();
829 Color (*colors)[4] = P010Colors();
830
831 static const size_t kMapScaleFactor = 2;
832 for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
833 for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
834 Color min = {{{ 1.0f, 1.0f, 1.0f }}};
835 Color max = {{{ -1.0f, -1.0f, -1.0f }}};
836
837 for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
838 for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
839 Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
840 min = ColorMin(min, e);
841 max = ColorMax(max, e);
842 }
843 }
844
845 // Instead of reimplementing the sampling algorithm, confirm that the
846 // sample output is within the range of the min and max of the nearest
847 // points.
848 EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
849 }
850 }
851}
852
853TEST_F(RecoveryMapMathTest, SampleMap) {
854 jpegr_uncompressed_struct image = MapImage();
855 float (*values)[4] = MapValues();
856
857 static const size_t kMapScaleFactor = 2;
Ram Mohanfe723d62022-12-15 00:59:11 +0530858 ShepardsIDW idwTable(kMapScaleFactor);
Nick Deakin65f492a2022-11-29 22:47:40 -0500859 for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
860 for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
861 size_t x_base = x / kMapScaleFactor;
862 size_t y_base = y / kMapScaleFactor;
863
864 float min = 1.0f;
865 float max = -1.0f;
866
867 min = fmin(min, values[y_base][x_base]);
868 max = fmax(max, values[y_base][x_base]);
869 if (y_base + 1 < 4) {
870 min = fmin(min, values[y_base + 1][x_base]);
871 max = fmax(max, values[y_base + 1][x_base]);
872 }
873 if (x_base + 1 < 4) {
874 min = fmin(min, values[y_base][x_base + 1]);
875 max = fmax(max, values[y_base][x_base + 1]);
876 }
877 if (y_base + 1 < 4 && x_base + 1 < 4) {
878 min = fmin(min, values[y_base + 1][x_base + 1]);
879 max = fmax(max, values[y_base + 1][x_base + 1]);
880 }
881
882 // Instead of reimplementing the sampling algorithm, confirm that the
883 // sample output is within the range of the min and max of the nearest
884 // points.
Nick Deakind19e5762023-02-10 15:39:08 -0500885 EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
Nick Deakin65f492a2022-11-29 22:47:40 -0500886 testing::AllOf(testing::Ge(min), testing::Le(max)));
Nick Deakind19e5762023-02-10 15:39:08 -0500887 EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
888 sampleMap(&image, kMapScaleFactor, x, y));
Nick Deakin65f492a2022-11-29 22:47:40 -0500889 }
890 }
891}
892
893TEST_F(RecoveryMapMathTest, ColorToRgba1010102) {
894 EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
895 EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
896 EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
897 EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
898 EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
899
900 Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
901 EXPECT_EQ(colorToRgba1010102(e_gamma),
902 0x3 << 30
903 | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
904 | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
905 | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
906}
907
908TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) {
909 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
910 0.0f);
911 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
912 kSdrWhiteNits);
913 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
914 srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
915 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
916 srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
917 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
918 srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
919}
920
921TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbP3) {
922 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
923 0.0f);
924 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
925 kSdrWhiteNits);
926 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
927 p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
928 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
929 p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
930 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
931 p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
932}
933
934TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbBt2100) {
935 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
936 0.0f);
937 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
938 kSdrWhiteNits);
939 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
940 bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
941 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
942 bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
943 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
944 bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
945}
946
947TEST_F(RecoveryMapMathTest, GenerateMapLuminanceHlg) {
948 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
949 bt2100Luminance, kHlgMaxNits),
950 0.0f);
951 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
952 bt2100Luminance, kHlgMaxNits),
953 kHlgMaxNits);
954 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
955 bt2100Luminance, kHlgMaxNits),
956 bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
957 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
958 bt2100Luminance, kHlgMaxNits),
959 bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
960 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
961 bt2100Luminance, kHlgMaxNits),
962 bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
963}
964
965TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) {
966 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
967 bt2100Luminance, kPqMaxNits),
968 0.0f);
969 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
970 bt2100Luminance, kPqMaxNits),
971 kPqMaxNits);
972 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
973 bt2100Luminance, kPqMaxNits),
974 bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
975 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
976 bt2100Luminance, kPqMaxNits),
977 bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
978 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
979 bt2100Luminance, kPqMaxNits),
980 bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
981}
982
Nick Deakin65f492a2022-11-29 22:47:40 -0500983TEST_F(RecoveryMapMathTest, ApplyMap) {
Nick Deakind19e5762023-02-10 15:39:08 -0500984 jpegr_metadata metadata = { .maxContentBoost = 8.0f,
985 .minContentBoost = 1.0f / 8.0f };
986
987 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500988 RgbWhite() * 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -0500989 EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500990 RgbBlack());
Nick Deakind19e5762023-02-10 15:39:08 -0500991 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500992 RgbRed() * 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -0500993 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500994 RgbGreen() * 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -0500995 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500996 RgbBlue() * 8.0f);
997
Nick Deakind19e5762023-02-10 15:39:08 -0500998 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -0500999 RgbWhite() * sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001000 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001001 RgbBlack());
Nick Deakind19e5762023-02-10 15:39:08 -05001002 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001003 RgbRed() * sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001004 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001005 RgbGreen() * sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001006 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001007 RgbBlue() * sqrt(8.0f));
1008
Nick Deakind19e5762023-02-10 15:39:08 -05001009 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001010 RgbWhite());
Nick Deakind19e5762023-02-10 15:39:08 -05001011 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001012 RgbBlack());
Nick Deakind19e5762023-02-10 15:39:08 -05001013 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001014 RgbRed());
Nick Deakind19e5762023-02-10 15:39:08 -05001015 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001016 RgbGreen());
Nick Deakind19e5762023-02-10 15:39:08 -05001017 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001018 RgbBlue());
1019
Nick Deakind19e5762023-02-10 15:39:08 -05001020 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001021 RgbWhite() / sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001022 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001023 RgbBlack());
Nick Deakind19e5762023-02-10 15:39:08 -05001024 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001025 RgbRed() / sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001026 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001027 RgbGreen() / sqrt(8.0f));
Nick Deakind19e5762023-02-10 15:39:08 -05001028 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001029 RgbBlue() / sqrt(8.0f));
1030
Nick Deakind19e5762023-02-10 15:39:08 -05001031 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001032 RgbWhite() / 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -05001033 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001034 RgbBlack());
Nick Deakind19e5762023-02-10 15:39:08 -05001035 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001036 RgbRed() / 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -05001037 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001038 RgbGreen() / 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -05001039 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata),
Nick Deakin65f492a2022-11-29 22:47:40 -05001040 RgbBlue() / 8.0f);
Nick Deakind19e5762023-02-10 15:39:08 -05001041
1042 metadata.maxContentBoost = 8.0f;
1043 metadata.minContentBoost = 1.0f;
1044
1045 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
1046 RgbWhite() * 8.0f);
1047 EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata),
1048 RgbWhite() * 4.0f);
1049 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata),
1050 RgbWhite() * 2.0f);
1051 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
1052 RgbWhite());
1053
1054 metadata.maxContentBoost = 8.0f;
1055 metadata.minContentBoost = 0.5f;;
1056
1057 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
1058 RgbWhite() * 8.0f);
1059 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata),
1060 RgbWhite() * 4.0f);
1061 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
1062 RgbWhite() * 2.0f);
1063 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
1064 RgbWhite());
1065 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
1066 RgbWhite() / 2.0f);
Nick Deakin65f492a2022-11-29 22:47:40 -05001067}
1068
1069} // namespace android::recoverymap