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