| /* |
| * Copyright 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <gtest/gtest.h> |
| #include <gmock/gmock.h> |
| |
| #include "ultrahdr/gainmapmath.h" |
| |
| namespace ultrahdr { |
| |
| class GainMapMathTest : public testing::Test { |
| public: |
| GainMapMathTest(); |
| ~GainMapMathTest(); |
| |
| float ComparisonEpsilon() { return 1e-4f; } |
| float LuminanceEpsilon() { return 1e-2f; } |
| float YuvConversionEpsilon() { return 1.0f / (255.0f * 2.0f); } |
| |
| Color Yuv420(uint8_t y, uint8_t u, uint8_t v) { |
| return {{{static_cast<float>(y) * (1 / 255.0f), static_cast<float>(u - 128) * (1 / 255.0f), |
| static_cast<float>(v - 128) * (1 / 255.0f)}}}; |
| } |
| |
| Color P010(uint16_t y, uint16_t u, uint16_t v) { |
| return {{{static_cast<float>(y - 64) * (1 / 876.0f), |
| static_cast<float>(u - 64) * (1 / 896.0f) - 0.5f, |
| static_cast<float>(v - 64) * (1 / 896.0f) - 0.5f}}}; |
| } |
| |
| // Using int16_t allows for testing fixed-point implementations. |
| struct Pixel { |
| int16_t y; |
| int16_t u; |
| int16_t v; |
| }; |
| |
| Pixel getYuv420Pixel_uint(uhdr_raw_image_t* image, size_t x, size_t y) { |
| uint8_t* luma_data = reinterpret_cast<uint8_t*>(image->planes[UHDR_PLANE_Y]); |
| size_t luma_stride = image->stride[UHDR_PLANE_Y]; |
| uint8_t* cb_data = reinterpret_cast<uint8_t*>(image->planes[UHDR_PLANE_U]); |
| size_t cb_stride = image->stride[UHDR_PLANE_U]; |
| uint8_t* cr_data = reinterpret_cast<uint8_t*>(image->planes[UHDR_PLANE_V]); |
| size_t cr_stride = image->stride[UHDR_PLANE_V]; |
| |
| size_t pixel_y_idx = x + y * luma_stride; |
| size_t pixel_cb_idx = x / 2 + (y / 2) * cb_stride; |
| size_t pixel_cr_idx = x / 2 + (y / 2) * cr_stride; |
| |
| uint8_t y_uint = luma_data[pixel_y_idx]; |
| uint8_t u_uint = cb_data[pixel_cb_idx]; |
| uint8_t v_uint = cr_data[pixel_cr_idx]; |
| |
| return {y_uint, u_uint, v_uint}; |
| } |
| |
| float Map(uint8_t e) { return static_cast<float>(e) / 255.0f; } |
| |
| Color ColorMin(Color e1, Color e2) { |
| return {{{fminf(e1.r, e2.r), fminf(e1.g, e2.g), fminf(e1.b, e2.b)}}}; |
| } |
| |
| Color ColorMax(Color e1, Color e2) { |
| return {{{fmaxf(e1.r, e2.r), fmaxf(e1.g, e2.g), fmaxf(e1.b, e2.b)}}}; |
| } |
| |
| Color RgbBlack() { return {{{0.0f, 0.0f, 0.0f}}}; } |
| Color RgbWhite() { return {{{1.0f, 1.0f, 1.0f}}}; } |
| |
| Color RgbRed() { return {{{1.0f, 0.0f, 0.0f}}}; } |
| Color RgbGreen() { return {{{0.0f, 1.0f, 0.0f}}}; } |
| Color RgbBlue() { return {{{0.0f, 0.0f, 1.0f}}}; } |
| |
| Color YuvBlack() { return {{{0.0f, 0.0f, 0.0f}}}; } |
| Color YuvWhite() { return {{{1.0f, 0.0f, 0.0f}}}; } |
| |
| Color SrgbYuvRed() { return {{{0.2126f, -0.11457f, 0.5f}}}; } |
| Color SrgbYuvGreen() { return {{{0.7152f, -0.38543f, -0.45415f}}}; } |
| Color SrgbYuvBlue() { return {{{0.0722f, 0.5f, -0.04585f}}}; } |
| |
| Color P3YuvRed() { return {{{0.299f, -0.16874f, 0.5f}}}; } |
| Color P3YuvGreen() { return {{{0.587f, -0.33126f, -0.41869f}}}; } |
| Color P3YuvBlue() { return {{{0.114f, 0.5f, -0.08131f}}}; } |
| |
| Color Bt2100YuvRed() { return {{{0.2627f, -0.13963f, 0.5f}}}; } |
| Color Bt2100YuvGreen() { return {{{0.6780f, -0.36037f, -0.45979f}}}; } |
| Color Bt2100YuvBlue() { return {{{0.0593f, 0.5f, -0.04021f}}}; } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Reference values for when using fixed-point arithmetic. |
| |
| Pixel RgbBlackPixel() { return {0, 0, 0}; } |
| Pixel RgbWhitePixel() { return {255, 255, 255}; } |
| |
| Pixel RgbRedPixel() { return {255, 0, 0}; } |
| Pixel RgbGreenPixel() { return {0, 255, 0}; } |
| Pixel RgbBluePixel() { return {0, 0, 255}; } |
| |
| Pixel YuvBlackPixel() { return {0, 0, 0}; } |
| Pixel YuvWhitePixel() { return {255, 0, 0}; } |
| |
| Pixel SrgbYuvRedPixel() { return {54, -29, 128}; } |
| Pixel SrgbYuvGreenPixel() { return {182, -98, -116}; } |
| Pixel SrgbYuvBluePixel() { return {18, 128, -12}; } |
| |
| Pixel P3YuvRedPixel() { return {76, -43, 128}; } |
| Pixel P3YuvGreenPixel() { return {150, -84, -107}; } |
| Pixel P3YuvBluePixel() { return {29, 128, -21}; } |
| |
| Pixel Bt2100YuvRedPixel() { return {67, -36, 128}; } |
| Pixel Bt2100YuvGreenPixel() { return {173, -92, -117}; } |
| Pixel Bt2100YuvBluePixel() { return {15, 128, -10}; } |
| |
| float SrgbYuvToLuminance(Color yuv_gamma, LuminanceFn luminanceFn) { |
| Color rgb_gamma = srgbYuvToRgb(yuv_gamma); |
| Color rgb = srgbInvOetf(rgb_gamma); |
| float luminance_scaled = luminanceFn(rgb); |
| return luminance_scaled * kSdrWhiteNits; |
| } |
| |
| float P3YuvToLuminance(Color yuv_gamma, LuminanceFn luminanceFn) { |
| Color rgb_gamma = p3YuvToRgb(yuv_gamma); |
| Color rgb = srgbInvOetf(rgb_gamma); |
| float luminance_scaled = luminanceFn(rgb); |
| return luminance_scaled * kSdrWhiteNits; |
| } |
| |
| float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf, |
| ColorTransformFn gamutConversionFn, LuminanceFn luminanceFn, |
| float scale_factor) { |
| Color rgb_gamma = bt2100YuvToRgb(yuv_gamma); |
| Color rgb = hdrInvOetf(rgb_gamma); |
| rgb = gamutConversionFn(rgb); |
| float luminance_scaled = luminanceFn(rgb); |
| return luminance_scaled * scale_factor; |
| } |
| |
| Color Recover(Color yuv_gamma, float gain, uhdr_gainmap_metadata_ext_t* metadata) { |
| Color rgb_gamma = srgbYuvToRgb(yuv_gamma); |
| Color rgb = srgbInvOetf(rgb_gamma); |
| return applyGain(rgb, gain, metadata); |
| } |
| |
| uhdr_raw_image_t Yuv420Image() { |
| static uint8_t pixels[] = { |
| // Y |
| 0x00, |
| 0x10, |
| 0x20, |
| 0x30, |
| 0x01, |
| 0x11, |
| 0x21, |
| 0x31, |
| 0x02, |
| 0x12, |
| 0x22, |
| 0x32, |
| 0x03, |
| 0x13, |
| 0x23, |
| 0x33, |
| // U |
| 0xA0, |
| 0xA1, |
| 0xA2, |
| 0xA3, |
| // V |
| 0xB0, |
| 0xB1, |
| 0xB2, |
| 0xB3, |
| }; |
| uhdr_raw_image_t img; |
| img.cg = UHDR_CG_BT_709; |
| img.ct = UHDR_CT_SRGB; |
| img.range = UHDR_CR_FULL_RANGE; |
| img.fmt = UHDR_IMG_FMT_12bppYCbCr420; |
| img.w = 4; |
| img.h = 4; |
| img.planes[UHDR_PLANE_Y] = pixels; |
| img.planes[UHDR_PLANE_U] = pixels + 16; |
| img.planes[UHDR_PLANE_V] = pixels + 16 + 4; |
| img.stride[UHDR_PLANE_Y] = 4; |
| img.stride[UHDR_PLANE_U] = 2; |
| img.stride[UHDR_PLANE_V] = 2; |
| return img; |
| } |
| |
| uhdr_raw_image_t Yuv420Image32x4() { |
| // clang-format off |
| static uint8_t pixels[] = { |
| // Y |
| 0x0, 0x10, 0x20, 0x30, 0x1, 0x11, 0x21, 0x31, 0x2, 0x12, 0x22, 0x32, 0x3, 0x13, 0x23, 0x33, |
| 0x4, 0x14, 0x24, 0x34, 0x5, 0x15, 0x25, 0x35, 0x6, 0x16, 0x26, 0x36, 0x7, 0x17, 0x27, 0x37, |
| 0x8, 0x18, 0x28, 0x38, 0x9, 0x19, 0x29, 0x39, 0xa, 0x1a, 0x2a, 0x3a, 0xb, 0x1b, 0x2b, 0x3b, |
| 0xc, 0x1c, 0x2c, 0x3c, 0xd, 0x1d, 0x2d, 0x3d, 0xe, 0x1e, 0x2e, 0x3e, 0xf, 0x1f, 0x2f, 0x3f, |
| 0x10, 0x20, 0x30, 0x40, 0x11, 0x21, 0x31, 0x41, 0x12, 0x22, 0x32, 0x42, 0x13, 0x23, 0x33, 0x43, |
| 0x14, 0x24, 0x34, 0x44, 0x15, 0x25, 0x35, 0x45, 0x16, 0x26, 0x36, 0x46, 0x17, 0x27, 0x37, 0x47, |
| 0x18, 0x28, 0x38, 0x48, 0x19, 0x29, 0x39, 0x49, 0x1a, 0x2a, 0x3a, 0x4a, 0x1b, 0x2b, 0x3b, 0x4b, |
| 0x1c, 0x2c, 0x3c, 0x4c, 0x1d, 0x2d, 0x3d, 0x4d, 0x1e, 0x2e, 0x3e, 0x4e, 0x1f, 0x2f, 0x3f, 0x4f, |
| // U |
| 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, |
| 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBB, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, |
| // V |
| 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCC, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, |
| 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDD, 0xDD, 0xDC, 0xDD, 0xDE, 0xDF, |
| }; |
| // clang-format on |
| uhdr_raw_image_t img; |
| img.cg = UHDR_CG_BT_709; |
| img.ct = UHDR_CT_SRGB; |
| img.range = UHDR_CR_FULL_RANGE; |
| img.fmt = UHDR_IMG_FMT_12bppYCbCr420; |
| img.w = 32; |
| img.h = 4; |
| img.planes[UHDR_PLANE_Y] = pixels; |
| img.planes[UHDR_PLANE_U] = pixels + 128; |
| img.planes[UHDR_PLANE_V] = pixels + 128 + 32; |
| img.stride[UHDR_PLANE_Y] = 32; |
| img.stride[UHDR_PLANE_U] = 16; |
| img.stride[UHDR_PLANE_V] = 16; |
| return img; |
| } |
| |
| Color (*Yuv420Colors())[4] { |
| static Color colors[4][4] = { |
| { |
| Yuv420(0x00, 0xA0, 0xB0), |
| Yuv420(0x10, 0xA0, 0xB0), |
| Yuv420(0x20, 0xA1, 0xB1), |
| Yuv420(0x30, 0xA1, 0xB1), |
| }, |
| { |
| Yuv420(0x01, 0xA0, 0xB0), |
| Yuv420(0x11, 0xA0, 0xB0), |
| Yuv420(0x21, 0xA1, 0xB1), |
| Yuv420(0x31, 0xA1, 0xB1), |
| }, |
| { |
| Yuv420(0x02, 0xA2, 0xB2), |
| Yuv420(0x12, 0xA2, 0xB2), |
| Yuv420(0x22, 0xA3, 0xB3), |
| Yuv420(0x32, 0xA3, 0xB3), |
| }, |
| { |
| Yuv420(0x03, 0xA2, 0xB2), |
| Yuv420(0x13, 0xA2, 0xB2), |
| Yuv420(0x23, 0xA3, 0xB3), |
| Yuv420(0x33, 0xA3, 0xB3), |
| }, |
| }; |
| return colors; |
| } |
| |
| uhdr_raw_image_t P010Image() { |
| static uint16_t pixels[] = { |
| // Y |
| 0x00 << 6, |
| 0x10 << 6, |
| 0x20 << 6, |
| 0x30 << 6, |
| 0x01 << 6, |
| 0x11 << 6, |
| 0x21 << 6, |
| 0x31 << 6, |
| 0x02 << 6, |
| 0x12 << 6, |
| 0x22 << 6, |
| 0x32 << 6, |
| 0x03 << 6, |
| 0x13 << 6, |
| 0x23 << 6, |
| 0x33 << 6, |
| // UV |
| 0xA0 << 6, |
| 0xB0 << 6, |
| 0xA1 << 6, |
| 0xB1 << 6, |
| 0xA2 << 6, |
| 0xB2 << 6, |
| 0xA3 << 6, |
| 0xB3 << 6, |
| }; |
| uhdr_raw_image_t img; |
| img.cg = UHDR_CG_BT_709; |
| img.ct = UHDR_CT_HLG; |
| img.range = UHDR_CR_LIMITED_RANGE; |
| img.fmt = UHDR_IMG_FMT_24bppYCbCrP010; |
| img.w = 4; |
| img.h = 4; |
| img.planes[UHDR_PLANE_Y] = pixels; |
| img.planes[UHDR_PLANE_UV] = pixels + 16; |
| img.planes[UHDR_PLANE_V] = nullptr; |
| img.stride[UHDR_PLANE_Y] = 4; |
| img.stride[UHDR_PLANE_UV] = 4; |
| img.stride[UHDR_PLANE_V] = 0; |
| return img; |
| } |
| |
| Color (*P010Colors())[4] { |
| static Color colors[4][4] = { |
| { |
| P010(0x00, 0xA0, 0xB0), |
| P010(0x10, 0xA0, 0xB0), |
| P010(0x20, 0xA1, 0xB1), |
| P010(0x30, 0xA1, 0xB1), |
| }, |
| { |
| P010(0x01, 0xA0, 0xB0), |
| P010(0x11, 0xA0, 0xB0), |
| P010(0x21, 0xA1, 0xB1), |
| P010(0x31, 0xA1, 0xB1), |
| }, |
| { |
| P010(0x02, 0xA2, 0xB2), |
| P010(0x12, 0xA2, 0xB2), |
| P010(0x22, 0xA3, 0xB3), |
| P010(0x32, 0xA3, 0xB3), |
| }, |
| { |
| P010(0x03, 0xA2, 0xB2), |
| P010(0x13, 0xA2, 0xB2), |
| P010(0x23, 0xA3, 0xB3), |
| P010(0x33, 0xA3, 0xB3), |
| }, |
| }; |
| return colors; |
| } |
| |
| uhdr_raw_image_t MapImage() { |
| static uint8_t pixels[] = { |
| 0x00, 0x10, 0x20, 0x30, 0x01, 0x11, 0x21, 0x31, |
| 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, |
| }; |
| |
| uhdr_raw_image_t img; |
| img.cg = UHDR_CG_UNSPECIFIED; |
| img.ct = UHDR_CT_UNSPECIFIED; |
| img.range = UHDR_CR_UNSPECIFIED; |
| img.fmt = UHDR_IMG_FMT_8bppYCbCr400; |
| img.w = 4; |
| img.h = 4; |
| img.planes[UHDR_PLANE_Y] = pixels; |
| img.planes[UHDR_PLANE_U] = nullptr; |
| img.planes[UHDR_PLANE_V] = nullptr; |
| img.stride[UHDR_PLANE_Y] = 4; |
| img.stride[UHDR_PLANE_U] = 0; |
| img.stride[UHDR_PLANE_V] = 0; |
| return img; |
| } |
| |
| float (*MapValues())[4] { |
| static float values[4][4] = { |
| { |
| Map(0x00), |
| Map(0x10), |
| Map(0x20), |
| Map(0x30), |
| }, |
| { |
| Map(0x01), |
| Map(0x11), |
| Map(0x21), |
| Map(0x31), |
| }, |
| { |
| Map(0x02), |
| Map(0x12), |
| Map(0x22), |
| Map(0x32), |
| }, |
| { |
| Map(0x03), |
| Map(0x13), |
| Map(0x23), |
| Map(0x33), |
| }, |
| }; |
| return values; |
| } |
| |
| protected: |
| virtual void SetUp(); |
| virtual void TearDown(); |
| }; |
| |
| GainMapMathTest::GainMapMathTest() {} |
| GainMapMathTest::~GainMapMathTest() {} |
| |
| void GainMapMathTest::SetUp() {} |
| void GainMapMathTest::TearDown() {} |
| |
| #define EXPECT_RGB_EQ(e1, e2) \ |
| EXPECT_FLOAT_EQ((e1).r, (e2).r); \ |
| EXPECT_FLOAT_EQ((e1).g, (e2).g); \ |
| EXPECT_FLOAT_EQ((e1).b, (e2).b) |
| |
| #define EXPECT_RGB_NEAR(e1, e2) \ |
| EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \ |
| EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \ |
| EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon()) |
| |
| #define EXPECT_RGB_CLOSE(e1, e2) \ |
| EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \ |
| EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \ |
| EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f) |
| |
| #define EXPECT_YUV_EQ(e1, e2) \ |
| EXPECT_FLOAT_EQ((e1).y, (e2).y); \ |
| EXPECT_FLOAT_EQ((e1).u, (e2).u); \ |
| EXPECT_FLOAT_EQ((e1).v, (e2).v) |
| |
| #define EXPECT_YUV_NEAR(e1, e2) \ |
| EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \ |
| EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \ |
| EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon()) |
| |
| // Due to -ffp-contract=fast being enabled by default with GCC, allow some |
| // margin when comparing fused and unfused floating-point operations. |
| #define EXPECT_YUV_BETWEEN(e, min, max) \ |
| EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y - ComparisonEpsilon()), \ |
| testing::Le((max).y + ComparisonEpsilon()))); \ |
| EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u - ComparisonEpsilon()), \ |
| testing::Le((max).u + ComparisonEpsilon()))); \ |
| EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v - ComparisonEpsilon()), \ |
| testing::Le((max).v + ComparisonEpsilon()))) |
| |
| // TODO: a bunch of these tests can be parameterized. |
| |
| TEST_F(GainMapMathTest, ColorConstruct) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| EXPECT_FLOAT_EQ(e1.r, 0.1f); |
| EXPECT_FLOAT_EQ(e1.g, 0.2f); |
| EXPECT_FLOAT_EQ(e1.b, 0.3f); |
| |
| EXPECT_FLOAT_EQ(e1.y, 0.1f); |
| EXPECT_FLOAT_EQ(e1.u, 0.2f); |
| EXPECT_FLOAT_EQ(e1.v, 0.3f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorAddColor) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 + e1; |
| EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f); |
| |
| e2 += e1; |
| EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorAddFloat) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 + 0.1f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f); |
| |
| e2 += 0.1f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorSubtractColor) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 - e1; |
| EXPECT_FLOAT_EQ(e2.r, 0.0f); |
| EXPECT_FLOAT_EQ(e2.g, 0.0f); |
| EXPECT_FLOAT_EQ(e2.b, 0.0f); |
| |
| e2 -= e1; |
| EXPECT_FLOAT_EQ(e2.r, -e1.r); |
| EXPECT_FLOAT_EQ(e2.g, -e1.g); |
| EXPECT_FLOAT_EQ(e2.b, -e1.b); |
| } |
| |
| TEST_F(GainMapMathTest, ColorSubtractFloat) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 - 0.1f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f); |
| |
| e2 -= 0.1f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorMultiplyFloat) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 * 2.0f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f); |
| |
| e2 *= 2.0f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorDivideFloat) { |
| Color e1 = {{{0.1f, 0.2f, 0.3f}}}; |
| |
| Color e2 = e1 / 2.0f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f); |
| |
| e2 /= 2.0f; |
| EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f); |
| EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f); |
| EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f); |
| } |
| |
| TEST_F(GainMapMathTest, SrgbLuminance) { |
| EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f); |
| EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f); |
| EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f); |
| EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f); |
| EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f); |
| } |
| |
| TEST_F(GainMapMathTest, SrgbYuvToRgb) { |
| Color rgb_black = srgbYuvToRgb(YuvBlack()); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = srgbYuvToRgb(YuvWhite()); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = srgbYuvToRgb(SrgbYuvRed()); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = srgbYuvToRgb(SrgbYuvGreen()); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = srgbYuvToRgb(SrgbYuvBlue()); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, SrgbRgbToYuv) { |
| Color yuv_black = srgbRgbToYuv(RgbBlack()); |
| EXPECT_YUV_NEAR(yuv_black, YuvBlack()); |
| |
| Color yuv_white = srgbRgbToYuv(RgbWhite()); |
| EXPECT_YUV_NEAR(yuv_white, YuvWhite()); |
| |
| Color yuv_r = srgbRgbToYuv(RgbRed()); |
| EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed()); |
| |
| Color yuv_g = srgbRgbToYuv(RgbGreen()); |
| EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen()); |
| |
| Color yuv_b = srgbRgbToYuv(RgbBlue()); |
| EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, SrgbRgbYuvRoundtrip) { |
| Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack())); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite())); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed())); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen())); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue())); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, SrgbTransferFunction) { |
| EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f); |
| EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon()); |
| EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon()); |
| EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f); |
| } |
| |
| TEST_F(GainMapMathTest, P3Luminance) { |
| EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f); |
| EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f); |
| EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f); |
| EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f); |
| EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f); |
| } |
| |
| TEST_F(GainMapMathTest, P3YuvToRgb) { |
| Color rgb_black = p3YuvToRgb(YuvBlack()); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = p3YuvToRgb(YuvWhite()); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = p3YuvToRgb(P3YuvRed()); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = p3YuvToRgb(P3YuvGreen()); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = p3YuvToRgb(P3YuvBlue()); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, P3RgbToYuv) { |
| Color yuv_black = p3RgbToYuv(RgbBlack()); |
| EXPECT_YUV_NEAR(yuv_black, YuvBlack()); |
| |
| Color yuv_white = p3RgbToYuv(RgbWhite()); |
| EXPECT_YUV_NEAR(yuv_white, YuvWhite()); |
| |
| Color yuv_r = p3RgbToYuv(RgbRed()); |
| EXPECT_YUV_NEAR(yuv_r, P3YuvRed()); |
| |
| Color yuv_g = p3RgbToYuv(RgbGreen()); |
| EXPECT_YUV_NEAR(yuv_g, P3YuvGreen()); |
| |
| Color yuv_b = p3RgbToYuv(RgbBlue()); |
| EXPECT_YUV_NEAR(yuv_b, P3YuvBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, P3RgbYuvRoundtrip) { |
| Color rgb_black = p3YuvToRgb(p3RgbToYuv(RgbBlack())); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = p3YuvToRgb(p3RgbToYuv(RgbWhite())); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = p3YuvToRgb(p3RgbToYuv(RgbRed())); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = p3YuvToRgb(p3RgbToYuv(RgbGreen())); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = p3YuvToRgb(p3RgbToYuv(RgbBlue())); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| TEST_F(GainMapMathTest, Bt2100Luminance) { |
| EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f); |
| EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f); |
| EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f); |
| EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f); |
| EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f); |
| } |
| |
| TEST_F(GainMapMathTest, Bt2100YuvToRgb) { |
| Color rgb_black = bt2100YuvToRgb(YuvBlack()); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = bt2100YuvToRgb(YuvWhite()); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed()); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen()); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue()); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, Bt2100RgbToYuv) { |
| Color yuv_black = bt2100RgbToYuv(RgbBlack()); |
| EXPECT_YUV_NEAR(yuv_black, YuvBlack()); |
| |
| Color yuv_white = bt2100RgbToYuv(RgbWhite()); |
| EXPECT_YUV_NEAR(yuv_white, YuvWhite()); |
| |
| Color yuv_r = bt2100RgbToYuv(RgbRed()); |
| EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed()); |
| |
| Color yuv_g = bt2100RgbToYuv(RgbGreen()); |
| EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen()); |
| |
| Color yuv_b = bt2100RgbToYuv(RgbBlue()); |
| EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, Bt2100RgbYuvRoundtrip) { |
| Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack())); |
| EXPECT_RGB_NEAR(rgb_black, RgbBlack()); |
| |
| Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite())); |
| EXPECT_RGB_NEAR(rgb_white, RgbWhite()); |
| |
| Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed())); |
| EXPECT_RGB_NEAR(rgb_r, RgbRed()); |
| |
| Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen())); |
| EXPECT_RGB_NEAR(rgb_g, RgbGreen()); |
| |
| Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue())); |
| EXPECT_RGB_NEAR(rgb_b, RgbBlue()); |
| } |
| |
| TEST_F(GainMapMathTest, YuvColorGamutConversion) { |
| const std::array<Color, 5> SrgbYuvColors{YuvBlack(), YuvWhite(), SrgbYuvRed(), SrgbYuvGreen(), |
| SrgbYuvBlue()}; |
| |
| const std::array<Color, 5> P3YuvColors{YuvBlack(), YuvWhite(), P3YuvRed(), P3YuvGreen(), |
| P3YuvBlue()}; |
| |
| const std::array<Color, 5> Bt2100YuvColors{YuvBlack(), YuvWhite(), Bt2100YuvRed(), |
| Bt2100YuvGreen(), Bt2100YuvBlue()}; |
| /* |
| * Each tuple contains three elements. |
| * 0. An array containing 9 coefficients needed to perform the color gamut conversion |
| * 1. Array of colors to be used as test input |
| * 2. Array of colors to used as reference output |
| */ |
| const std::array<std::tuple<const std::array<float, 9>&, const std::array<Color, 5>, |
| const std::array<Color, 5>>, |
| 6> |
| coeffs_setup_expected{{ |
| {kYuvBt709ToBt601, SrgbYuvColors, P3YuvColors}, |
| {kYuvBt709ToBt2100, SrgbYuvColors, Bt2100YuvColors}, |
| {kYuvBt601ToBt709, P3YuvColors, SrgbYuvColors}, |
| {kYuvBt601ToBt2100, P3YuvColors, Bt2100YuvColors}, |
| {kYuvBt2100ToBt709, Bt2100YuvColors, SrgbYuvColors}, |
| {kYuvBt2100ToBt601, Bt2100YuvColors, P3YuvColors}, |
| }}; |
| |
| for (const auto& [coeffs, input, expected] : coeffs_setup_expected) { |
| for (size_t color_idx = 0; color_idx < SrgbYuvColors.size(); ++color_idx) { |
| const Color input_color = input.at(color_idx); |
| const Color output_color = yuvColorGamutConversion(input_color, coeffs); |
| |
| EXPECT_YUV_NEAR(expected.at(color_idx), output_color); |
| } |
| } |
| } |
| |
| #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON))) |
| TEST_F(GainMapMathTest, YuvConversionNeon) { |
| const std::array<Pixel, 5> SrgbYuvColors{YuvBlackPixel(), YuvWhitePixel(), SrgbYuvRedPixel(), |
| SrgbYuvGreenPixel(), SrgbYuvBluePixel()}; |
| |
| const std::array<Pixel, 5> P3YuvColors{YuvBlackPixel(), YuvWhitePixel(), P3YuvRedPixel(), |
| P3YuvGreenPixel(), P3YuvBluePixel()}; |
| |
| const std::array<Pixel, 5> Bt2100YuvColors{YuvBlackPixel(), YuvWhitePixel(), Bt2100YuvRedPixel(), |
| Bt2100YuvGreenPixel(), Bt2100YuvBluePixel()}; |
| |
| struct InputSamples { |
| std::array<uint8_t, 8> y; |
| std::array<int16_t, 8> u; |
| std::array<int16_t, 8> v; |
| }; |
| |
| struct ExpectedSamples { |
| std::array<int16_t, 8> y; |
| std::array<int16_t, 8> u; |
| std::array<int16_t, 8> v; |
| }; |
| |
| // Each tuple contains three elements. |
| // 0. A pointer to the coefficients that will be passed to the Neon implementation |
| // 1. Input pixel/color array |
| // 2. The expected results |
| const std::array< |
| std::tuple<const int16_t*, const std::array<Pixel, 5>, const std::array<Pixel, 5>>, 6> |
| coeffs_setup_correct{{ |
| {kYuv709To601_coeffs_neon, SrgbYuvColors, P3YuvColors}, |
| {kYuv709To2100_coeffs_neon, SrgbYuvColors, Bt2100YuvColors}, |
| {kYuv601To709_coeffs_neon, P3YuvColors, SrgbYuvColors}, |
| {kYuv601To2100_coeffs_neon, P3YuvColors, Bt2100YuvColors}, |
| {kYuv2100To709_coeffs_neon, Bt2100YuvColors, SrgbYuvColors}, |
| {kYuv2100To601_coeffs_neon, Bt2100YuvColors, P3YuvColors}, |
| }}; |
| |
| for (const auto& [coeff_ptr, input, expected] : coeffs_setup_correct) { |
| const int16x8_t coeffs = vld1q_s16(coeff_ptr); |
| InputSamples input_values; |
| ExpectedSamples expected_values; |
| for (size_t sample_idx = 0; sample_idx < 8; ++sample_idx) { |
| size_t ring_idx = sample_idx % input.size(); |
| input_values.y.at(sample_idx) = static_cast<uint8_t>(input.at(ring_idx).y); |
| input_values.u.at(sample_idx) = input.at(ring_idx).u; |
| input_values.v.at(sample_idx) = input.at(ring_idx).v; |
| |
| expected_values.y.at(sample_idx) = expected.at(ring_idx).y; |
| expected_values.u.at(sample_idx) = expected.at(ring_idx).u; |
| expected_values.v.at(sample_idx) = expected.at(ring_idx).v; |
| } |
| |
| const uint8x8_t y_neon = vld1_u8(input_values.y.data()); |
| const int16x8_t u_neon = vld1q_s16(input_values.u.data()); |
| const int16x8_t v_neon = vld1q_s16(input_values.v.data()); |
| |
| const int16x8x3_t neon_result = yuvConversion_neon(y_neon, u_neon, v_neon, coeffs); |
| |
| const int16x8_t y_neon_result = neon_result.val[0]; |
| const int16x8_t u_neon_result = neon_result.val[1]; |
| const int16x8_t v_neon_result = neon_result.val[2]; |
| |
| const Pixel result0 = {vgetq_lane_s16(y_neon_result, 0), vgetq_lane_s16(u_neon_result, 0), |
| vgetq_lane_s16(v_neon_result, 0)}; |
| |
| const Pixel result1 = {vgetq_lane_s16(y_neon_result, 1), vgetq_lane_s16(u_neon_result, 1), |
| vgetq_lane_s16(v_neon_result, 1)}; |
| |
| const Pixel result2 = {vgetq_lane_s16(y_neon_result, 2), vgetq_lane_s16(u_neon_result, 2), |
| vgetq_lane_s16(v_neon_result, 2)}; |
| |
| const Pixel result3 = {vgetq_lane_s16(y_neon_result, 3), vgetq_lane_s16(u_neon_result, 3), |
| vgetq_lane_s16(v_neon_result, 3)}; |
| |
| const Pixel result4 = {vgetq_lane_s16(y_neon_result, 4), vgetq_lane_s16(u_neon_result, 4), |
| vgetq_lane_s16(v_neon_result, 4)}; |
| |
| const Pixel result5 = {vgetq_lane_s16(y_neon_result, 5), vgetq_lane_s16(u_neon_result, 5), |
| vgetq_lane_s16(v_neon_result, 5)}; |
| |
| const Pixel result6 = {vgetq_lane_s16(y_neon_result, 6), vgetq_lane_s16(u_neon_result, 6), |
| vgetq_lane_s16(v_neon_result, 6)}; |
| |
| const Pixel result7 = {vgetq_lane_s16(y_neon_result, 7), vgetq_lane_s16(u_neon_result, 7), |
| vgetq_lane_s16(v_neon_result, 7)}; |
| |
| EXPECT_NEAR(result0.y, expected_values.y.at(0), 1); |
| EXPECT_NEAR(result0.u, expected_values.u.at(0), 1); |
| EXPECT_NEAR(result0.v, expected_values.v.at(0), 1); |
| |
| EXPECT_NEAR(result1.y, expected_values.y.at(1), 1); |
| EXPECT_NEAR(result1.u, expected_values.u.at(1), 1); |
| EXPECT_NEAR(result1.v, expected_values.v.at(1), 1); |
| |
| EXPECT_NEAR(result2.y, expected_values.y.at(2), 1); |
| EXPECT_NEAR(result2.u, expected_values.u.at(2), 1); |
| EXPECT_NEAR(result2.v, expected_values.v.at(2), 1); |
| |
| EXPECT_NEAR(result3.y, expected_values.y.at(3), 1); |
| EXPECT_NEAR(result3.u, expected_values.u.at(3), 1); |
| EXPECT_NEAR(result3.v, expected_values.v.at(3), 1); |
| |
| EXPECT_NEAR(result4.y, expected_values.y.at(4), 1); |
| EXPECT_NEAR(result4.u, expected_values.u.at(4), 1); |
| EXPECT_NEAR(result4.v, expected_values.v.at(4), 1); |
| |
| EXPECT_NEAR(result5.y, expected_values.y.at(5), 1); |
| EXPECT_NEAR(result5.u, expected_values.u.at(5), 1); |
| EXPECT_NEAR(result5.v, expected_values.v.at(5), 1); |
| |
| EXPECT_NEAR(result6.y, expected_values.y.at(6), 1); |
| EXPECT_NEAR(result6.u, expected_values.u.at(6), 1); |
| EXPECT_NEAR(result6.v, expected_values.v.at(6), 1); |
| |
| EXPECT_NEAR(result7.y, expected_values.y.at(7), 1); |
| EXPECT_NEAR(result7.u, expected_values.u.at(7), 1); |
| EXPECT_NEAR(result7.v, expected_values.v.at(7), 1); |
| } |
| } |
| #endif |
| |
| TEST_F(GainMapMathTest, TransformYuv420) { |
| auto input = Yuv420Image(); |
| const size_t buf_size = input.w * input.h * 3 / 2; |
| std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(buf_size); |
| uint8_t* luma = out_buf.get(); |
| uint8_t* cb = luma + input.w * input.h; |
| uint8_t* cr = cb + input.w * input.h / 4; |
| |
| const std::array<std::array<float, 9>, 6> conversion_coeffs = { |
| kYuvBt709ToBt601, kYuvBt709ToBt2100, kYuvBt601ToBt709, |
| kYuvBt601ToBt2100, kYuvBt2100ToBt709, kYuvBt2100ToBt601}; |
| |
| for (size_t coeffs_idx = 0; coeffs_idx < conversion_coeffs.size(); ++coeffs_idx) { |
| auto output = Yuv420Image(); |
| memcpy(luma, input.planes[UHDR_PLANE_Y], input.w * input.h); |
| memcpy(cb, input.planes[UHDR_PLANE_U], input.w * input.h / 4); |
| memcpy(cr, input.planes[UHDR_PLANE_V], input.w * input.h / 4); |
| output.planes[UHDR_PLANE_Y] = luma; |
| output.planes[UHDR_PLANE_U] = cb; |
| output.planes[UHDR_PLANE_V] = cr; |
| |
| // Perform a color gamut conversion to the entire 4:2:0 image. |
| transformYuv420(&output, conversion_coeffs.at(coeffs_idx)); |
| |
| for (size_t y = 0; y < input.h; y += 2) { |
| for (size_t x = 0; x < input.w; x += 2) { |
| Pixel out1 = getYuv420Pixel_uint(&output, x, y); |
| Pixel out2 = getYuv420Pixel_uint(&output, x + 1, y); |
| Pixel out3 = getYuv420Pixel_uint(&output, x, y + 1); |
| Pixel out4 = getYuv420Pixel_uint(&output, x + 1, y + 1); |
| |
| Color in1 = getYuv420Pixel(&input, x, y); |
| Color in2 = getYuv420Pixel(&input, x + 1, y); |
| Color in3 = getYuv420Pixel(&input, x, y + 1); |
| Color in4 = getYuv420Pixel(&input, x + 1, y + 1); |
| |
| in1 = yuvColorGamutConversion(in1, conversion_coeffs.at(coeffs_idx)); |
| in2 = yuvColorGamutConversion(in2, conversion_coeffs.at(coeffs_idx)); |
| in3 = yuvColorGamutConversion(in3, conversion_coeffs.at(coeffs_idx)); |
| in4 = yuvColorGamutConversion(in4, conversion_coeffs.at(coeffs_idx)); |
| |
| // Clamp and reduce to uint8_t from float. |
| uint8_t expect_y1 = static_cast<uint8_t>(CLIP3((in1.y * 255.0f + 0.5f), 0, 255)); |
| uint8_t expect_y2 = static_cast<uint8_t>(CLIP3((in2.y * 255.0f + 0.5f), 0, 255)); |
| uint8_t expect_y3 = static_cast<uint8_t>(CLIP3((in3.y * 255.0f + 0.5f), 0, 255)); |
| uint8_t expect_y4 = static_cast<uint8_t>(CLIP3((in4.y * 255.0f + 0.5f), 0, 255)); |
| |
| // Allow an absolute difference of 1 to allow for implmentations using a fixed-point |
| // approximation. |
| EXPECT_NEAR(expect_y1, out1.y, 1); |
| EXPECT_NEAR(expect_y2, out2.y, 1); |
| EXPECT_NEAR(expect_y3, out3.y, 1); |
| EXPECT_NEAR(expect_y4, out4.y, 1); |
| |
| Color expect_uv = (in1 + in2 + in3 + in4) / 4.0f; |
| |
| uint8_t expect_u = |
| static_cast<uint8_t>(CLIP3((expect_uv.u * 255.0f + 128.0f + 0.5f), 0, 255)); |
| uint8_t expect_v = |
| static_cast<uint8_t>(CLIP3((expect_uv.v * 255.0f + 128.0f + 0.5f), 0, 255)); |
| |
| EXPECT_NEAR(expect_u, out1.u, 1); |
| EXPECT_NEAR(expect_u, out2.u, 1); |
| EXPECT_NEAR(expect_u, out3.u, 1); |
| EXPECT_NEAR(expect_u, out4.u, 1); |
| |
| EXPECT_NEAR(expect_v, out1.v, 1); |
| EXPECT_NEAR(expect_v, out2.v, 1); |
| EXPECT_NEAR(expect_v, out3.v, 1); |
| EXPECT_NEAR(expect_v, out4.v, 1); |
| } |
| } |
| } |
| } |
| |
| #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON))) |
| TEST_F(GainMapMathTest, TransformYuv420Neon) { |
| const std::array<std::pair<const int16_t*, const std::array<float, 9>>, 6> fixed_floating_coeffs{ |
| {{kYuv709To601_coeffs_neon, kYuvBt709ToBt601}, |
| {kYuv709To2100_coeffs_neon, kYuvBt709ToBt2100}, |
| {kYuv601To709_coeffs_neon, kYuvBt601ToBt709}, |
| {kYuv601To2100_coeffs_neon, kYuvBt601ToBt2100}, |
| {kYuv2100To709_coeffs_neon, kYuvBt2100ToBt709}, |
| {kYuv2100To601_coeffs_neon, kYuvBt2100ToBt601}}}; |
| |
| for (const auto& [neon_coeffs_ptr, floating_point_coeffs] : fixed_floating_coeffs) { |
| uhdr_raw_image_t input = Yuv420Image32x4(); |
| const size_t buf_size = input.w * input.h * 3 / 2; |
| std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(buf_size); |
| uint8_t* luma = out_buf.get(); |
| uint8_t* cb = luma + input.w * input.h; |
| uint8_t* cr = cb + input.w * input.h / 4; |
| |
| uhdr_raw_image_t output = Yuv420Image32x4(); |
| memcpy(luma, input.planes[UHDR_PLANE_Y], input.w * input.h); |
| memcpy(cb, input.planes[UHDR_PLANE_U], input.w * input.h / 4); |
| memcpy(cr, input.planes[UHDR_PLANE_V], input.w * input.h / 4); |
| output.planes[UHDR_PLANE_Y] = luma; |
| output.planes[UHDR_PLANE_U] = cb; |
| output.planes[UHDR_PLANE_V] = cr; |
| |
| transformYuv420_neon(&output, neon_coeffs_ptr); |
| |
| for (size_t y = 0; y < input.h / 2; ++y) { |
| for (size_t x = 0; x < input.w / 2; ++x) { |
| const Pixel out1 = getYuv420Pixel_uint(&output, x * 2, y * 2); |
| const Pixel out2 = getYuv420Pixel_uint(&output, x * 2 + 1, y * 2); |
| const Pixel out3 = getYuv420Pixel_uint(&output, x * 2, y * 2 + 1); |
| const Pixel out4 = getYuv420Pixel_uint(&output, x * 2 + 1, y * 2 + 1); |
| |
| Color in1 = getYuv420Pixel(&input, x * 2, y * 2); |
| Color in2 = getYuv420Pixel(&input, x * 2 + 1, y * 2); |
| Color in3 = getYuv420Pixel(&input, x * 2, y * 2 + 1); |
| Color in4 = getYuv420Pixel(&input, x * 2 + 1, y * 2 + 1); |
| |
| in1 = yuvColorGamutConversion(in1, floating_point_coeffs); |
| in2 = yuvColorGamutConversion(in2, floating_point_coeffs); |
| in3 = yuvColorGamutConversion(in3, floating_point_coeffs); |
| in4 = yuvColorGamutConversion(in4, floating_point_coeffs); |
| |
| const Color expect_uv = (in1 + in2 + in3 + in4) / 4.0f; |
| |
| const uint8_t expect_y1 = static_cast<uint8_t>(CLIP3(in1.y * 255.0f + 0.5f, 0, 255)); |
| const uint8_t expect_y2 = static_cast<uint8_t>(CLIP3(in2.y * 255.0f + 0.5f, 0, 255)); |
| const uint8_t expect_y3 = static_cast<uint8_t>(CLIP3(in3.y * 255.0f + 0.5f, 0, 255)); |
| const uint8_t expect_y4 = static_cast<uint8_t>(CLIP3(in4.y * 255.0f + 0.5f, 0, 255)); |
| |
| const uint8_t expect_u = |
| static_cast<uint8_t>(CLIP3(expect_uv.u * 255.0f + 128.0f + 0.5f, 0, 255)); |
| const uint8_t expect_v = |
| static_cast<uint8_t>(CLIP3(expect_uv.v * 255.0f + 128.0f + 0.5f, 0, 255)); |
| |
| // Due to the Neon version using a fixed-point approximation, this can result in an off by |
| // one error compared with the standard floating-point version. |
| EXPECT_NEAR(expect_y1, out1.y, 1); |
| EXPECT_NEAR(expect_y2, out2.y, 1); |
| EXPECT_NEAR(expect_y3, out3.y, 1); |
| EXPECT_NEAR(expect_y4, out4.y, 1); |
| |
| EXPECT_NEAR(expect_u, out1.u, 1); |
| EXPECT_NEAR(expect_u, out2.u, 1); |
| EXPECT_NEAR(expect_u, out3.u, 1); |
| EXPECT_NEAR(expect_u, out4.u, 1); |
| |
| EXPECT_NEAR(expect_v, out1.v, 1); |
| EXPECT_NEAR(expect_v, out2.v, 1); |
| EXPECT_NEAR(expect_v, out3.v, 1); |
| EXPECT_NEAR(expect_v, out4.v, 1); |
| } |
| } |
| } |
| } |
| #endif |
| |
| TEST_F(GainMapMathTest, HlgOetf) { |
| EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f); |
| EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f); |
| |
| Color e = {{{0.04167f, 0.08333f, 0.5f}}}; |
| Color e_gamma = {{{0.35357f, 0.5f, 0.87164f}}}; |
| EXPECT_RGB_NEAR(hlgOetf(e), e_gamma); |
| } |
| |
| TEST_F(GainMapMathTest, HlgInvOetf) { |
| EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f); |
| EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f); |
| |
| Color e_gamma = {{{0.25f, 0.5f, 0.75f}}}; |
| Color e = {{{0.02083f, 0.08333f, 0.26496f}}}; |
| EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e); |
| } |
| |
| TEST_F(GainMapMathTest, HlgTransferFunctionRoundtrip) { |
| EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f); |
| EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon()); |
| EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f); |
| } |
| |
| TEST_F(GainMapMathTest, PqOetf) { |
| EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f); |
| EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f); |
| |
| Color e = {{{0.01f, 0.5f, 0.99f}}}; |
| Color e_gamma = {{{0.50808f, 0.92655f, 0.99895f}}}; |
| EXPECT_RGB_NEAR(pqOetf(e), e_gamma); |
| } |
| |
| TEST_F(GainMapMathTest, PqInvOetf) { |
| EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f); |
| EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f); |
| |
| Color e_gamma = {{{0.01f, 0.5f, 0.99f}}}; |
| Color e = {{{2.31017e-7f, 0.00922f, 0.90903f}}}; |
| EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e); |
| } |
| |
| TEST_F(GainMapMathTest, PqInvOetfLUT) { |
| for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1); |
| EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value)); |
| } |
| } |
| |
| TEST_F(GainMapMathTest, HlgInvOetfLUT) { |
| for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1); |
| EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value)); |
| } |
| } |
| |
| TEST_F(GainMapMathTest, pqOetfLUT) { |
| for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1); |
| EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value)); |
| } |
| } |
| |
| TEST_F(GainMapMathTest, hlgOetfLUT) { |
| for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1); |
| EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value)); |
| } |
| } |
| |
| TEST_F(GainMapMathTest, srgbInvOetfLUT) { |
| for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1); |
| EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value)); |
| } |
| } |
| |
| TEST_F(GainMapMathTest, applyGainLUT) { |
| for (float boost = 1.5; boost <= 12; boost++) { |
| uhdr_gainmap_metadata_ext_t metadata; |
| |
| metadata.min_content_boost = 1.0f / boost; |
| metadata.max_content_boost = boost; |
| metadata.gamma = 1.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| GainLUT gainLUT(&metadata); |
| float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) / |
| (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min)); |
| weight = CLIP3(weight, 0.0f, 1.0f); |
| GainLUT gainLUTWithBoost(&metadata, weight); |
| for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata), |
| applyGainLUT(RgbBlack(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata), |
| applyGainLUT(RgbWhite(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata), |
| applyGainLUT(RgbRed(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata), |
| applyGainLUT(RgbGreen(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata), |
| applyGainLUT(RgbBlue(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight), |
| applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight), |
| applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight), |
| applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight), |
| applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight), |
| applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata)); |
| } |
| } |
| |
| for (float boost = 1.5; boost <= 12; boost++) { |
| uhdr_gainmap_metadata_ext_t metadata; |
| |
| metadata.min_content_boost = 1.0f; |
| metadata.max_content_boost = boost; |
| metadata.gamma = 1.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| GainLUT gainLUT(&metadata); |
| float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) / |
| (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min)); |
| weight = CLIP3(weight, 0.0f, 1.0f); |
| GainLUT gainLUTWithBoost(&metadata, weight); |
| for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata), |
| applyGainLUT(RgbBlack(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata), |
| applyGainLUT(RgbWhite(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata), |
| applyGainLUT(RgbRed(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata), |
| applyGainLUT(RgbGreen(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata), |
| applyGainLUT(RgbBlue(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight), |
| applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight), |
| applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight), |
| applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight), |
| applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight), |
| applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata)); |
| } |
| } |
| |
| for (float boost = 1.5; boost <= 12; boost++) { |
| uhdr_gainmap_metadata_ext_t metadata; |
| |
| metadata.min_content_boost = 1.0f / powf(boost, 1.0f / 3.0f); |
| metadata.max_content_boost = boost; |
| metadata.gamma = 1.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| GainLUT gainLUT(&metadata); |
| float weight = (log2(boost) - log2(metadata.hdr_capacity_min)) / |
| (log2(metadata.hdr_capacity_max) - log2(metadata.hdr_capacity_min)); |
| weight = CLIP3(weight, 0.0f, 1.0f); |
| GainLUT gainLUTWithBoost(&metadata, weight); |
| for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) { |
| float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata), |
| applyGainLUT(RgbBlack(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata), |
| applyGainLUT(RgbWhite(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata), |
| applyGainLUT(RgbRed(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata), |
| applyGainLUT(RgbGreen(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata), |
| applyGainLUT(RgbBlue(), value, gainLUT, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata, weight), |
| applyGainLUT(RgbBlack(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata, weight), |
| applyGainLUT(RgbWhite(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata, weight), |
| applyGainLUT(RgbRed(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata, weight), |
| applyGainLUT(RgbGreen(), value, gainLUTWithBoost, &metadata)); |
| EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata, weight), |
| applyGainLUT(RgbBlue(), value, gainLUTWithBoost, &metadata)); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, PqTransferFunctionRoundtrip) { |
| EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f); |
| EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon()); |
| EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon()); |
| EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f); |
| } |
| |
| TEST_F(GainMapMathTest, ColorConversionLookup) { |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_UNSPECIFIED), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_BT_709), identityConversion); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_DISPLAY_P3), p3ToBt709); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_709, UHDR_CG_BT_2100), bt2100ToBt709); |
| |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_UNSPECIFIED), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_BT_709), bt709ToP3); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_DISPLAY_P3), identityConversion); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_DISPLAY_P3, UHDR_CG_BT_2100), bt2100ToP3); |
| |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_UNSPECIFIED), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_BT_709), bt709ToBt2100); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3), p3ToBt2100); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_BT_2100, UHDR_CG_BT_2100), identityConversion); |
| |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_UNSPECIFIED), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_BT_709), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_DISPLAY_P3), nullptr); |
| EXPECT_EQ(getGamutConversionFn(UHDR_CG_UNSPECIFIED, UHDR_CG_BT_2100), nullptr); |
| } |
| |
| TEST_F(GainMapMathTest, EncodeGain) { |
| float min_boost = log2(1.0f / 4.0f); |
| float max_boost = log2(4.0f); |
| float gamma = 1.0f; |
| |
| EXPECT_EQ(affineMapGain(computeGain(0.0f, 1.0f), min_boost, max_boost, 1.0f), 128); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(0.5f, 0.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0), min_boost, max_boost, 1.0f), 128); |
| |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 5.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(4.0f, 1.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(4.0f, 0.5f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 191); |
| EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 64); |
| |
| min_boost = log2(1.0f / 2.0f); |
| max_boost = log2(2.0f); |
| |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(2.0f, 1.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.41421f), min_boost, max_boost, 1.0f), 191); |
| EXPECT_EQ(affineMapGain(computeGain(1.41421f, 1.0f), min_boost, max_boost, 1.0f), 64); |
| |
| min_boost = log2(1.0f / 8.0f); |
| max_boost = log2(8.0f); |
| |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(8.0f, 1.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.82843f), min_boost, max_boost, 1.0f), 191); |
| EXPECT_EQ(affineMapGain(computeGain(2.82843f, 1.0f), min_boost, max_boost, 1.0f), 64); |
| |
| min_boost = log2(1.0f); |
| max_boost = log2(8.0f); |
| |
| EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 170); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 85); |
| |
| min_boost = log2(1.0f / 2.0f); |
| max_boost = log2(8.0f); |
| |
| EXPECT_EQ(affineMapGain(computeGain(0.0f, 0.0f), min_boost, max_boost, 1.0f), 64); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 64); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 191); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 128); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.7071f), min_boost, max_boost, 1.0f), 32); |
| EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.5f), min_boost, max_boost, 1.0f), 0); |
| } |
| |
| TEST_F(GainMapMathTest, ApplyGain) { |
| uhdr_gainmap_metadata_ext_t metadata; |
| |
| metadata.min_content_boost = 1.0f / 4.0f; |
| metadata.max_content_boost = 4.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| metadata.offset_sdr = 0.0f; |
| metadata.offset_hdr = 0.0f; |
| metadata.gamma = 1.0f; |
| |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack()); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.5f, &metadata), RgbBlack()); |
| EXPECT_RGB_NEAR(applyGain(RgbBlack(), 1.0f, &metadata), RgbBlack()); |
| |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite()); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f); |
| |
| metadata.max_content_boost = 2.0f; |
| metadata.min_content_boost = 1.0f / 2.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite()); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f); |
| |
| metadata.max_content_boost = 8.0f; |
| metadata.min_content_boost = 1.0f / 8.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite()); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| |
| metadata.max_content_boost = 8.0f; |
| metadata.min_content_boost = 1.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite()); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| |
| metadata.max_content_boost = 8.0f; |
| metadata.min_content_boost = 0.5f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite()); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f); |
| EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| |
| Color e = {{{0.0f, 0.5f, 1.0f}}}; |
| metadata.max_content_boost = 4.0f; |
| metadata.min_content_boost = 1.0f / 4.0f; |
| metadata.hdr_capacity_max = metadata.max_content_boost; |
| metadata.hdr_capacity_min = metadata.min_content_boost; |
| |
| EXPECT_RGB_NEAR(applyGain(e, 0.0f, &metadata), e / 4.0f); |
| EXPECT_RGB_NEAR(applyGain(e, 0.25f, &metadata), e / 2.0f); |
| EXPECT_RGB_NEAR(applyGain(e, 0.5f, &metadata), e); |
| EXPECT_RGB_NEAR(applyGain(e, 0.75f, &metadata), e * 2.0f); |
| EXPECT_RGB_NEAR(applyGain(e, 1.0f, &metadata), e * 4.0f); |
| } |
| |
| TEST_F(GainMapMathTest, GetYuv420Pixel) { |
| auto image = Yuv420Image(); |
| Color(*colors)[4] = Yuv420Colors(); |
| |
| for (size_t y = 0; y < 4; ++y) { |
| for (size_t x = 0; x < 4; ++x) { |
| EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, GetP010Pixel) { |
| auto image = P010Image(); |
| Color(*colors)[4] = P010Colors(); |
| |
| for (size_t y = 0; y < 4; ++y) { |
| for (size_t x = 0; x < 4; ++x) { |
| EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, SampleYuv420) { |
| auto image = Yuv420Image(); |
| Color(*colors)[4] = Yuv420Colors(); |
| |
| static const size_t kMapScaleFactor = 2; |
| for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) { |
| for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) { |
| Color min = {{{1.0f, 1.0f, 1.0f}}}; |
| Color max = {{{-1.0f, -1.0f, -1.0f}}}; |
| |
| for (size_t dy = 0; dy < kMapScaleFactor; ++dy) { |
| for (size_t dx = 0; dx < kMapScaleFactor; ++dx) { |
| Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx]; |
| min = ColorMin(min, e); |
| max = ColorMax(max, e); |
| } |
| } |
| |
| // Instead of reimplementing the sampling algorithm, confirm that the |
| // sample output is within the range of the min and max of the nearest |
| // points. |
| EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, SampleP010) { |
| auto image = P010Image(); |
| Color(*colors)[4] = P010Colors(); |
| |
| static const size_t kMapScaleFactor = 2; |
| for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) { |
| for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) { |
| Color min = {{{1.0f, 1.0f, 1.0f}}}; |
| Color max = {{{-1.0f, -1.0f, -1.0f}}}; |
| |
| for (size_t dy = 0; dy < kMapScaleFactor; ++dy) { |
| for (size_t dx = 0; dx < kMapScaleFactor; ++dx) { |
| Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx]; |
| min = ColorMin(min, e); |
| max = ColorMax(max, e); |
| } |
| } |
| |
| // Instead of reimplementing the sampling algorithm, confirm that the |
| // sample output is within the range of the min and max of the nearest |
| // points. |
| EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, SampleMap) { |
| auto image = MapImage(); |
| float(*values)[4] = MapValues(); |
| |
| static const size_t kMapScaleFactor = 2; |
| ShepardsIDW idwTable(kMapScaleFactor); |
| for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) { |
| for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) { |
| size_t x_base = x / kMapScaleFactor; |
| size_t y_base = y / kMapScaleFactor; |
| |
| float min = 1.0f; |
| float max = -1.0f; |
| |
| min = fmin(min, values[y_base][x_base]); |
| max = fmax(max, values[y_base][x_base]); |
| if (y_base + 1 < 4) { |
| min = fmin(min, values[y_base + 1][x_base]); |
| max = fmax(max, values[y_base + 1][x_base]); |
| } |
| if (x_base + 1 < 4) { |
| min = fmin(min, values[y_base][x_base + 1]); |
| max = fmax(max, values[y_base][x_base + 1]); |
| } |
| if (y_base + 1 < 4 && x_base + 1 < 4) { |
| min = fmin(min, values[y_base + 1][x_base + 1]); |
| max = fmax(max, values[y_base + 1][x_base + 1]); |
| } |
| |
| // Instead of reimplementing the sampling algorithm, confirm that the |
| // sample output is within the range of the min and max of the nearest |
| // points. |
| EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y), |
| testing::AllOf(testing::Ge(min), testing::Le(max))); |
| EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable), |
| sampleMap(&image, kMapScaleFactor, x, y)); |
| } |
| } |
| } |
| |
| TEST_F(GainMapMathTest, ColorToRgba1010102) { |
| EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30); |
| EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF); |
| EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff); |
| EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10); |
| EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20); |
| |
| Color e_gamma = {{{0.1f, 0.2f, 0.3f}}}; |
| EXPECT_EQ(colorToRgba1010102(e_gamma), |
| 0x3 << 30 | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff) + 0.5) | |
| static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff) + 0.5) << 10 | |
| static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff) + 0.5) << 20); |
| } |
| |
| TEST_F(GainMapMathTest, ColorToRgbaF16) { |
| EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t)0x3C00) << 48); |
| EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00); |
| EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t)0x3C00) << 48) | ((uint64_t)0x3C00)); |
| EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 16)); |
| EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 32)); |
| |
| Color e_gamma = {{{0.1f, 0.2f, 0.3f}}}; |
| EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66); |
| } |
| |
| TEST_F(GainMapMathTest, Float32ToFloat16) { |
| EXPECT_EQ(floatToHalf(0.1f), 0x2E66); |
| EXPECT_EQ(floatToHalf(0.0f), 0x0); |
| EXPECT_EQ(floatToHalf(1.0f), 0x3C00); |
| EXPECT_EQ(floatToHalf(-1.0f), 0xBC00); |
| EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max |
| EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF); // float min |
| EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero |
| } |
| |
| TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) { |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance), 0.0f); |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance), kSdrWhiteNits); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance), |
| srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance), |
| srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance), |
| srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); |
| } |
| |
| TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) { |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance), 0.0f); |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance), kSdrWhiteNits); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance), p3Luminance(RgbRed()) * kSdrWhiteNits, |
| LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance), |
| p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance), |
| p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); |
| } |
| |
| TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) { |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance), 0.0f); |
| EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance), kSdrWhiteNits); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance), |
| bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance), |
| bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon()); |
| EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance), |
| bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon()); |
| } |
| |
| TEST_F(GainMapMathTest, GenerateMapLuminanceHlg) { |
| EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion, bt2100Luminance, |
| kHlgMaxNits), |
| 0.0f); |
| EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion, bt2100Luminance, |
| kHlgMaxNits), |
| kHlgMaxNits); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion, bt2100Luminance, |
| kHlgMaxNits), |
| bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon()); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion, |
| bt2100Luminance, kHlgMaxNits), |
| bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon()); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion, bt2100Luminance, |
| kHlgMaxNits), |
| bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon()); |
| } |
| |
| TEST_F(GainMapMathTest, GenerateMapLuminancePq) { |
| EXPECT_FLOAT_EQ( |
| Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits), |
| 0.0f); |
| EXPECT_FLOAT_EQ( |
| Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits), |
| kPqMaxNits); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion, bt2100Luminance, |
| kPqMaxNits), |
| bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon()); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion, bt2100Luminance, |
| kPqMaxNits), |
| bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon()); |
| EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion, bt2100Luminance, |
| kPqMaxNits), |
| bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon()); |
| } |
| |
| TEST_F(GainMapMathTest, ApplyMap) { |
| uhdr_gainmap_metadata_ext_t metadata; |
| |
| metadata.min_content_boost = 1.0f / 8.0f; |
| metadata.max_content_boost = 8.0f; |
| metadata.offset_sdr = 0.0f; |
| metadata.offset_hdr = 0.0f; |
| metadata.gamma = 1.0f; |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata), RgbBlack()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata), RgbRed() * 8.0f); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata), RgbGreen() * 8.0f); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata), RgbBlue() * 8.0f); |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata), RgbWhite() * sqrt(8.0f)); |
| EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata), RgbBlack()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata), RgbRed() * sqrt(8.0f)); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata), RgbGreen() * sqrt(8.0f)); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata), RgbBlue() * sqrt(8.0f)); |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite()); |
| EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata), RgbBlack()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata), RgbRed()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata), RgbGreen()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata), RgbBlue()); |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite() / sqrt(8.0f)); |
| EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata), RgbBlack()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata), RgbRed() / sqrt(8.0f)); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata), RgbGreen() / sqrt(8.0f)); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata), RgbBlue() / sqrt(8.0f)); |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); |
| EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata), RgbBlack()); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata), RgbRed() / 8.0f); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata), RgbGreen() / 8.0f); |
| EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata), RgbBlue() / 8.0f); |
| |
| metadata.max_content_boost = 8.0f; |
| metadata.min_content_boost = 1.0f; |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite()); |
| |
| metadata.max_content_boost = 8.0f; |
| metadata.min_content_boost = 0.5f; |
| |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), RgbWhite() * 4.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite() * 2.0f); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite()); |
| EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); |
| } |
| |
| } // namespace ultrahdr |