1 /*
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 <gtest/gtest.h>
18 #include <gmock/gmock.h>
19
20 #include "ultrahdr/gainmapmath.h"
21
22 namespace ultrahdr {
23
24 class GainMapMathTest : public testing::Test {
25 public:
26 GainMapMathTest();
27 ~GainMapMathTest();
28
ComparisonEpsilon()29 float ComparisonEpsilon() { return 1e-4f; }
LuminanceEpsilon()30 float LuminanceEpsilon() { return 1e-2f; }
YuvConversionEpsilon()31 float YuvConversionEpsilon() { return 1.0f / (255.0f * 2.0f); }
32
Yuv420(uint8_t y,uint8_t u,uint8_t v)33 Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
34 return {{{static_cast<float>(y) * (1 / 255.0f), static_cast<float>(u - 128) * (1 / 255.0f),
35 static_cast<float>(v - 128) * (1 / 255.0f)}}};
36 }
37
P010(uint16_t y,uint16_t u,uint16_t v)38 Color P010(uint16_t y, uint16_t u, uint16_t v) {
39 return {{{static_cast<float>(y - 64) * (1 / 876.0f),
40 static_cast<float>(u - 64) * (1 / 896.0f) - 0.5f,
41 static_cast<float>(v - 64) * (1 / 896.0f) - 0.5f}}};
42 }
43
44 // Using int16_t allows for testing fixed-point implementations.
45 struct Pixel {
46 int16_t y;
47 int16_t u;
48 int16_t v;
49 };
50
getYuv420Pixel_uint(jr_uncompressed_ptr image,size_t x,size_t y)51 Pixel getYuv420Pixel_uint(jr_uncompressed_ptr image, size_t x, size_t y) {
52 uint8_t* luma_data = reinterpret_cast<uint8_t*>(image->data);
53 size_t luma_stride = image->luma_stride;
54 uint8_t* chroma_data = reinterpret_cast<uint8_t*>(image->chroma_data);
55 size_t chroma_stride = image->chroma_stride;
56
57 size_t offset_cr = chroma_stride * (image->height / 2);
58 size_t pixel_y_idx = x + y * luma_stride;
59 size_t pixel_chroma_idx = x / 2 + (y / 2) * chroma_stride;
60
61 uint8_t y_uint = luma_data[pixel_y_idx];
62 uint8_t u_uint = chroma_data[pixel_chroma_idx];
63 uint8_t v_uint = chroma_data[offset_cr + pixel_chroma_idx];
64
65 return {y_uint, u_uint, v_uint};
66 }
67
Map(uint8_t e)68 float Map(uint8_t e) { return static_cast<float>(e) / 255.0f; }
69
ColorMin(Color e1,Color e2)70 Color ColorMin(Color e1, Color e2) {
71 return {{{fminf(e1.r, e2.r), fminf(e1.g, e2.g), fminf(e1.b, e2.b)}}};
72 }
73
ColorMax(Color e1,Color e2)74 Color ColorMax(Color e1, Color e2) {
75 return {{{fmaxf(e1.r, e2.r), fmaxf(e1.g, e2.g), fmaxf(e1.b, e2.b)}}};
76 }
77
RgbBlack()78 Color RgbBlack() { return {{{0.0f, 0.0f, 0.0f}}}; }
RgbWhite()79 Color RgbWhite() { return {{{1.0f, 1.0f, 1.0f}}}; }
80
RgbRed()81 Color RgbRed() { return {{{1.0f, 0.0f, 0.0f}}}; }
RgbGreen()82 Color RgbGreen() { return {{{0.0f, 1.0f, 0.0f}}}; }
RgbBlue()83 Color RgbBlue() { return {{{0.0f, 0.0f, 1.0f}}}; }
84
YuvBlack()85 Color YuvBlack() { return {{{0.0f, 0.0f, 0.0f}}}; }
YuvWhite()86 Color YuvWhite() { return {{{1.0f, 0.0f, 0.0f}}}; }
87
SrgbYuvRed()88 Color SrgbYuvRed() { return {{{0.2126f, -0.11457f, 0.5f}}}; }
SrgbYuvGreen()89 Color SrgbYuvGreen() { return {{{0.7152f, -0.38543f, -0.45415f}}}; }
SrgbYuvBlue()90 Color SrgbYuvBlue() { return {{{0.0722f, 0.5f, -0.04585f}}}; }
91
P3YuvRed()92 Color P3YuvRed() { return {{{0.299f, -0.16874f, 0.5f}}}; }
P3YuvGreen()93 Color P3YuvGreen() { return {{{0.587f, -0.33126f, -0.41869f}}}; }
P3YuvBlue()94 Color P3YuvBlue() { return {{{0.114f, 0.5f, -0.08131f}}}; }
95
Bt2100YuvRed()96 Color Bt2100YuvRed() { return {{{0.2627f, -0.13963f, 0.5f}}}; }
Bt2100YuvGreen()97 Color Bt2100YuvGreen() { return {{{0.6780f, -0.36037f, -0.45979f}}}; }
Bt2100YuvBlue()98 Color Bt2100YuvBlue() { return {{{0.0593f, 0.5f, -0.04021f}}}; }
99
100 //////////////////////////////////////////////////////////////////////////////
101 // Reference values for when using fixed-point arithmetic.
102
RgbBlackPixel()103 Pixel RgbBlackPixel() { return {0, 0, 0}; }
RgbWhitePixel()104 Pixel RgbWhitePixel() { return {255, 255, 255}; }
105
RgbRedPixel()106 Pixel RgbRedPixel() { return {255, 0, 0}; }
RgbGreenPixel()107 Pixel RgbGreenPixel() { return {0, 255, 0}; }
RgbBluePixel()108 Pixel RgbBluePixel() { return {0, 0, 255}; }
109
YuvBlackPixel()110 Pixel YuvBlackPixel() { return {0, 0, 0}; }
YuvWhitePixel()111 Pixel YuvWhitePixel() { return {255, 0, 0}; }
112
SrgbYuvRedPixel()113 Pixel SrgbYuvRedPixel() { return {54, -29, 128}; }
SrgbYuvGreenPixel()114 Pixel SrgbYuvGreenPixel() { return {182, -98, -116}; }
SrgbYuvBluePixel()115 Pixel SrgbYuvBluePixel() { return {18, 128, -12}; }
116
P3YuvRedPixel()117 Pixel P3YuvRedPixel() { return {76, -43, 128}; }
P3YuvGreenPixel()118 Pixel P3YuvGreenPixel() { return {150, -84, -107}; }
P3YuvBluePixel()119 Pixel P3YuvBluePixel() { return {29, 128, -21}; }
120
Bt2100YuvRedPixel()121 Pixel Bt2100YuvRedPixel() { return {67, -36, 128}; }
Bt2100YuvGreenPixel()122 Pixel Bt2100YuvGreenPixel() { return {173, -92, -117}; }
Bt2100YuvBluePixel()123 Pixel Bt2100YuvBluePixel() { return {15, 128, -10}; }
124
SrgbYuvToLuminance(Color yuv_gamma,ColorCalculationFn luminanceFn)125 float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
126 Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
127 Color rgb = srgbInvOetf(rgb_gamma);
128 float luminance_scaled = luminanceFn(rgb);
129 return luminance_scaled * kSdrWhiteNits;
130 }
131
P3YuvToLuminance(Color yuv_gamma,ColorCalculationFn luminanceFn)132 float P3YuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
133 Color rgb_gamma = p3YuvToRgb(yuv_gamma);
134 Color rgb = srgbInvOetf(rgb_gamma);
135 float luminance_scaled = luminanceFn(rgb);
136 return luminance_scaled * kSdrWhiteNits;
137 }
138
Bt2100YuvToLuminance(Color yuv_gamma,ColorTransformFn hdrInvOetf,ColorTransformFn gamutConversionFn,ColorCalculationFn luminanceFn,float scale_factor)139 float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
140 ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
141 float scale_factor) {
142 Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
143 Color rgb = hdrInvOetf(rgb_gamma);
144 rgb = gamutConversionFn(rgb);
145 float luminance_scaled = luminanceFn(rgb);
146 return luminance_scaled * scale_factor;
147 }
148
Recover(Color yuv_gamma,float gain,ultrahdr_metadata_ptr metadata)149 Color Recover(Color yuv_gamma, float gain, ultrahdr_metadata_ptr metadata) {
150 Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
151 Color rgb = srgbInvOetf(rgb_gamma);
152 return applyGain(rgb, gain, metadata);
153 }
154
Yuv420Image()155 jpegr_uncompressed_struct Yuv420Image() {
156 static uint8_t pixels[] = {
157 // Y
158 0x00,
159 0x10,
160 0x20,
161 0x30,
162 0x01,
163 0x11,
164 0x21,
165 0x31,
166 0x02,
167 0x12,
168 0x22,
169 0x32,
170 0x03,
171 0x13,
172 0x23,
173 0x33,
174 // U
175 0xA0,
176 0xA1,
177 0xA2,
178 0xA3,
179 // V
180 0xB0,
181 0xB1,
182 0xB2,
183 0xB3,
184 };
185 return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2};
186 }
187
Yuv420Image32x4()188 jpegr_uncompressed_struct Yuv420Image32x4() {
189 // clang-format off
190 static uint8_t pixels[] = {
191 // Y
192 0x0, 0x10, 0x20, 0x30, 0x1, 0x11, 0x21, 0x31, 0x2, 0x12, 0x22, 0x32, 0x3, 0x13, 0x23, 0x33,
193 0x4, 0x14, 0x24, 0x34, 0x5, 0x15, 0x25, 0x35, 0x6, 0x16, 0x26, 0x36, 0x7, 0x17, 0x27, 0x37,
194 0x8, 0x18, 0x28, 0x38, 0x9, 0x19, 0x29, 0x39, 0xa, 0x1a, 0x2a, 0x3a, 0xb, 0x1b, 0x2b, 0x3b,
195 0xc, 0x1c, 0x2c, 0x3c, 0xd, 0x1d, 0x2d, 0x3d, 0xe, 0x1e, 0x2e, 0x3e, 0xf, 0x1f, 0x2f, 0x3f,
196 0x10, 0x20, 0x30, 0x40, 0x11, 0x21, 0x31, 0x41, 0x12, 0x22, 0x32, 0x42, 0x13, 0x23, 0x33, 0x43,
197 0x14, 0x24, 0x34, 0x44, 0x15, 0x25, 0x35, 0x45, 0x16, 0x26, 0x36, 0x46, 0x17, 0x27, 0x37, 0x47,
198 0x18, 0x28, 0x38, 0x48, 0x19, 0x29, 0x39, 0x49, 0x1a, 0x2a, 0x3a, 0x4a, 0x1b, 0x2b, 0x3b, 0x4b,
199 0x1c, 0x2c, 0x3c, 0x4c, 0x1d, 0x2d, 0x3d, 0x4d, 0x1e, 0x2e, 0x3e, 0x4e, 0x1f, 0x2f, 0x3f, 0x4f,
200 // U
201 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
202 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBB, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
203 // V
204 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCC, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
205 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDD, 0xDD, 0xDC, 0xDD, 0xDE, 0xDF,
206 };
207 // clang-format on
208 return {pixels, 32, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 128, 32, 16};
209 }
210
__anon6a980e0a0102null211 Color (*Yuv420Colors())[4] {
212 static Color colors[4][4] = {
213 {
214 Yuv420(0x00, 0xA0, 0xB0),
215 Yuv420(0x10, 0xA0, 0xB0),
216 Yuv420(0x20, 0xA1, 0xB1),
217 Yuv420(0x30, 0xA1, 0xB1),
218 },
219 {
220 Yuv420(0x01, 0xA0, 0xB0),
221 Yuv420(0x11, 0xA0, 0xB0),
222 Yuv420(0x21, 0xA1, 0xB1),
223 Yuv420(0x31, 0xA1, 0xB1),
224 },
225 {
226 Yuv420(0x02, 0xA2, 0xB2),
227 Yuv420(0x12, 0xA2, 0xB2),
228 Yuv420(0x22, 0xA3, 0xB3),
229 Yuv420(0x32, 0xA3, 0xB3),
230 },
231 {
232 Yuv420(0x03, 0xA2, 0xB2),
233 Yuv420(0x13, 0xA2, 0xB2),
234 Yuv420(0x23, 0xA3, 0xB3),
235 Yuv420(0x33, 0xA3, 0xB3),
236 },
237 };
238 return colors;
239 }
240
241 jpegr_uncompressed_struct P010Image() {
242 static uint16_t pixels[] = {
243 // Y
244 0x00 << 6,
245 0x10 << 6,
246 0x20 << 6,
247 0x30 << 6,
248 0x01 << 6,
249 0x11 << 6,
250 0x21 << 6,
251 0x31 << 6,
252 0x02 << 6,
253 0x12 << 6,
254 0x22 << 6,
255 0x32 << 6,
256 0x03 << 6,
257 0x13 << 6,
258 0x23 << 6,
259 0x33 << 6,
260 // UV
261 0xA0 << 6,
262 0xB0 << 6,
263 0xA1 << 6,
264 0xB1 << 6,
265 0xA2 << 6,
266 0xB2 << 6,
267 0xA3 << 6,
268 0xB3 << 6,
269 };
270 return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4};
271 }
272
__anon6a980e0a0202null273 Color (*P010Colors())[4] {
274 static Color colors[4][4] = {
275 {
276 P010(0x00, 0xA0, 0xB0),
277 P010(0x10, 0xA0, 0xB0),
278 P010(0x20, 0xA1, 0xB1),
279 P010(0x30, 0xA1, 0xB1),
280 },
281 {
282 P010(0x01, 0xA0, 0xB0),
283 P010(0x11, 0xA0, 0xB0),
284 P010(0x21, 0xA1, 0xB1),
285 P010(0x31, 0xA1, 0xB1),
286 },
287 {
288 P010(0x02, 0xA2, 0xB2),
289 P010(0x12, 0xA2, 0xB2),
290 P010(0x22, 0xA3, 0xB3),
291 P010(0x32, 0xA3, 0xB3),
292 },
293 {
294 P010(0x03, 0xA2, 0xB2),
295 P010(0x13, 0xA2, 0xB2),
296 P010(0x23, 0xA3, 0xB3),
297 P010(0x33, 0xA3, 0xB3),
298 },
299 };
300 return colors;
301 }
302
303 jpegr_uncompressed_struct MapImage() {
304 static uint8_t pixels[] = {
305 0x00, 0x10, 0x20, 0x30, 0x01, 0x11, 0x21, 0x31,
306 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33,
307 };
308 return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_UNSPECIFIED};
309 }
310
__anon6a980e0a0302null311 float (*MapValues())[4] {
312 static float values[4][4] = {
313 {
314 Map(0x00),
315 Map(0x10),
316 Map(0x20),
317 Map(0x30),
318 },
319 {
320 Map(0x01),
321 Map(0x11),
322 Map(0x21),
323 Map(0x31),
324 },
325 {
326 Map(0x02),
327 Map(0x12),
328 Map(0x22),
329 Map(0x32),
330 },
331 {
332 Map(0x03),
333 Map(0x13),
334 Map(0x23),
335 Map(0x33),
336 },
337 };
338 return values;
339 }
340
341 protected:
342 virtual void SetUp();
343 virtual void TearDown();
344 };
345
GainMapMathTest()346 GainMapMathTest::GainMapMathTest() {}
~GainMapMathTest()347 GainMapMathTest::~GainMapMathTest() {}
348
SetUp()349 void GainMapMathTest::SetUp() {}
TearDown()350 void GainMapMathTest::TearDown() {}
351
352 #define EXPECT_RGB_EQ(e1, e2) \
353 EXPECT_FLOAT_EQ((e1).r, (e2).r); \
354 EXPECT_FLOAT_EQ((e1).g, (e2).g); \
355 EXPECT_FLOAT_EQ((e1).b, (e2).b)
356
357 #define EXPECT_RGB_NEAR(e1, e2) \
358 EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
359 EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
360 EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
361
362 #define EXPECT_RGB_CLOSE(e1, e2) \
363 EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
364 EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
365 EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
366
367 #define EXPECT_YUV_EQ(e1, e2) \
368 EXPECT_FLOAT_EQ((e1).y, (e2).y); \
369 EXPECT_FLOAT_EQ((e1).u, (e2).u); \
370 EXPECT_FLOAT_EQ((e1).v, (e2).v)
371
372 #define EXPECT_YUV_NEAR(e1, e2) \
373 EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
374 EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
375 EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
376
377 // Due to -ffp-contract=fast being enabled by default with GCC, allow some
378 // margin when comparing fused and unfused floating-point operations.
379 #define EXPECT_YUV_BETWEEN(e, min, max) \
380 EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y - ComparisonEpsilon()), \
381 testing::Le((max).y + ComparisonEpsilon()))); \
382 EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u - ComparisonEpsilon()), \
383 testing::Le((max).u + ComparisonEpsilon()))); \
384 EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v - ComparisonEpsilon()), \
385 testing::Le((max).v + ComparisonEpsilon())))
386
387 // TODO: a bunch of these tests can be parameterized.
388
TEST_F(GainMapMathTest,ColorConstruct)389 TEST_F(GainMapMathTest, ColorConstruct) {
390 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
391
392 EXPECT_FLOAT_EQ(e1.r, 0.1f);
393 EXPECT_FLOAT_EQ(e1.g, 0.2f);
394 EXPECT_FLOAT_EQ(e1.b, 0.3f);
395
396 EXPECT_FLOAT_EQ(e1.y, 0.1f);
397 EXPECT_FLOAT_EQ(e1.u, 0.2f);
398 EXPECT_FLOAT_EQ(e1.v, 0.3f);
399 }
400
TEST_F(GainMapMathTest,ColorAddColor)401 TEST_F(GainMapMathTest, ColorAddColor) {
402 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
403
404 Color e2 = e1 + e1;
405 EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
406 EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
407 EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
408
409 e2 += e1;
410 EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
411 EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
412 EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
413 }
414
TEST_F(GainMapMathTest,ColorAddFloat)415 TEST_F(GainMapMathTest, ColorAddFloat) {
416 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
417
418 Color e2 = e1 + 0.1f;
419 EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
420 EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
421 EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
422
423 e2 += 0.1f;
424 EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
425 EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
426 EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
427 }
428
TEST_F(GainMapMathTest,ColorSubtractColor)429 TEST_F(GainMapMathTest, ColorSubtractColor) {
430 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
431
432 Color e2 = e1 - e1;
433 EXPECT_FLOAT_EQ(e2.r, 0.0f);
434 EXPECT_FLOAT_EQ(e2.g, 0.0f);
435 EXPECT_FLOAT_EQ(e2.b, 0.0f);
436
437 e2 -= e1;
438 EXPECT_FLOAT_EQ(e2.r, -e1.r);
439 EXPECT_FLOAT_EQ(e2.g, -e1.g);
440 EXPECT_FLOAT_EQ(e2.b, -e1.b);
441 }
442
TEST_F(GainMapMathTest,ColorSubtractFloat)443 TEST_F(GainMapMathTest, ColorSubtractFloat) {
444 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
445
446 Color e2 = e1 - 0.1f;
447 EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
448 EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
449 EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
450
451 e2 -= 0.1f;
452 EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
453 EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
454 EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
455 }
456
TEST_F(GainMapMathTest,ColorMultiplyFloat)457 TEST_F(GainMapMathTest, ColorMultiplyFloat) {
458 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
459
460 Color e2 = e1 * 2.0f;
461 EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
462 EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
463 EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
464
465 e2 *= 2.0f;
466 EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
467 EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
468 EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
469 }
470
TEST_F(GainMapMathTest,ColorDivideFloat)471 TEST_F(GainMapMathTest, ColorDivideFloat) {
472 Color e1 = {{{0.1f, 0.2f, 0.3f}}};
473
474 Color e2 = e1 / 2.0f;
475 EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
476 EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
477 EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
478
479 e2 /= 2.0f;
480 EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
481 EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
482 EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
483 }
484
TEST_F(GainMapMathTest,SrgbLuminance)485 TEST_F(GainMapMathTest, SrgbLuminance) {
486 EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
487 EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
488 EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
489 EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
490 EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
491 }
492
TEST_F(GainMapMathTest,SrgbYuvToRgb)493 TEST_F(GainMapMathTest, SrgbYuvToRgb) {
494 Color rgb_black = srgbYuvToRgb(YuvBlack());
495 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
496
497 Color rgb_white = srgbYuvToRgb(YuvWhite());
498 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
499
500 Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
501 EXPECT_RGB_NEAR(rgb_r, RgbRed());
502
503 Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
504 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
505
506 Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
507 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
508 }
509
TEST_F(GainMapMathTest,SrgbRgbToYuv)510 TEST_F(GainMapMathTest, SrgbRgbToYuv) {
511 Color yuv_black = srgbRgbToYuv(RgbBlack());
512 EXPECT_YUV_NEAR(yuv_black, YuvBlack());
513
514 Color yuv_white = srgbRgbToYuv(RgbWhite());
515 EXPECT_YUV_NEAR(yuv_white, YuvWhite());
516
517 Color yuv_r = srgbRgbToYuv(RgbRed());
518 EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
519
520 Color yuv_g = srgbRgbToYuv(RgbGreen());
521 EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
522
523 Color yuv_b = srgbRgbToYuv(RgbBlue());
524 EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
525 }
526
TEST_F(GainMapMathTest,SrgbRgbYuvRoundtrip)527 TEST_F(GainMapMathTest, SrgbRgbYuvRoundtrip) {
528 Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
529 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
530
531 Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
532 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
533
534 Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
535 EXPECT_RGB_NEAR(rgb_r, RgbRed());
536
537 Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
538 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
539
540 Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
541 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
542 }
543
TEST_F(GainMapMathTest,SrgbTransferFunction)544 TEST_F(GainMapMathTest, SrgbTransferFunction) {
545 EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
546 EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
547 EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
548 EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
549 EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
550 }
551
TEST_F(GainMapMathTest,P3Luminance)552 TEST_F(GainMapMathTest, P3Luminance) {
553 EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
554 EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
555 EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
556 EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
557 EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
558 }
559
TEST_F(GainMapMathTest,P3YuvToRgb)560 TEST_F(GainMapMathTest, P3YuvToRgb) {
561 Color rgb_black = p3YuvToRgb(YuvBlack());
562 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
563
564 Color rgb_white = p3YuvToRgb(YuvWhite());
565 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
566
567 Color rgb_r = p3YuvToRgb(P3YuvRed());
568 EXPECT_RGB_NEAR(rgb_r, RgbRed());
569
570 Color rgb_g = p3YuvToRgb(P3YuvGreen());
571 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
572
573 Color rgb_b = p3YuvToRgb(P3YuvBlue());
574 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
575 }
576
TEST_F(GainMapMathTest,P3RgbToYuv)577 TEST_F(GainMapMathTest, P3RgbToYuv) {
578 Color yuv_black = p3RgbToYuv(RgbBlack());
579 EXPECT_YUV_NEAR(yuv_black, YuvBlack());
580
581 Color yuv_white = p3RgbToYuv(RgbWhite());
582 EXPECT_YUV_NEAR(yuv_white, YuvWhite());
583
584 Color yuv_r = p3RgbToYuv(RgbRed());
585 EXPECT_YUV_NEAR(yuv_r, P3YuvRed());
586
587 Color yuv_g = p3RgbToYuv(RgbGreen());
588 EXPECT_YUV_NEAR(yuv_g, P3YuvGreen());
589
590 Color yuv_b = p3RgbToYuv(RgbBlue());
591 EXPECT_YUV_NEAR(yuv_b, P3YuvBlue());
592 }
593
TEST_F(GainMapMathTest,P3RgbYuvRoundtrip)594 TEST_F(GainMapMathTest, P3RgbYuvRoundtrip) {
595 Color rgb_black = p3YuvToRgb(p3RgbToYuv(RgbBlack()));
596 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
597
598 Color rgb_white = p3YuvToRgb(p3RgbToYuv(RgbWhite()));
599 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
600
601 Color rgb_r = p3YuvToRgb(p3RgbToYuv(RgbRed()));
602 EXPECT_RGB_NEAR(rgb_r, RgbRed());
603
604 Color rgb_g = p3YuvToRgb(p3RgbToYuv(RgbGreen()));
605 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
606
607 Color rgb_b = p3YuvToRgb(p3RgbToYuv(RgbBlue()));
608 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
609 }
TEST_F(GainMapMathTest,Bt2100Luminance)610 TEST_F(GainMapMathTest, Bt2100Luminance) {
611 EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
612 EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
613 EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
614 EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
615 EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
616 }
617
TEST_F(GainMapMathTest,Bt2100YuvToRgb)618 TEST_F(GainMapMathTest, Bt2100YuvToRgb) {
619 Color rgb_black = bt2100YuvToRgb(YuvBlack());
620 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
621
622 Color rgb_white = bt2100YuvToRgb(YuvWhite());
623 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
624
625 Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
626 EXPECT_RGB_NEAR(rgb_r, RgbRed());
627
628 Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
629 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
630
631 Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
632 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
633 }
634
TEST_F(GainMapMathTest,Bt2100RgbToYuv)635 TEST_F(GainMapMathTest, Bt2100RgbToYuv) {
636 Color yuv_black = bt2100RgbToYuv(RgbBlack());
637 EXPECT_YUV_NEAR(yuv_black, YuvBlack());
638
639 Color yuv_white = bt2100RgbToYuv(RgbWhite());
640 EXPECT_YUV_NEAR(yuv_white, YuvWhite());
641
642 Color yuv_r = bt2100RgbToYuv(RgbRed());
643 EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
644
645 Color yuv_g = bt2100RgbToYuv(RgbGreen());
646 EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
647
648 Color yuv_b = bt2100RgbToYuv(RgbBlue());
649 EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
650 }
651
TEST_F(GainMapMathTest,Bt2100RgbYuvRoundtrip)652 TEST_F(GainMapMathTest, Bt2100RgbYuvRoundtrip) {
653 Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
654 EXPECT_RGB_NEAR(rgb_black, RgbBlack());
655
656 Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
657 EXPECT_RGB_NEAR(rgb_white, RgbWhite());
658
659 Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
660 EXPECT_RGB_NEAR(rgb_r, RgbRed());
661
662 Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
663 EXPECT_RGB_NEAR(rgb_g, RgbGreen());
664
665 Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
666 EXPECT_RGB_NEAR(rgb_b, RgbBlue());
667 }
668
TEST_F(GainMapMathTest,YuvColorGamutConversion)669 TEST_F(GainMapMathTest, YuvColorGamutConversion) {
670 const std::array<Color, 5> SrgbYuvColors{YuvBlack(), YuvWhite(), SrgbYuvRed(), SrgbYuvGreen(),
671 SrgbYuvBlue()};
672
673 const std::array<Color, 5> P3YuvColors{YuvBlack(), YuvWhite(), P3YuvRed(), P3YuvGreen(),
674 P3YuvBlue()};
675
676 const std::array<Color, 5> Bt2100YuvColors{YuvBlack(), YuvWhite(), Bt2100YuvRed(),
677 Bt2100YuvGreen(), Bt2100YuvBlue()};
678 /*
679 * Each tuple contains three elements.
680 * 0. An array containing 9 coefficients needed to perform the color gamut conversion
681 * 1. Array of colors to be used as test input
682 * 2. Array of colors to used as reference output
683 */
684 const std::array<std::tuple<const std::array<float, 9>&, const std::array<Color, 5>,
685 const std::array<Color, 5>>,
686 6>
687 coeffs_setup_expected{{
688 {kYuvBt709ToBt601, SrgbYuvColors, P3YuvColors},
689 {kYuvBt709ToBt2100, SrgbYuvColors, Bt2100YuvColors},
690 {kYuvBt601ToBt709, P3YuvColors, SrgbYuvColors},
691 {kYuvBt601ToBt2100, P3YuvColors, Bt2100YuvColors},
692 {kYuvBt2100ToBt709, Bt2100YuvColors, SrgbYuvColors},
693 {kYuvBt2100ToBt601, Bt2100YuvColors, P3YuvColors},
694 }};
695
696 for (const auto& [coeffs, input, expected] : coeffs_setup_expected) {
697 for (size_t color_idx = 0; color_idx < SrgbYuvColors.size(); ++color_idx) {
698 const Color input_color = input.at(color_idx);
699 const Color output_color = yuvColorGamutConversion(input_color, coeffs);
700
701 EXPECT_YUV_NEAR(expected.at(color_idx), output_color);
702 }
703 }
704 }
705
706 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
TEST_F(GainMapMathTest,YuvConversionNeon)707 TEST_F(GainMapMathTest, YuvConversionNeon) {
708 const std::array<Pixel, 5> SrgbYuvColors{YuvBlackPixel(), YuvWhitePixel(), SrgbYuvRedPixel(),
709 SrgbYuvGreenPixel(), SrgbYuvBluePixel()};
710
711 const std::array<Pixel, 5> P3YuvColors{YuvBlackPixel(), YuvWhitePixel(), P3YuvRedPixel(),
712 P3YuvGreenPixel(), P3YuvBluePixel()};
713
714 const std::array<Pixel, 5> Bt2100YuvColors{YuvBlackPixel(), YuvWhitePixel(), Bt2100YuvRedPixel(),
715 Bt2100YuvGreenPixel(), Bt2100YuvBluePixel()};
716
717 struct InputSamples {
718 std::array<uint8_t, 8> y;
719 std::array<int16_t, 8> u;
720 std::array<int16_t, 8> v;
721 };
722
723 struct ExpectedSamples {
724 std::array<int16_t, 8> y;
725 std::array<int16_t, 8> u;
726 std::array<int16_t, 8> v;
727 };
728
729 // Each tuple contains three elements.
730 // 0. A pointer to the coefficients that will be passed to the Neon implementation
731 // 1. Input pixel/color array
732 // 2. The expected results
733 const std::array<
734 std::tuple<const int16_t*, const std::array<Pixel, 5>, const std::array<Pixel, 5>>, 6>
735 coeffs_setup_correct{{
736 {kYuv709To601_coeffs_neon, SrgbYuvColors, P3YuvColors},
737 {kYuv709To2100_coeffs_neon, SrgbYuvColors, Bt2100YuvColors},
738 {kYuv601To709_coeffs_neon, P3YuvColors, SrgbYuvColors},
739 {kYuv601To2100_coeffs_neon, P3YuvColors, Bt2100YuvColors},
740 {kYuv2100To709_coeffs_neon, Bt2100YuvColors, SrgbYuvColors},
741 {kYuv2100To601_coeffs_neon, Bt2100YuvColors, P3YuvColors},
742 }};
743
744 for (const auto& [coeff_ptr, input, expected] : coeffs_setup_correct) {
745 const int16x8_t coeffs = vld1q_s16(coeff_ptr);
746 InputSamples input_values;
747 ExpectedSamples expected_values;
748 for (size_t sample_idx = 0; sample_idx < 8; ++sample_idx) {
749 size_t ring_idx = sample_idx % input.size();
750 input_values.y.at(sample_idx) = static_cast<uint8_t>(input.at(ring_idx).y);
751 input_values.u.at(sample_idx) = input.at(ring_idx).u;
752 input_values.v.at(sample_idx) = input.at(ring_idx).v;
753
754 expected_values.y.at(sample_idx) = expected.at(ring_idx).y;
755 expected_values.u.at(sample_idx) = expected.at(ring_idx).u;
756 expected_values.v.at(sample_idx) = expected.at(ring_idx).v;
757 }
758
759 const uint8x8_t y_neon = vld1_u8(input_values.y.data());
760 const int16x8_t u_neon = vld1q_s16(input_values.u.data());
761 const int16x8_t v_neon = vld1q_s16(input_values.v.data());
762
763 const int16x8x3_t neon_result = yuvConversion_neon(y_neon, u_neon, v_neon, coeffs);
764
765 const int16x8_t y_neon_result = neon_result.val[0];
766 const int16x8_t u_neon_result = neon_result.val[1];
767 const int16x8_t v_neon_result = neon_result.val[2];
768
769 const Pixel result0 = {vgetq_lane_s16(y_neon_result, 0), vgetq_lane_s16(u_neon_result, 0),
770 vgetq_lane_s16(v_neon_result, 0)};
771
772 const Pixel result1 = {vgetq_lane_s16(y_neon_result, 1), vgetq_lane_s16(u_neon_result, 1),
773 vgetq_lane_s16(v_neon_result, 1)};
774
775 const Pixel result2 = {vgetq_lane_s16(y_neon_result, 2), vgetq_lane_s16(u_neon_result, 2),
776 vgetq_lane_s16(v_neon_result, 2)};
777
778 const Pixel result3 = {vgetq_lane_s16(y_neon_result, 3), vgetq_lane_s16(u_neon_result, 3),
779 vgetq_lane_s16(v_neon_result, 3)};
780
781 const Pixel result4 = {vgetq_lane_s16(y_neon_result, 4), vgetq_lane_s16(u_neon_result, 4),
782 vgetq_lane_s16(v_neon_result, 4)};
783
784 const Pixel result5 = {vgetq_lane_s16(y_neon_result, 5), vgetq_lane_s16(u_neon_result, 5),
785 vgetq_lane_s16(v_neon_result, 5)};
786
787 const Pixel result6 = {vgetq_lane_s16(y_neon_result, 6), vgetq_lane_s16(u_neon_result, 6),
788 vgetq_lane_s16(v_neon_result, 6)};
789
790 const Pixel result7 = {vgetq_lane_s16(y_neon_result, 7), vgetq_lane_s16(u_neon_result, 7),
791 vgetq_lane_s16(v_neon_result, 7)};
792
793 EXPECT_NEAR(result0.y, expected_values.y.at(0), 1);
794 EXPECT_NEAR(result0.u, expected_values.u.at(0), 1);
795 EXPECT_NEAR(result0.v, expected_values.v.at(0), 1);
796
797 EXPECT_NEAR(result1.y, expected_values.y.at(1), 1);
798 EXPECT_NEAR(result1.u, expected_values.u.at(1), 1);
799 EXPECT_NEAR(result1.v, expected_values.v.at(1), 1);
800
801 EXPECT_NEAR(result2.y, expected_values.y.at(2), 1);
802 EXPECT_NEAR(result2.u, expected_values.u.at(2), 1);
803 EXPECT_NEAR(result2.v, expected_values.v.at(2), 1);
804
805 EXPECT_NEAR(result3.y, expected_values.y.at(3), 1);
806 EXPECT_NEAR(result3.u, expected_values.u.at(3), 1);
807 EXPECT_NEAR(result3.v, expected_values.v.at(3), 1);
808
809 EXPECT_NEAR(result4.y, expected_values.y.at(4), 1);
810 EXPECT_NEAR(result4.u, expected_values.u.at(4), 1);
811 EXPECT_NEAR(result4.v, expected_values.v.at(4), 1);
812
813 EXPECT_NEAR(result5.y, expected_values.y.at(5), 1);
814 EXPECT_NEAR(result5.u, expected_values.u.at(5), 1);
815 EXPECT_NEAR(result5.v, expected_values.v.at(5), 1);
816
817 EXPECT_NEAR(result6.y, expected_values.y.at(6), 1);
818 EXPECT_NEAR(result6.u, expected_values.u.at(6), 1);
819 EXPECT_NEAR(result6.v, expected_values.v.at(6), 1);
820
821 EXPECT_NEAR(result7.y, expected_values.y.at(7), 1);
822 EXPECT_NEAR(result7.u, expected_values.u.at(7), 1);
823 EXPECT_NEAR(result7.v, expected_values.v.at(7), 1);
824 }
825 }
826 #endif
827
TEST_F(GainMapMathTest,TransformYuv420)828 TEST_F(GainMapMathTest, TransformYuv420) {
829 jpegr_uncompressed_struct input = Yuv420Image();
830 const size_t buf_size = input.width * input.height * 3 / 2;
831 std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(buf_size);
832
833 const std::array<std::array<float, 9>, 6> conversion_coeffs = {
834 kYuvBt709ToBt601, kYuvBt709ToBt2100, kYuvBt601ToBt709,
835 kYuvBt601ToBt2100, kYuvBt2100ToBt709, kYuvBt2100ToBt601};
836
837 for (size_t coeffs_idx = 0; coeffs_idx < conversion_coeffs.size(); ++coeffs_idx) {
838 jpegr_uncompressed_struct output = Yuv420Image();
839 memcpy(out_buf.get(), input.data, buf_size);
840 output.data = out_buf.get();
841 output.chroma_data = out_buf.get() + input.width * input.height;
842 output.luma_stride = input.width;
843 output.chroma_stride = input.width / 2;
844
845 // Perform a color gamut conversion to the entire 4:2:0 image.
846 transformYuv420(&output, conversion_coeffs.at(coeffs_idx));
847
848 for (size_t y = 0; y < input.height; y += 2) {
849 for (size_t x = 0; x < input.width; x += 2) {
850 Pixel out1 = getYuv420Pixel_uint(&output, x, y);
851 Pixel out2 = getYuv420Pixel_uint(&output, x + 1, y);
852 Pixel out3 = getYuv420Pixel_uint(&output, x, y + 1);
853 Pixel out4 = getYuv420Pixel_uint(&output, x + 1, y + 1);
854
855 Color in1 = getYuv420Pixel(&input, x, y);
856 Color in2 = getYuv420Pixel(&input, x + 1, y);
857 Color in3 = getYuv420Pixel(&input, x, y + 1);
858 Color in4 = getYuv420Pixel(&input, x + 1, y + 1);
859
860 in1 = yuvColorGamutConversion(in1, conversion_coeffs.at(coeffs_idx));
861 in2 = yuvColorGamutConversion(in2, conversion_coeffs.at(coeffs_idx));
862 in3 = yuvColorGamutConversion(in3, conversion_coeffs.at(coeffs_idx));
863 in4 = yuvColorGamutConversion(in4, conversion_coeffs.at(coeffs_idx));
864
865 // Clamp and reduce to uint8_t from float.
866 uint8_t expect_y1 = static_cast<uint8_t>(CLIP3((in1.y * 255.0f + 0.5f), 0, 255));
867 uint8_t expect_y2 = static_cast<uint8_t>(CLIP3((in2.y * 255.0f + 0.5f), 0, 255));
868 uint8_t expect_y3 = static_cast<uint8_t>(CLIP3((in3.y * 255.0f + 0.5f), 0, 255));
869 uint8_t expect_y4 = static_cast<uint8_t>(CLIP3((in4.y * 255.0f + 0.5f), 0, 255));
870
871 // Allow an absolute difference of 1 to allow for implmentations using a fixed-point
872 // approximation.
873 EXPECT_NEAR(expect_y1, out1.y, 1);
874 EXPECT_NEAR(expect_y2, out2.y, 1);
875 EXPECT_NEAR(expect_y3, out3.y, 1);
876 EXPECT_NEAR(expect_y4, out4.y, 1);
877
878 Color expect_uv = (in1 + in2 + in3 + in4) / 4.0f;
879
880 uint8_t expect_u =
881 static_cast<uint8_t>(CLIP3((expect_uv.u * 255.0f + 128.0f + 0.5f), 0, 255));
882 uint8_t expect_v =
883 static_cast<uint8_t>(CLIP3((expect_uv.v * 255.0f + 128.0f + 0.5f), 0, 255));
884
885 EXPECT_NEAR(expect_u, out1.u, 1);
886 EXPECT_NEAR(expect_u, out2.u, 1);
887 EXPECT_NEAR(expect_u, out3.u, 1);
888 EXPECT_NEAR(expect_u, out4.u, 1);
889
890 EXPECT_NEAR(expect_v, out1.v, 1);
891 EXPECT_NEAR(expect_v, out2.v, 1);
892 EXPECT_NEAR(expect_v, out3.v, 1);
893 EXPECT_NEAR(expect_v, out4.v, 1);
894 }
895 }
896 }
897 }
898
899 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
TEST_F(GainMapMathTest,TransformYuv420Neon)900 TEST_F(GainMapMathTest, TransformYuv420Neon) {
901 const std::array<std::pair<const int16_t*, const std::array<float, 9>>, 6> fixed_floating_coeffs{
902 {{kYuv709To601_coeffs_neon, kYuvBt709ToBt601},
903 {kYuv709To2100_coeffs_neon, kYuvBt709ToBt2100},
904 {kYuv601To709_coeffs_neon, kYuvBt601ToBt709},
905 {kYuv601To2100_coeffs_neon, kYuvBt601ToBt2100},
906 {kYuv2100To709_coeffs_neon, kYuvBt2100ToBt709},
907 {kYuv2100To601_coeffs_neon, kYuvBt2100ToBt601}}};
908
909 for (const auto& [neon_coeffs_ptr, floating_point_coeffs] : fixed_floating_coeffs) {
910 jpegr_uncompressed_struct input = Yuv420Image32x4();
911 const size_t buf_size = input.width * input.height * 3 / 2;
912
913 std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(buf_size);
914 memcpy(out_buf.get(), input.data, buf_size);
915 jpegr_uncompressed_struct output = Yuv420Image32x4();
916 output.data = out_buf.get();
917 output.chroma_data = out_buf.get() + input.width * input.height;
918 output.luma_stride = input.width;
919 output.chroma_stride = input.width / 2;
920
921 transformYuv420_neon(&output, neon_coeffs_ptr);
922
923 for (size_t y = 0; y < input.height / 2; ++y) {
924 for (size_t x = 0; x < input.width / 2; ++x) {
925 const Pixel out1 = getYuv420Pixel_uint(&output, x * 2, y * 2);
926 const Pixel out2 = getYuv420Pixel_uint(&output, x * 2 + 1, y * 2);
927 const Pixel out3 = getYuv420Pixel_uint(&output, x * 2, y * 2 + 1);
928 const Pixel out4 = getYuv420Pixel_uint(&output, x * 2 + 1, y * 2 + 1);
929
930 Color in1 = getYuv420Pixel(&input, x * 2, y * 2);
931 Color in2 = getYuv420Pixel(&input, x * 2 + 1, y * 2);
932 Color in3 = getYuv420Pixel(&input, x * 2, y * 2 + 1);
933 Color in4 = getYuv420Pixel(&input, x * 2 + 1, y * 2 + 1);
934
935 in1 = yuvColorGamutConversion(in1, floating_point_coeffs);
936 in2 = yuvColorGamutConversion(in2, floating_point_coeffs);
937 in3 = yuvColorGamutConversion(in3, floating_point_coeffs);
938 in4 = yuvColorGamutConversion(in4, floating_point_coeffs);
939
940 const Color expect_uv = (in1 + in2 + in3 + in4) / 4.0f;
941
942 const uint8_t expect_y1 = static_cast<uint8_t>(CLIP3(in1.y * 255.0f + 0.5f, 0, 255));
943 const uint8_t expect_y2 = static_cast<uint8_t>(CLIP3(in2.y * 255.0f + 0.5f, 0, 255));
944 const uint8_t expect_y3 = static_cast<uint8_t>(CLIP3(in3.y * 255.0f + 0.5f, 0, 255));
945 const uint8_t expect_y4 = static_cast<uint8_t>(CLIP3(in4.y * 255.0f + 0.5f, 0, 255));
946
947 const uint8_t expect_u =
948 static_cast<uint8_t>(CLIP3(expect_uv.u * 255.0f + 128.0f + 0.5f, 0, 255));
949 const uint8_t expect_v =
950 static_cast<uint8_t>(CLIP3(expect_uv.v * 255.0f + 128.0f + 0.5f, 0, 255));
951
952 // Due to the Neon version using a fixed-point approximation, this can result in an off by
953 // one error compared with the standard floating-point version.
954 EXPECT_NEAR(expect_y1, out1.y, 1);
955 EXPECT_NEAR(expect_y2, out2.y, 1);
956 EXPECT_NEAR(expect_y3, out3.y, 1);
957 EXPECT_NEAR(expect_y4, out4.y, 1);
958
959 EXPECT_NEAR(expect_u, out1.u, 1);
960 EXPECT_NEAR(expect_u, out2.u, 1);
961 EXPECT_NEAR(expect_u, out3.u, 1);
962 EXPECT_NEAR(expect_u, out4.u, 1);
963
964 EXPECT_NEAR(expect_v, out1.v, 1);
965 EXPECT_NEAR(expect_v, out2.v, 1);
966 EXPECT_NEAR(expect_v, out3.v, 1);
967 EXPECT_NEAR(expect_v, out4.v, 1);
968 }
969 }
970 }
971 }
972 #endif
973
TEST_F(GainMapMathTest,HlgOetf)974 TEST_F(GainMapMathTest, HlgOetf) {
975 EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
976 EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
977 EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
978 EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
979 EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
980
981 Color e = {{{0.04167f, 0.08333f, 0.5f}}};
982 Color e_gamma = {{{0.35357f, 0.5f, 0.87164f}}};
983 EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
984 }
985
TEST_F(GainMapMathTest,HlgInvOetf)986 TEST_F(GainMapMathTest, HlgInvOetf) {
987 EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
988 EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
989 EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
990 EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
991 EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
992
993 Color e_gamma = {{{0.25f, 0.5f, 0.75f}}};
994 Color e = {{{0.02083f, 0.08333f, 0.26496f}}};
995 EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
996 }
997
TEST_F(GainMapMathTest,HlgTransferFunctionRoundtrip)998 TEST_F(GainMapMathTest, HlgTransferFunctionRoundtrip) {
999 EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
1000 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
1001 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
1002 EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
1003 EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
1004 }
1005
TEST_F(GainMapMathTest,PqOetf)1006 TEST_F(GainMapMathTest, PqOetf) {
1007 EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
1008 EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
1009 EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
1010 EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
1011 EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
1012
1013 Color e = {{{0.01f, 0.5f, 0.99f}}};
1014 Color e_gamma = {{{0.50808f, 0.92655f, 0.99895f}}};
1015 EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
1016 }
1017
TEST_F(GainMapMathTest,PqInvOetf)1018 TEST_F(GainMapMathTest, PqInvOetf) {
1019 EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
1020 EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
1021 EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
1022 EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
1023 EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
1024
1025 Color e_gamma = {{{0.01f, 0.5f, 0.99f}}};
1026 Color e = {{{2.31017e-7f, 0.00922f, 0.90903f}}};
1027 EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
1028 }
1029
TEST_F(GainMapMathTest,PqInvOetfLUT)1030 TEST_F(GainMapMathTest, PqInvOetfLUT) {
1031 for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) {
1032 float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
1033 EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
1034 }
1035 }
1036
TEST_F(GainMapMathTest,HlgInvOetfLUT)1037 TEST_F(GainMapMathTest, HlgInvOetfLUT) {
1038 for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
1039 float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
1040 EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
1041 }
1042 }
1043
TEST_F(GainMapMathTest,pqOetfLUT)1044 TEST_F(GainMapMathTest, pqOetfLUT) {
1045 for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) {
1046 float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
1047 EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
1048 }
1049 }
1050
TEST_F(GainMapMathTest,hlgOetfLUT)1051 TEST_F(GainMapMathTest, hlgOetfLUT) {
1052 for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) {
1053 float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
1054 EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
1055 }
1056 }
1057
TEST_F(GainMapMathTest,srgbInvOetfLUT)1058 TEST_F(GainMapMathTest, srgbInvOetfLUT) {
1059 for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
1060 float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
1061 EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
1062 }
1063 }
1064
TEST_F(GainMapMathTest,applyGainLUT)1065 TEST_F(GainMapMathTest, applyGainLUT) {
1066 for (int boost = 1; boost <= 10; boost++) {
1067 ultrahdr_metadata_struct metadata;
1068
1069 metadata.minContentBoost = 1.0f / static_cast<float>(boost);
1070 metadata.maxContentBoost = static_cast<float>(boost);
1071 GainLUT gainLUT(&metadata);
1072 GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
1073 for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1074 float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1075 EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1076 applyGainLUT(RgbBlack(), value, gainLUT));
1077 EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1078 applyGainLUT(RgbWhite(), value, gainLUT));
1079 EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1080 applyGainLUT(RgbRed(), value, gainLUT));
1081 EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1082 applyGainLUT(RgbGreen(), value, gainLUT));
1083 EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1084 applyGainLUT(RgbBlue(), value, gainLUT));
1085 EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
1086 applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
1087 EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
1088 applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
1089 EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
1090 applyGainLUT(RgbRed(), value, gainLUTWithBoost));
1091 EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
1092 applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
1093 EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
1094 applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
1095 }
1096 }
1097
1098 for (int boost = 1; boost <= 10; boost++) {
1099 ultrahdr_metadata_struct metadata;
1100
1101 metadata.minContentBoost = 1.0f;
1102 metadata.maxContentBoost = static_cast<float>(boost);
1103 GainLUT gainLUT(&metadata);
1104 GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
1105 for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1106 float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1107 EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1108 applyGainLUT(RgbBlack(), value, gainLUT));
1109 EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1110 applyGainLUT(RgbWhite(), value, gainLUT));
1111 EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1112 applyGainLUT(RgbRed(), value, gainLUT));
1113 EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1114 applyGainLUT(RgbGreen(), value, gainLUT));
1115 EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1116 applyGainLUT(RgbBlue(), value, gainLUT));
1117 EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
1118 applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
1119 EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
1120 applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
1121 EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
1122 applyGainLUT(RgbRed(), value, gainLUTWithBoost));
1123 EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
1124 applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
1125 EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
1126 applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
1127 }
1128 }
1129
1130 for (int boost = 1; boost <= 10; boost++) {
1131 ultrahdr_metadata_struct metadata;
1132
1133 metadata.minContentBoost = 1.0f / powf(static_cast<float>(boost), 1.0f / 3.0f);
1134 metadata.maxContentBoost = static_cast<float>(boost);
1135 GainLUT gainLUT(&metadata);
1136 GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
1137 for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1138 float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1139 EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1140 applyGainLUT(RgbBlack(), value, gainLUT));
1141 EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1142 applyGainLUT(RgbWhite(), value, gainLUT));
1143 EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1144 applyGainLUT(RgbRed(), value, gainLUT));
1145 EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1146 applyGainLUT(RgbGreen(), value, gainLUT));
1147 EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1148 applyGainLUT(RgbBlue(), value, gainLUT));
1149 EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
1150 applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
1151 EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
1152 applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
1153 EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
1154 applyGainLUT(RgbRed(), value, gainLUTWithBoost));
1155 EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
1156 applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
1157 EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
1158 applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
1159 }
1160 }
1161 }
1162
TEST_F(GainMapMathTest,PqTransferFunctionRoundtrip)1163 TEST_F(GainMapMathTest, PqTransferFunctionRoundtrip) {
1164 EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
1165 EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
1166 EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
1167 EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
1168 EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
1169 }
1170
TEST_F(GainMapMathTest,ColorConversionLookup)1171 TEST_F(GainMapMathTest, ColorConversionLookup) {
1172 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
1173 nullptr);
1174 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT709),
1175 identityConversion);
1176 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3), p3ToBt709);
1177 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT2100),
1178 bt2100ToBt709);
1179
1180 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_UNSPECIFIED), nullptr);
1181 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT709), bt709ToP3);
1182 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_P3), identityConversion);
1183 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100), bt2100ToP3);
1184
1185 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
1186 nullptr);
1187 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT709),
1188 bt709ToBt2100);
1189 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_P3), p3ToBt2100);
1190 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT2100),
1191 identityConversion);
1192
1193 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
1194 nullptr);
1195 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT709),
1196 nullptr);
1197 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_P3), nullptr);
1198 EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT2100),
1199 nullptr);
1200 }
1201
TEST_F(GainMapMathTest,EncodeGain)1202 TEST_F(GainMapMathTest, EncodeGain) {
1203 ultrahdr_metadata_struct metadata;
1204
1205 metadata.minContentBoost = 1.0f / 4.0f;
1206 metadata.maxContentBoost = 4.0f;
1207
1208 EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127);
1209 EXPECT_EQ(encodeGain(0.0f, 1.0f, &metadata), 127);
1210 EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
1211 EXPECT_EQ(encodeGain(0.5f, 0.0f, &metadata), 0);
1212
1213 EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 127);
1214 EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 255);
1215 EXPECT_EQ(encodeGain(1.0f, 5.0f, &metadata), 255);
1216 EXPECT_EQ(encodeGain(4.0f, 1.0f, &metadata), 0);
1217 EXPECT_EQ(encodeGain(4.0f, 0.5f, &metadata), 0);
1218 EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 191);
1219 EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 63);
1220
1221 metadata.maxContentBoost = 2.0f;
1222 metadata.minContentBoost = 1.0f / 2.0f;
1223
1224 EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 255);
1225 EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 0);
1226 EXPECT_EQ(encodeGain(1.0f, 1.41421f, &metadata), 191);
1227 EXPECT_EQ(encodeGain(1.41421f, 1.0f, &metadata), 63);
1228
1229 metadata.maxContentBoost = 8.0f;
1230 metadata.minContentBoost = 1.0f / 8.0f;
1231
1232 EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
1233 EXPECT_EQ(encodeGain(8.0f, 1.0f, &metadata), 0);
1234 EXPECT_EQ(encodeGain(1.0f, 2.82843f, &metadata), 191);
1235 EXPECT_EQ(encodeGain(2.82843f, 1.0f, &metadata), 63);
1236
1237 metadata.maxContentBoost = 8.0f;
1238 metadata.minContentBoost = 1.0f;
1239
1240 EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 0);
1241 EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
1242
1243 EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 0);
1244 EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
1245 EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 170);
1246 EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 85);
1247
1248 metadata.maxContentBoost = 8.0f;
1249 metadata.minContentBoost = 0.5f;
1250
1251 EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 63);
1252 EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
1253
1254 EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 63);
1255 EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
1256 EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 191);
1257 EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 127);
1258 EXPECT_EQ(encodeGain(1.0f, 0.7071f, &metadata), 31);
1259 EXPECT_EQ(encodeGain(1.0f, 0.5f, &metadata), 0);
1260 }
1261
TEST_F(GainMapMathTest,ApplyGain)1262 TEST_F(GainMapMathTest, ApplyGain) {
1263 ultrahdr_metadata_struct metadata;
1264
1265 metadata.minContentBoost = 1.0f / 4.0f;
1266 metadata.maxContentBoost = 4.0f;
1267 float displayBoost = metadata.maxContentBoost;
1268
1269 EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack());
1270 EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.5f, &metadata), RgbBlack());
1271 EXPECT_RGB_NEAR(applyGain(RgbBlack(), 1.0f, &metadata), RgbBlack());
1272
1273 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f);
1274 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f);
1275 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1276 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f);
1277 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f);
1278
1279 metadata.maxContentBoost = 2.0f;
1280 metadata.minContentBoost = 1.0f / 2.0f;
1281
1282 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1283 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f);
1284 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1285 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f);
1286 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f);
1287
1288 metadata.maxContentBoost = 8.0f;
1289 metadata.minContentBoost = 1.0f / 8.0f;
1290
1291 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
1292 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f);
1293 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1294 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f);
1295 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1296
1297 metadata.maxContentBoost = 8.0f;
1298 metadata.minContentBoost = 1.0f;
1299
1300 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite());
1301 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
1302 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
1303 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1304
1305 metadata.maxContentBoost = 8.0f;
1306 metadata.minContentBoost = 0.5f;
1307
1308 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1309 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite());
1310 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
1311 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
1312 EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1313
1314 Color e = {{{0.0f, 0.5f, 1.0f}}};
1315 metadata.maxContentBoost = 4.0f;
1316 metadata.minContentBoost = 1.0f / 4.0f;
1317
1318 EXPECT_RGB_NEAR(applyGain(e, 0.0f, &metadata), e / 4.0f);
1319 EXPECT_RGB_NEAR(applyGain(e, 0.25f, &metadata), e / 2.0f);
1320 EXPECT_RGB_NEAR(applyGain(e, 0.5f, &metadata), e);
1321 EXPECT_RGB_NEAR(applyGain(e, 0.75f, &metadata), e * 2.0f);
1322 EXPECT_RGB_NEAR(applyGain(e, 1.0f, &metadata), e * 4.0f);
1323
1324 EXPECT_RGB_EQ(applyGain(RgbBlack(), 1.0f, &metadata),
1325 applyGain(RgbBlack(), 1.0f, &metadata, displayBoost));
1326 EXPECT_RGB_EQ(applyGain(RgbWhite(), 1.0f, &metadata),
1327 applyGain(RgbWhite(), 1.0f, &metadata, displayBoost));
1328 EXPECT_RGB_EQ(applyGain(RgbRed(), 1.0f, &metadata),
1329 applyGain(RgbRed(), 1.0f, &metadata, displayBoost));
1330 EXPECT_RGB_EQ(applyGain(RgbGreen(), 1.0f, &metadata),
1331 applyGain(RgbGreen(), 1.0f, &metadata, displayBoost));
1332 EXPECT_RGB_EQ(applyGain(RgbBlue(), 1.0f, &metadata),
1333 applyGain(RgbBlue(), 1.0f, &metadata, displayBoost));
1334 EXPECT_RGB_EQ(applyGain(e, 1.0f, &metadata), applyGain(e, 1.0f, &metadata, displayBoost));
1335 }
1336
TEST_F(GainMapMathTest,GetYuv420Pixel)1337 TEST_F(GainMapMathTest, GetYuv420Pixel) {
1338 jpegr_uncompressed_struct image = Yuv420Image();
1339 Color(*colors)[4] = Yuv420Colors();
1340
1341 for (size_t y = 0; y < 4; ++y) {
1342 for (size_t x = 0; x < 4; ++x) {
1343 EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
1344 }
1345 }
1346 }
1347
TEST_F(GainMapMathTest,GetP010Pixel)1348 TEST_F(GainMapMathTest, GetP010Pixel) {
1349 jpegr_uncompressed_struct image = P010Image();
1350 Color(*colors)[4] = P010Colors();
1351
1352 for (size_t y = 0; y < 4; ++y) {
1353 for (size_t x = 0; x < 4; ++x) {
1354 EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
1355 }
1356 }
1357 }
1358
TEST_F(GainMapMathTest,SampleYuv420)1359 TEST_F(GainMapMathTest, SampleYuv420) {
1360 jpegr_uncompressed_struct image = Yuv420Image();
1361 Color(*colors)[4] = Yuv420Colors();
1362
1363 static const size_t kMapScaleFactor = 2;
1364 for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
1365 for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
1366 Color min = {{{1.0f, 1.0f, 1.0f}}};
1367 Color max = {{{-1.0f, -1.0f, -1.0f}}};
1368
1369 for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
1370 for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
1371 Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
1372 min = ColorMin(min, e);
1373 max = ColorMax(max, e);
1374 }
1375 }
1376
1377 // Instead of reimplementing the sampling algorithm, confirm that the
1378 // sample output is within the range of the min and max of the nearest
1379 // points.
1380 EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
1381 }
1382 }
1383 }
1384
TEST_F(GainMapMathTest,SampleP010)1385 TEST_F(GainMapMathTest, SampleP010) {
1386 jpegr_uncompressed_struct image = P010Image();
1387 Color(*colors)[4] = P010Colors();
1388
1389 static const size_t kMapScaleFactor = 2;
1390 for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
1391 for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
1392 Color min = {{{1.0f, 1.0f, 1.0f}}};
1393 Color max = {{{-1.0f, -1.0f, -1.0f}}};
1394
1395 for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
1396 for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
1397 Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
1398 min = ColorMin(min, e);
1399 max = ColorMax(max, e);
1400 }
1401 }
1402
1403 // Instead of reimplementing the sampling algorithm, confirm that the
1404 // sample output is within the range of the min and max of the nearest
1405 // points.
1406 EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
1407 }
1408 }
1409 }
1410
TEST_F(GainMapMathTest,SampleMap)1411 TEST_F(GainMapMathTest, SampleMap) {
1412 jpegr_uncompressed_struct image = MapImage();
1413 float(*values)[4] = MapValues();
1414
1415 static const size_t kMapScaleFactor = 2;
1416 ShepardsIDW idwTable(kMapScaleFactor);
1417 for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
1418 for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
1419 size_t x_base = x / kMapScaleFactor;
1420 size_t y_base = y / kMapScaleFactor;
1421
1422 float min = 1.0f;
1423 float max = -1.0f;
1424
1425 min = fmin(min, values[y_base][x_base]);
1426 max = fmax(max, values[y_base][x_base]);
1427 if (y_base + 1 < 4) {
1428 min = fmin(min, values[y_base + 1][x_base]);
1429 max = fmax(max, values[y_base + 1][x_base]);
1430 }
1431 if (x_base + 1 < 4) {
1432 min = fmin(min, values[y_base][x_base + 1]);
1433 max = fmax(max, values[y_base][x_base + 1]);
1434 }
1435 if (y_base + 1 < 4 && x_base + 1 < 4) {
1436 min = fmin(min, values[y_base + 1][x_base + 1]);
1437 max = fmax(max, values[y_base + 1][x_base + 1]);
1438 }
1439
1440 // Instead of reimplementing the sampling algorithm, confirm that the
1441 // sample output is within the range of the min and max of the nearest
1442 // points.
1443 EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
1444 testing::AllOf(testing::Ge(min), testing::Le(max)));
1445 EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
1446 sampleMap(&image, kMapScaleFactor, x, y));
1447 }
1448 }
1449 }
1450
TEST_F(GainMapMathTest,ColorToRgba1010102)1451 TEST_F(GainMapMathTest, ColorToRgba1010102) {
1452 EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
1453 EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
1454 EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
1455 EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
1456 EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
1457
1458 Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
1459 EXPECT_EQ(colorToRgba1010102(e_gamma),
1460 0x3 << 30 | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff)) |
1461 static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10 |
1462 static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
1463 }
1464
TEST_F(GainMapMathTest,ColorToRgbaF16)1465 TEST_F(GainMapMathTest, ColorToRgbaF16) {
1466 EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t)0x3C00) << 48);
1467 EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00);
1468 EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t)0x3C00) << 48) | ((uint64_t)0x3C00));
1469 EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 16));
1470 EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 32));
1471
1472 Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
1473 EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66);
1474 }
1475
TEST_F(GainMapMathTest,Float32ToFloat16)1476 TEST_F(GainMapMathTest, Float32ToFloat16) {
1477 EXPECT_EQ(floatToHalf(0.1f), 0x2E66);
1478 EXPECT_EQ(floatToHalf(0.0f), 0x0);
1479 EXPECT_EQ(floatToHalf(1.0f), 0x3C00);
1480 EXPECT_EQ(floatToHalf(-1.0f), 0xBC00);
1481 EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max
1482 EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF); // float min
1483 EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero
1484 }
1485
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgb)1486 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) {
1487 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance), 0.0f);
1488 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance), kSdrWhiteNits);
1489 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
1490 srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
1491 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
1492 srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1493 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
1494 srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1495 }
1496
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgbP3)1497 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) {
1498 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance), 0.0f);
1499 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance), kSdrWhiteNits);
1500 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance), p3Luminance(RgbRed()) * kSdrWhiteNits,
1501 LuminanceEpsilon());
1502 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
1503 p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1504 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
1505 p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1506 }
1507
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgbBt2100)1508 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) {
1509 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance), 0.0f);
1510 EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance), kSdrWhiteNits);
1511 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
1512 bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
1513 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
1514 bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1515 EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
1516 bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1517 }
1518
TEST_F(GainMapMathTest,GenerateMapLuminanceHlg)1519 TEST_F(GainMapMathTest, GenerateMapLuminanceHlg) {
1520 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion, bt2100Luminance,
1521 kHlgMaxNits),
1522 0.0f);
1523 EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion, bt2100Luminance,
1524 kHlgMaxNits),
1525 kHlgMaxNits);
1526 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion, bt2100Luminance,
1527 kHlgMaxNits),
1528 bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
1529 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
1530 bt2100Luminance, kHlgMaxNits),
1531 bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
1532 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion, bt2100Luminance,
1533 kHlgMaxNits),
1534 bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
1535 }
1536
TEST_F(GainMapMathTest,GenerateMapLuminancePq)1537 TEST_F(GainMapMathTest, GenerateMapLuminancePq) {
1538 EXPECT_FLOAT_EQ(
1539 Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
1540 0.0f);
1541 EXPECT_FLOAT_EQ(
1542 Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
1543 kPqMaxNits);
1544 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion, bt2100Luminance,
1545 kPqMaxNits),
1546 bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
1547 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion, bt2100Luminance,
1548 kPqMaxNits),
1549 bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
1550 EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion, bt2100Luminance,
1551 kPqMaxNits),
1552 bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
1553 }
1554
TEST_F(GainMapMathTest,ApplyMap)1555 TEST_F(GainMapMathTest, ApplyMap) {
1556 ultrahdr_metadata_struct metadata;
1557
1558 metadata.minContentBoost = 1.0f / 8.0f;
1559 metadata.maxContentBoost = 8.0f;
1560
1561 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1562 EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata), RgbBlack());
1563 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata), RgbRed() * 8.0f);
1564 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata), RgbGreen() * 8.0f);
1565 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata), RgbBlue() * 8.0f);
1566
1567 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata), RgbWhite() * sqrt(8.0f));
1568 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata), RgbBlack());
1569 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata), RgbRed() * sqrt(8.0f));
1570 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata), RgbGreen() * sqrt(8.0f));
1571 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata), RgbBlue() * sqrt(8.0f));
1572
1573 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite());
1574 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata), RgbBlack());
1575 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata), RgbRed());
1576 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata), RgbGreen());
1577 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata), RgbBlue());
1578
1579 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite() / sqrt(8.0f));
1580 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata), RgbBlack());
1581 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata), RgbRed() / sqrt(8.0f));
1582 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata), RgbGreen() / sqrt(8.0f));
1583 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata), RgbBlue() / sqrt(8.0f));
1584
1585 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
1586 EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata), RgbBlack());
1587 EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata), RgbRed() / 8.0f);
1588 EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata), RgbGreen() / 8.0f);
1589 EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata), RgbBlue() / 8.0f);
1590
1591 metadata.maxContentBoost = 8.0f;
1592 metadata.minContentBoost = 1.0f;
1593
1594 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1595 EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
1596 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
1597 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite());
1598
1599 metadata.maxContentBoost = 8.0f;
1600 metadata.minContentBoost = 0.5f;
1601 ;
1602
1603 EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1604 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), RgbWhite() * 4.0f);
1605 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
1606 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite());
1607 EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1608 }
1609
1610 } // namespace ultrahdr
1611