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