diff --git a/lib/src/dsp/arm/gainmapmath_neon.cpp b/lib/src/dsp/arm/gainmapmath_neon.cpp index 68a84940..dc419165 100644 --- a/lib/src/dsp/arm/gainmapmath_neon.cpp +++ b/lib/src/dsp/arm/gainmapmath_neon.cpp @@ -324,12 +324,12 @@ uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t sr ALIGNED(16) const uint16_t kRgb709ToYuv_coeffs_neon[8] = {3484, 11717, 1183, 1877, 6315, 8192, 7441, 751}; -// RGB Display P3 -> Yuv Display P3 -// Y = 0.2289746 * R + 0.6917385 * G + 0.0792869 * B -// U = -0.124346335 * R + -0.375653665 * G + 0.5 * B -// V = 0.5 * R + -0.448583471 * G + -0.051416529 * B +// RGB Display P3 -> YUV using BT.601 luma coefficients, matching p3RgbToYuv(). +// Y = 0.299 * R + 0.587 * G + 0.114 * B +// U = -0.168735892 * R + -0.331264108 * G + 0.5 * B +// V = 0.5 * R + -0.418687589 * G + -0.081312411 * B ALIGNED(16) -const uint16_t kRgbDispP3ToYuv_coeffs_neon[8] = {3752, 11333, 1299, 2037, 6155, 8192, 7350, 842}; +const uint16_t kRgb601ToYuv_coeffs_neon[8] = {4899, 9617, 1868, 2765, 5427, 8192, 6860, 1332}; // RGB Bt2100 -> Yuv Bt2100 // Y = 0.2627 * R + 0.677998 * G + 0.059302 * B @@ -456,9 +456,9 @@ std::unique_ptr convert_raw_input_to_ycbcr_neon(uhdr_raw_i if (src->cg == UHDR_CG_BT_709) { coeffs_ptr = kRgb709ToYuv_coeffs_neon; } else if (src->cg == UHDR_CG_BT_2100) { - coeffs_ptr = kRgbDispP3ToYuv_coeffs_neon; - } else if (src->cg == UHDR_CG_DISPLAY_P3) { coeffs_ptr = kRgb2100ToYuv_coeffs_neon; + } else if (src->cg == UHDR_CG_DISPLAY_P3) { + coeffs_ptr = kRgb601ToYuv_coeffs_neon; } else { return dst; } diff --git a/tests/gainmapmath_test.cpp b/tests/gainmapmath_test.cpp index 570742db..4e1ef5a8 100644 --- a/tests/gainmapmath_test.cpp +++ b/tests/gainmapmath_test.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include "ultrahdr/gainmapmath.h" #ifdef UHDR_ENABLE_SMPTE2094_50 #include "smpte2094_50/smpte2094_50.h" @@ -875,6 +877,49 @@ TEST_F(GainMapMathTest, YuvConversionNeon) { EXPECT_NEAR(result7.v, expected_values.v.at(7), 1); } } + +TEST_F(GainMapMathTest, Rgba8888ToYuv444NeonMatchesScalarForWideGamuts) { + constexpr size_t kWidth = 16; + constexpr size_t kHeight = 1; + const std::array, 4> colors{{ + {{255, 0, 0, 255}}, + {{0, 255, 0, 255}}, + {{0, 0, 255, 255}}, + {{37, 149, 233, 255}}, + }}; + + std::vector rgba(kWidth * kHeight * 4); + for (size_t x = 0; x < kWidth; ++x) { + std::copy(colors[x % colors.size()].begin(), colors[x % colors.size()].end(), + rgba.begin() + x * 4); + } + + for (uhdr_color_gamut_t gamut : {UHDR_CG_DISPLAY_P3, UHDR_CG_BT_2100}) { + uhdr_raw_image_t source{}; + source.fmt = UHDR_IMG_FMT_32bppRGBA8888; + source.cg = gamut; + source.ct = UHDR_CT_SRGB; + source.range = UHDR_CR_FULL_RANGE; + source.w = kWidth; + source.h = kHeight; + source.planes[UHDR_PLANE_PACKED] = rgba.data(); + source.stride[UHDR_PLANE_PACKED] = kWidth; + + auto scalar = convert_raw_input_to_ycbcr(&source); + auto neon = convert_raw_input_to_ycbcr_neon(&source); + ASSERT_NE(scalar, nullptr); + ASSERT_NE(neon, nullptr); + + for (auto plane : {UHDR_PLANE_Y, UHDR_PLANE_U, UHDR_PLANE_V}) { + const auto* scalar_data = static_cast(scalar->planes[plane]); + const auto* neon_data = static_cast(neon->planes[plane]); + for (size_t x = 0; x < kWidth; ++x) { + EXPECT_NEAR(scalar_data[x], neon_data[x], 1) + << "gamut=" << gamut << ", plane=" << plane << ", x=" << x; + } + } + } +} #endif TEST_F(GainMapMathTest, TransformYuv420) {