• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
__anon76f66eec0102null239   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 
__anon76f66eec0202null314   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 
__anon76f66eec0302null366   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.212639f);
544   EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.715169f);
545   EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.072192f);
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.2289746f);
611   EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.6917385f);
612   EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.0792869f);
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.677998f);
670   EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.059302f);
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(kJpegrVersion);
1131 
1132     std::fill_n(metadata.min_content_boost, 3, 1.0f / boost);
1133     std::fill_n(metadata.max_content_boost, 3, boost);
1134     std::fill_n(metadata.gamma, 3, 1.0f);
1135     std::fill_n(metadata.offset_sdr, 3, 0.0f);
1136     std::fill_n(metadata.offset_hdr, 3, 0.0f);
1137     metadata.hdr_capacity_max = metadata.max_content_boost[0];
1138     metadata.hdr_capacity_min = metadata.min_content_boost[0];
1139     metadata.use_base_cg = true;
1140     GainLUT gainLUT(&metadata);
1141     float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) /
1142                    (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min));
1143     weight = CLIP3(weight, 0.0f, 1.0f);
1144     GainLUT gainLUTWithBoost(&metadata, weight);
1145     for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1146       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1147       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1148                       applyGainLUT(RgbBlack(), value, gainLUT, &metadata));
1149       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1150                       applyGainLUT(RgbWhite(), value, gainLUT, &metadata));
1151       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1152                       applyGainLUT(RgbRed(), value, gainLUT, &metadata));
1153       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1154                       applyGainLUT(RgbGreen(), value, gainLUT, &metadata));
1155       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1156                       applyGainLUT(RgbBlue(), value, gainLUT, &metadata));
1157       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight),
1158                       applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata));
1159       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight),
1160                       applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata));
1161       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight),
1162                       applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata));
1163       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight),
1164                       applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata));
1165       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight),
1166                       applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata));
1167     }
1168   }
1169 
1170   for (float boost = 1.5; boost <= 12; boost++) {
1171     uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
1172 
1173     std::fill_n(metadata.min_content_boost, 3, 1.0f / boost);
1174     std::fill_n(metadata.max_content_boost, 3, boost);
1175     std::fill_n(metadata.gamma, 3, 1.0f);
1176     std::fill_n(metadata.offset_sdr, 3, 0.0f);
1177     std::fill_n(metadata.offset_hdr, 3, 0.0f);
1178     metadata.hdr_capacity_max = metadata.max_content_boost[0];
1179     metadata.hdr_capacity_min = metadata.min_content_boost[0];
1180     metadata.use_base_cg = true;
1181     GainLUT gainLUT(&metadata);
1182     float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) /
1183                    (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min));
1184     weight = CLIP3(weight, 0.0f, 1.0f);
1185     GainLUT gainLUTWithBoost(&metadata, weight);
1186     for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1187       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1188       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1189                       applyGainLUT(RgbBlack(), value, gainLUT, &metadata));
1190       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1191                       applyGainLUT(RgbWhite(), value, gainLUT, &metadata));
1192       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1193                       applyGainLUT(RgbRed(), value, gainLUT, &metadata));
1194       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1195                       applyGainLUT(RgbGreen(), value, gainLUT, &metadata));
1196       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1197                       applyGainLUT(RgbBlue(), value, gainLUT, &metadata));
1198       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight),
1199                       applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata));
1200       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight),
1201                       applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata));
1202       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight),
1203                       applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata));
1204       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight),
1205                       applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata));
1206       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight),
1207                       applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata));
1208     }
1209   }
1210 
1211   for (float boost = 1.5; boost <= 12; boost++) {
1212     uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
1213 
1214     std::fill_n(metadata.min_content_boost, 3, 1.0f / powf(boost, 1.0f / 3.0f));
1215     std::fill_n(metadata.max_content_boost, 3, boost);
1216     std::fill_n(metadata.gamma, 3, 1.0f);
1217     std::fill_n(metadata.offset_sdr, 3, 0.0f);
1218     std::fill_n(metadata.offset_hdr, 3, 0.0f);
1219     metadata.hdr_capacity_max = metadata.max_content_boost[0];
1220     metadata.hdr_capacity_min = metadata.min_content_boost[0];
1221     metadata.use_base_cg = true;
1222     GainLUT gainLUT(&metadata);
1223     float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) /
1224                    (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min));
1225     weight = CLIP3(weight, 0.0f, 1.0f);
1226     GainLUT gainLUTWithBoost(&metadata, weight);
1227     for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
1228       float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
1229       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
1230                       applyGainLUT(RgbBlack(), value, gainLUT, &metadata));
1231       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
1232                       applyGainLUT(RgbWhite(), value, gainLUT, &metadata));
1233       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
1234                       applyGainLUT(RgbRed(), value, gainLUT, &metadata));
1235       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
1236                       applyGainLUT(RgbGreen(), value, gainLUT, &metadata));
1237       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
1238                       applyGainLUT(RgbBlue(), value, gainLUT, &metadata));
1239       EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight),
1240                       applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata));
1241       EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight),
1242                       applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata));
1243       EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight),
1244                       applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata));
1245       EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight),
1246                       applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata));
1247       EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight),
1248                       applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata));
1249     }
1250   }
1251 }
1252 
TEST_F(GainMapMathTest,PqTransferFunctionRoundtrip)1253 TEST_F(GainMapMathTest, PqTransferFunctionRoundtrip) {
1254   EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
1255   EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
1256   EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
1257   EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
1258   EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
1259 }
1260 
TEST_F(GainMapMathTest,ColorConversionLookup)1261 TEST_F(GainMapMathTest, ColorConversionLookup) {
1262   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_UNSPECIFIED), nullptr);
1263   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_BT_709), identityConversion);
1264   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_DISPLAY_P3), p3ToBt709);
1265   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_BT_2100), bt2100ToBt709);
1266 
1267   EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_UNSPECIFIED), nullptr);
1268   EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_BT_709), bt709ToP3);
1269   EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_DISPLAY_P3), identityConversion);
1270   EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_BT_2100), bt2100ToP3);
1271 
1272   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_UNSPECIFIED), nullptr);
1273   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_BT_709), bt709ToBt2100);
1274   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3), p3ToBt2100);
1275   EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_BT_2100), identityConversion);
1276 
1277   EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_UNSPECIFIED), nullptr);
1278   EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_BT_709), nullptr);
1279   EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_DISPLAY_P3), nullptr);
1280   EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_BT_2100), nullptr);
1281 }
1282 
TEST_F(GainMapMathTest,EncodeGain)1283 TEST_F(GainMapMathTest, EncodeGain) {
1284   float min_boost = log2(1.0f / 4.0f);
1285   float max_boost = log2(4.0f);
1286   float gamma = 1.0f;
1287 
1288   EXPECT_EQ(affineMapGain(computeGain(0.0f, 1.0f), min_boost, max_boost, 1.0f), 255);
1289   EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0);
1290   EXPECT_EQ(affineMapGain(computeGain(0.5f, 0.0f), min_boost, max_boost, 1.0f), 0);
1291   EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0), min_boost, max_boost, 1.0f), 128);
1292 
1293   EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 255);
1294   EXPECT_EQ(affineMapGain(computeGain(1.0f, 5.0f), min_boost, max_boost, 1.0f), 255);
1295   EXPECT_EQ(affineMapGain(computeGain(4.0f, 1.0f), min_boost, max_boost, 1.0f), 0);
1296   EXPECT_EQ(affineMapGain(computeGain(4.0f, 0.5f), min_boost, max_boost, 1.0f), 0);
1297   EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 191);
1298   EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 64);
1299 
1300   min_boost = log2(1.0f / 2.0f);
1301   max_boost = log2(2.0f);
1302 
1303   EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 255);
1304   EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 0);
1305   EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.41421f), min_boost, max_boost, 1.0f), 191);
1306   EXPECT_EQ(affineMapGain(computeGain(1.41421f, 1.0f), min_boost, max_boost, 1.0f), 64);
1307 
1308   min_boost = log2(1.0f / 8.0f);
1309   max_boost = log2(8.0f);
1310 
1311   EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255);
1312   EXPECT_EQ(affineMapGain(computeGain(8.0f, 1.0f), min_boost, max_boost, 1.0f), 0);
1313   EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.82843f), min_boost, max_boost, 1.0f), 191);
1314   EXPECT_EQ(affineMapGain(computeGain(2.82843f, 1.0f), min_boost, max_boost, 1.0f), 64);
1315 
1316   min_boost = log2(1.0f);
1317   max_boost = log2(8.0f);
1318 
1319   EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 0);
1320   EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0);
1321   EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 0);
1322   EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255);
1323   EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 170);
1324   EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 85);
1325 
1326   min_boost = log2(1.0f / 2.0f);
1327   max_boost = log2(8.0f);
1328 
1329   EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 64);
1330   EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0);
1331   EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 64);
1332   EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255);
1333   EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 191);
1334   EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 127);
1335   EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.7071f), min_boost, max_boost, 1.0f), 32);
1336   EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.5f), min_boost, max_boost, 1.0f), 0);
1337 }
1338 
TEST_F(GainMapMathTest,ApplyGain)1339 TEST_F(GainMapMathTest, ApplyGain) {
1340   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
1341 
1342   std::fill_n(metadata.min_content_boost, 3, 1.0f / 4.0f);
1343   std::fill_n(metadata.max_content_boost, 3, 4.0f);
1344   std::fill_n(metadata.offset_sdr, 3, 0.0f);
1345   std::fill_n(metadata.offset_hdr, 3, 0.0f);
1346   std::fill_n(metadata.gamma, 3, 1.0f);
1347   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1348   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1349   metadata.use_base_cg = true;
1350 
1351   EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack());
1352   EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.5f, &metadata), RgbBlack());
1353   EXPECT_RGB_NEAR(applyGain(RgbBlack(), 1.0f, &metadata), RgbBlack());
1354 
1355   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f);
1356   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f);
1357   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1358   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f);
1359   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f);
1360 
1361   std::fill_n(metadata.max_content_boost, 3, 2.0f);
1362   std::fill_n(metadata.min_content_boost, 3, 1.0f / 2.0f);
1363   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1364   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1365 
1366   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1367   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f);
1368   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1369   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f);
1370   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f);
1371 
1372   std::fill_n(metadata.max_content_boost, 3, 8.0f);
1373   std::fill_n(metadata.min_content_boost, 3, 1.0f / 8.0f);
1374   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1375   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1376 
1377   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
1378   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f);
1379   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
1380   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f);
1381   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1382 
1383   std::fill_n(metadata.max_content_boost, 3, 8.0f);
1384   std::fill_n(metadata.min_content_boost, 3, 1.0f);
1385   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1386   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1387 
1388   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite());
1389   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
1390   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
1391   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1392 
1393   std::fill_n(metadata.max_content_boost, 3, 8.0f);
1394   std::fill_n(metadata.min_content_boost, 3, 0.5f);
1395   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1396   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1397 
1398   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1399   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite());
1400   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
1401   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
1402   EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1403 
1404   Color e = {{{0.0f, 0.5f, 1.0f}}};
1405   std::fill_n(metadata.max_content_boost, 3, 4.0f);
1406   std::fill_n(metadata.min_content_boost, 3, 1.0f / 4.0f);
1407   metadata.hdr_capacity_max = metadata.max_content_boost[0];
1408   metadata.hdr_capacity_min = metadata.min_content_boost[0];
1409 
1410   EXPECT_RGB_NEAR(applyGain(e, 0.0f, &metadata), e / 4.0f);
1411   EXPECT_RGB_NEAR(applyGain(e, 0.25f, &metadata), e / 2.0f);
1412   EXPECT_RGB_NEAR(applyGain(e, 0.5f, &metadata), e);
1413   EXPECT_RGB_NEAR(applyGain(e, 0.75f, &metadata), e * 2.0f);
1414   EXPECT_RGB_NEAR(applyGain(e, 1.0f, &metadata), e * 4.0f);
1415 }
1416 
TEST_F(GainMapMathTest,GetYuv420Pixel)1417 TEST_F(GainMapMathTest, GetYuv420Pixel) {
1418   auto image = Yuv420Image();
1419   Color(*colors)[4] = Yuv420Colors();
1420 
1421   for (size_t y = 0; y < 4; ++y) {
1422     for (size_t x = 0; x < 4; ++x) {
1423       EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
1424     }
1425   }
1426 }
1427 
TEST_F(GainMapMathTest,GetP010Pixel)1428 TEST_F(GainMapMathTest, GetP010Pixel) {
1429   auto image = P010Image();
1430   Color(*colors)[4] = P010Colors();
1431 
1432   for (size_t y = 0; y < 4; ++y) {
1433     for (size_t x = 0; x < 4; ++x) {
1434       EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
1435     }
1436   }
1437 }
1438 
TEST_F(GainMapMathTest,SampleYuv420)1439 TEST_F(GainMapMathTest, SampleYuv420) {
1440   auto image = Yuv420Image();
1441   Color(*colors)[4] = Yuv420Colors();
1442 
1443   static const size_t kMapScaleFactor = 2;
1444   for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
1445     for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
1446       Color min = {{{1.0f, 1.0f, 1.0f}}};
1447       Color max = {{{-1.0f, -1.0f, -1.0f}}};
1448 
1449       for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
1450         for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
1451           Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
1452           min = ColorMin(min, e);
1453           max = ColorMax(max, e);
1454         }
1455       }
1456 
1457       // Instead of reimplementing the sampling algorithm, confirm that the
1458       // sample output is within the range of the min and max of the nearest
1459       // points.
1460       EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
1461     }
1462   }
1463 }
1464 
TEST_F(GainMapMathTest,SampleP010)1465 TEST_F(GainMapMathTest, SampleP010) {
1466   auto image = P010Image();
1467   Color(*colors)[4] = P010Colors();
1468 
1469   static const size_t kMapScaleFactor = 2;
1470   for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
1471     for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
1472       Color min = {{{1.0f, 1.0f, 1.0f}}};
1473       Color max = {{{-1.0f, -1.0f, -1.0f}}};
1474 
1475       for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
1476         for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
1477           Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
1478           min = ColorMin(min, e);
1479           max = ColorMax(max, e);
1480         }
1481       }
1482 
1483       // Instead of reimplementing the sampling algorithm, confirm that the
1484       // sample output is within the range of the min and max of the nearest
1485       // points.
1486       EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
1487     }
1488   }
1489 }
1490 
TEST_F(GainMapMathTest,SampleMap)1491 TEST_F(GainMapMathTest, SampleMap) {
1492   auto image = MapImage();
1493   float(*values)[4] = MapValues();
1494 
1495   static const size_t kMapScaleFactor = 2;
1496   ShepardsIDW idwTable(kMapScaleFactor);
1497   for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
1498     for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
1499       size_t x_base = x / kMapScaleFactor;
1500       size_t y_base = y / kMapScaleFactor;
1501 
1502       float min = 1.0f;
1503       float max = -1.0f;
1504 
1505       min = fmin(min, values[y_base][x_base]);
1506       max = fmax(max, values[y_base][x_base]);
1507       if (y_base + 1 < 4) {
1508         min = fmin(min, values[y_base + 1][x_base]);
1509         max = fmax(max, values[y_base + 1][x_base]);
1510       }
1511       if (x_base + 1 < 4) {
1512         min = fmin(min, values[y_base][x_base + 1]);
1513         max = fmax(max, values[y_base][x_base + 1]);
1514       }
1515       if (y_base + 1 < 4 && x_base + 1 < 4) {
1516         min = fmin(min, values[y_base + 1][x_base + 1]);
1517         max = fmax(max, values[y_base + 1][x_base + 1]);
1518       }
1519 
1520       // Instead of reimplementing the sampling algorithm, confirm that the
1521       // sample output is within the range of the min and max of the nearest
1522       // points.
1523       EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
1524                   testing::AllOf(testing::Ge(min), testing::Le(max)));
1525       EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
1526                 sampleMap(&image, kMapScaleFactor, x, y));
1527     }
1528   }
1529 }
1530 
TEST_F(GainMapMathTest,ColorToRgba1010102)1531 TEST_F(GainMapMathTest, ColorToRgba1010102) {
1532   EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
1533   EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
1534   EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
1535   EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
1536   EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
1537 
1538   Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
1539   EXPECT_EQ(colorToRgba1010102(e_gamma),
1540             0x3 << 30 | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff) + 0.5) |
1541                 static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff) + 0.5) << 10 |
1542                 static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff) + 0.5) << 20);
1543 }
1544 
TEST_F(GainMapMathTest,ColorToRgbaF16)1545 TEST_F(GainMapMathTest, ColorToRgbaF16) {
1546   EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t)0x3C00) << 48);
1547   EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00);
1548   EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t)0x3C00) << 48) | ((uint64_t)0x3C00));
1549   EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 16));
1550   EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 32));
1551 
1552   Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
1553   EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66);
1554 }
1555 
TEST_F(GainMapMathTest,Float32ToFloat16)1556 TEST_F(GainMapMathTest, Float32ToFloat16) {
1557   EXPECT_EQ(floatToHalf(0.1f), 0x2E66);
1558   EXPECT_EQ(floatToHalf(0.0f), 0x0);
1559   EXPECT_EQ(floatToHalf(1.0f), 0x3C00);
1560   EXPECT_EQ(floatToHalf(-1.0f), 0xBC00);
1561   EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF);   // float max
1562   EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF);  // float min
1563   EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0);          // float zero
1564 }
1565 
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgb)1566 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) {
1567   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance), 0.0f);
1568   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance), kSdrWhiteNits);
1569   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
1570               srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
1571   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
1572               srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1573   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
1574               srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1575 }
1576 
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgbP3)1577 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) {
1578   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance), 0.0f);
1579   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance), kSdrWhiteNits);
1580   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance), p3Luminance(RgbRed()) * kSdrWhiteNits,
1581               LuminanceEpsilon());
1582   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
1583               p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1584   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
1585               p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1586 }
1587 
TEST_F(GainMapMathTest,GenerateMapLuminanceSrgbBt2100)1588 TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) {
1589   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance), 0.0f);
1590   EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance), kSdrWhiteNits);
1591   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
1592               bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
1593   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
1594               bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
1595   EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
1596               bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
1597 }
1598 
TEST_F(GainMapMathTest,GenerateMapLuminanceHlg)1599 TEST_F(GainMapMathTest, GenerateMapLuminanceHlg) {
1600   EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion, bt2100Luminance,
1601                                        kHlgMaxNits),
1602                   0.0f);
1603   EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion, bt2100Luminance,
1604                                        kHlgMaxNits),
1605                   kHlgMaxNits);
1606   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion, bt2100Luminance,
1607                                    kHlgMaxNits),
1608               bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
1609   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
1610                                    bt2100Luminance, kHlgMaxNits),
1611               bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
1612   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion, bt2100Luminance,
1613                                    kHlgMaxNits),
1614               bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
1615 }
1616 
TEST_F(GainMapMathTest,GenerateMapLuminancePq)1617 TEST_F(GainMapMathTest, GenerateMapLuminancePq) {
1618   EXPECT_FLOAT_EQ(
1619       Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
1620       0.0f);
1621   EXPECT_FLOAT_EQ(
1622       Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
1623       kPqMaxNits);
1624   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion, bt2100Luminance,
1625                                    kPqMaxNits),
1626               bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
1627   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion, bt2100Luminance,
1628                                    kPqMaxNits),
1629               bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
1630   EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion, bt2100Luminance,
1631                                    kPqMaxNits),
1632               bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
1633 }
1634 
TEST_F(GainMapMathTest,ApplyMap)1635 TEST_F(GainMapMathTest, ApplyMap) {
1636   uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion);
1637 
1638   std::fill_n(metadata.min_content_boost, 3, 1.0f / 8.0f);
1639   std::fill_n(metadata.max_content_boost, 3, 8.0f);
1640   std::fill_n(metadata.offset_sdr, 3, 0.0f);
1641   std::fill_n(metadata.offset_hdr, 3, 0.0f);
1642   std::fill_n(metadata.gamma, 3, 1.0f);
1643 
1644   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1645   EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata), RgbBlack());
1646   EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata), RgbRed() * 8.0f);
1647   EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata), RgbGreen() * 8.0f);
1648   EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata), RgbBlue() * 8.0f);
1649 
1650   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata), RgbWhite() * sqrt(8.0f));
1651   EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata), RgbBlack());
1652   EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata), RgbRed() * sqrt(8.0f));
1653   EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata), RgbGreen() * sqrt(8.0f));
1654   EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata), RgbBlue() * sqrt(8.0f));
1655 
1656   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite());
1657   EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata), RgbBlack());
1658   EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata), RgbRed());
1659   EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata), RgbGreen());
1660   EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata), RgbBlue());
1661 
1662   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite() / sqrt(8.0f));
1663   EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata), RgbBlack());
1664   EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata), RgbRed() / sqrt(8.0f));
1665   EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata), RgbGreen() / sqrt(8.0f));
1666   EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata), RgbBlue() / sqrt(8.0f));
1667 
1668   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
1669   EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata), RgbBlack());
1670   EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata), RgbRed() / 8.0f);
1671   EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata), RgbGreen() / 8.0f);
1672   EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata), RgbBlue() / 8.0f);
1673 
1674   metadata.max_content_boost[0] = 8.0f;
1675   metadata.min_content_boost[0] = 1.0f;
1676 
1677   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1678   EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
1679   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
1680   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite());
1681 
1682   metadata.max_content_boost[0] = 8.0f;
1683   metadata.min_content_boost[0] = 0.5f;
1684 
1685   EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
1686   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), RgbWhite() * 4.0f);
1687   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
1688   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite());
1689   EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
1690 }
1691 
1692 }  // namespace ultrahdr
1693