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