From fd742471ad20b886675c65422e0716b55cfd50a2 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 11 Apr 2019 16:57:39 +0000 Subject: [PATCH] Bug 1493898 - P4. Add BT2020 YUV->RGB conversion. r=jgilbert This is used by the basic compositor. Re-using existing logic, however as with other conversion it only handles limited 8 bits ranges (16-235) and to make things worse is rounded aggressively as the focus is on speed. Differential Revision: https://phabricator.services.mozilla.com/D25345 --HG-- extra : moz-landing-system : lando --- gfx/ycbcr/scale_yuv_argb.cpp | 17 ++- gfx/ycbcr/yuv_convert.cpp | 142 +++++++++--------- .../libyuv/include/libyuv/convert_argb.h | 38 +++++ media/libyuv/libyuv/include/libyuv/row.h | 2 + media/libyuv/libyuv/source/convert_argb.cc | 51 +++++++ media/libyuv/libyuv/source/row_common.cc | 80 ++++++++++ 6 files changed, 252 insertions(+), 78 deletions(-) diff --git a/gfx/ycbcr/scale_yuv_argb.cpp b/gfx/ycbcr/scale_yuv_argb.cpp index 91a96cb9f1ce..59537fce8b1b 100644 --- a/gfx/ycbcr/scale_yuv_argb.cpp +++ b/gfx/ycbcr/scale_yuv_argb.cpp @@ -54,7 +54,7 @@ extern "C" { // -[3] Modified scaling functions as to handle YUV conversion buffer and // use YUVBuferIter. // -[4] Color conversion function selections in YUVBuferIter were borrowed from -// I444ToARGBMatrix(), I422ToARGBMatrix() and I420ToARGBMatrix() +// I444ToARGBMatrix(), I422ToARGBMatrix() and I420ToARGBMatrix() static __inline int Abs(int v) { return v >= 0 ? v : -v; @@ -210,10 +210,15 @@ void YUVBuferIter_Init(YUVBuferIter& iter, uint32 src_fourcc, mozilla::YUVColorS iter.src_row_y = iter.src_y; iter.src_row_u = iter.src_u; iter.src_row_v = iter.src_v; - if (yuv_color_space == mozilla::YUVColorSpace::BT709) { - iter.yuvconstants = &kYuvH709Constants; - } else { - iter.yuvconstants = &kYuvI601Constants; + switch (yuv_color_space) { + case mozilla::YUVColorSpace::BT2020: + iter.yuvconstants = &kYuv2020Constants; + break; + case mozilla::YUVColorSpace::BT709: + iter.yuvconstants = &kYuvH709Constants; + break; + default: + iter.yuvconstants = &kYuvI601Constants; } if (src_fourcc == FOURCC_I444) { @@ -531,7 +536,7 @@ static void ScaleYUVToARGBBilinearDown(int src_width, int src_height, // Allocate 2 row of ARGB for source conversion. const int kRowSize = (src_width * 4 + 15) & ~15; align_buffer_64(argb_cnv_row, kRowSize * 2); - uint8* argb_cnv_rowptr = argb_cnv_row; + uint8* argb_cnv_rowptr = argb_cnv_row; int argb_cnv_rowstride = kRowSize; #if defined(HAS_INTERPOLATEROW_SSSE3) diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp index 159e1c0ac9b5..fb6062595c5c 100644 --- a/gfx/ycbcr/yuv_convert.cpp +++ b/gfx/ycbcr/yuv_convert.cpp @@ -64,26 +64,16 @@ libyuv::FourCC FourCCFromYUVType(YUVType aYUVType) } // Convert a frame of YUV to 32 bit ARGB. -void ConvertYCbCrToRGB32(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int pic_x, - int pic_y, - int pic_width, - int pic_height, - int y_pitch, - int uv_pitch, - int rgb_pitch, - YUVType yuv_type, +void ConvertYCbCrToRGB32(const uint8* y_buf, const uint8* u_buf, + const uint8* v_buf, uint8* rgb_buf, int pic_x, + int pic_y, int pic_width, int pic_height, int y_pitch, + int uv_pitch, int rgb_pitch, YUVType yuv_type, YUVColorSpace yuv_color_space) { - - // Deprecated function's conversion is accurate. // libyuv converion is a bit inaccurate to get performance. It dynamically - // calculates RGB from YUV to use simd. In it, signed byte is used for conversion's - // coefficient, but it requests 129. libyuv cut 129 to 127. And only 6 bits are - // used for a decimal part during the dynamic calculation. + // calculates RGB from YUV to use simd. In it, signed byte is used for + // conversion's coefficient, but it requests 129. libyuv cut 129 to 127. And + // only 6 bits are used for a decimal part during the dynamic calculation. // // The function is still fast on some old intel chips. // See Bug 1256475. @@ -96,69 +86,77 @@ void ConvertYCbCrToRGB32(const uint8* y_buf, use_deprecated = false; } if (use_deprecated) { - ConvertYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf, rgb_buf, - pic_x, pic_y, pic_width, pic_height, - y_pitch, uv_pitch, rgb_pitch, yuv_type); + ConvertYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf, rgb_buf, pic_x, pic_y, + pic_width, pic_height, y_pitch, uv_pitch, + rgb_pitch, yuv_type); return; } - if (yuv_type == YV24) { - const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; - const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x; - const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x; - if (yuv_color_space == mozilla::YUVColorSpace::BT709) { - DebugOnly err = libyuv::H444ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); - MOZ_ASSERT(!err); - } else { - DebugOnly err = libyuv::I444ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); + decltype(libyuv::U444ToARGB)* fConvertYUVToARGB = nullptr; + switch (yuv_type) { + case YV24: { + const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; + const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x; + const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x; + switch (yuv_color_space) { + case mozilla::YUVColorSpace::BT2020: + fConvertYUVToARGB = libyuv::U444ToARGB; + break; + case mozilla::YUVColorSpace::BT709: + fConvertYUVToARGB = libyuv::H444ToARGB; + break; + default: + fConvertYUVToARGB = libyuv::I444ToARGB; + break; + } + DebugOnly err = + fConvertYUVToARGB(src_y, y_pitch, src_u, uv_pitch, src_v, uv_pitch, + rgb_buf, rgb_pitch, pic_width, pic_height); MOZ_ASSERT(!err); + break; } - } else if (yuv_type == YV16) { - const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; - const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x / 2; - const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x / 2; - if (yuv_color_space == mozilla::YUVColorSpace::BT709) { - DebugOnly err = libyuv::H422ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); - MOZ_ASSERT(!err); - } else { - DebugOnly err = libyuv::I422ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); + case YV16: { + const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; + const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x / 2; + const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x / 2; + switch (yuv_color_space) { + case mozilla::YUVColorSpace::BT2020: + fConvertYUVToARGB = libyuv::U422ToARGB; + break; + case mozilla::YUVColorSpace::BT709: + fConvertYUVToARGB = libyuv::H422ToARGB; + break; + default: + fConvertYUVToARGB = libyuv::I422ToARGB; + break; + } + DebugOnly err = + fConvertYUVToARGB(src_y, y_pitch, src_u, uv_pitch, src_v, uv_pitch, + rgb_buf, rgb_pitch, pic_width, pic_height); MOZ_ASSERT(!err); + break; } - } else { - MOZ_ASSERT(yuv_type == YV12); - const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; - const uint8* src_u = u_buf + (uv_pitch * pic_y + pic_x) / 2; - const uint8* src_v = v_buf + (uv_pitch * pic_y + pic_x) / 2; - if (yuv_color_space == mozilla::YUVColorSpace::BT709) { - DebugOnly err = libyuv::H420ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); - MOZ_ASSERT(!err); - } else { - DebugOnly err = libyuv::I420ToARGB(src_y, y_pitch, - src_u, uv_pitch, - src_v, uv_pitch, - rgb_buf, rgb_pitch, - pic_width, pic_height); + default: { + MOZ_ASSERT(yuv_type == YV12); + const uint8* src_y = y_buf + y_pitch * pic_y + pic_x; + const uint8* src_u = u_buf + (uv_pitch * pic_y + pic_x) / 2; + const uint8* src_v = v_buf + (uv_pitch * pic_y + pic_x) / 2; + switch (yuv_color_space) { + case mozilla::YUVColorSpace::BT2020: + fConvertYUVToARGB = libyuv::U420ToARGB; + break; + case mozilla::YUVColorSpace::BT709: + fConvertYUVToARGB = libyuv::H420ToARGB; + break; + default: + fConvertYUVToARGB = libyuv::I420ToARGB; + break; + } + DebugOnly err = + fConvertYUVToARGB(src_y, y_pitch, src_u, uv_pitch, src_v, uv_pitch, + rgb_buf, rgb_pitch, pic_width, pic_height); MOZ_ASSERT(!err); + break; } } } diff --git a/media/libyuv/libyuv/include/libyuv/convert_argb.h b/media/libyuv/libyuv/include/libyuv/convert_argb.h index e811e3e4ac8a..7e5bd2efb27a 100644 --- a/media/libyuv/libyuv/include/libyuv/convert_argb.h +++ b/media/libyuv/libyuv/include/libyuv/convert_argb.h @@ -166,6 +166,18 @@ int H444ToARGB(const uint8_t* src_y, int width, int height); +LIBYUV_API +int U444ToARGB(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height); + // Convert J444 to ARGB. LIBYUV_API int J444ToARGB(const uint8_t* src_y, @@ -402,6 +414,19 @@ int H420ToARGB(const uint8_t* src_y, int width, int height); +// Convert U420 to ARGB. +LIBYUV_API +int U420ToARGB(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height); + // Convert H422 to ARGB. LIBYUV_API int H422ToARGB(const uint8_t* src_y, @@ -415,6 +440,19 @@ int H422ToARGB(const uint8_t* src_y, int width, int height); +// Convert U422 to ARGB. +LIBYUV_API +int U422ToARGB(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height); + // Convert H420 to ABGR. LIBYUV_API int H420ToABGR(const uint8_t* src_y, diff --git a/media/libyuv/libyuv/include/libyuv/row.h b/media/libyuv/libyuv/include/libyuv/row.h index 8076f889e391..b1619bfa5bab 100644 --- a/media/libyuv/libyuv/include/libyuv/row.h +++ b/media/libyuv/libyuv/include/libyuv/row.h @@ -558,11 +558,13 @@ struct YuvConstants { extern const struct YuvConstants SIMD_ALIGNED(kYuvI601Constants); // BT.601 extern const struct YuvConstants SIMD_ALIGNED(kYuvJPEGConstants); // JPeg extern const struct YuvConstants SIMD_ALIGNED(kYuvH709Constants); // BT.709 +extern const struct YuvConstants SIMD_ALIGNED(kYuv2020Constants); // BT.2020 // Conversion matrix for YVU to BGR extern const struct YuvConstants SIMD_ALIGNED(kYvuI601Constants); // BT.601 extern const struct YuvConstants SIMD_ALIGNED(kYvuJPEGConstants); // JPeg extern const struct YuvConstants SIMD_ALIGNED(kYvuH709Constants); // BT.709 +extern const struct YuvConstants SIMD_ALIGNED(kYvu2020Constants); // BT.2020 #define IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a)-1))) diff --git a/media/libyuv/libyuv/source/convert_argb.cc b/media/libyuv/libyuv/source/convert_argb.cc index cbc79d739ed7..967f3d1cbd7b 100644 --- a/media/libyuv/libyuv/source/convert_argb.cc +++ b/media/libyuv/libyuv/source/convert_argb.cc @@ -226,6 +226,23 @@ int H420ToABGR(const uint8_t* src_y, width, height); } +// Convert U420 to ARGB. +LIBYUV_API +int U420ToARGB(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height) { + return I420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_argb, dst_stride_argb, + &kYuv2020Constants, width, height); +} + // Convert I422 to ARGB with matrix static int I422ToARGBMatrix(const uint8_t* src_y, int src_stride_y, @@ -660,6 +677,23 @@ int H010ToABGR(const uint16_t* src_y, width, height); } +// Convert U422 to ARGB. +LIBYUV_API +int U422ToARGB(const uint8* src_y, + int src_stride_y, + const uint8* src_u, + int src_stride_u, + const uint8* src_v, + int src_stride_v, + uint8* dst_argb, + int dst_stride_argb, + int width, + int height) { + return I422ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_argb, dst_stride_argb, + &kYuv2020Constants, width, height); +} + // Convert I444 to ARGB with matrix static int I444ToARGBMatrix(const uint8_t* src_y, int src_stride_y, @@ -770,6 +804,23 @@ int H444ToARGB(const uint8_t* src_y, &kYuvH709Constants, width, height); } +// Convert U444 to ARGB. +LIBYUV_API +int U444ToARGB(const uint8* src_y, + int src_stride_y, + const uint8* src_u, + int src_stride_u, + const uint8* src_v, + int src_stride_v, + uint8* dst_argb, + int dst_stride_argb, + int width, + int height) { + return I444ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_argb, dst_stride_argb, + &kYuv2020Constants, width, height); +} + // Convert I444 to ABGR. LIBYUV_API int I444ToABGR(const uint8_t* src_y, diff --git a/media/libyuv/libyuv/source/row_common.cc b/media/libyuv/libyuv/source/row_common.cc index da97821f7964..04b5caa27527 100644 --- a/media/libyuv/libyuv/source/row_common.cc +++ b/media/libyuv/libyuv/source/row_common.cc @@ -1306,6 +1306,86 @@ const struct YuvConstants SIMD_ALIGNED(kYvuH709Constants) = { #undef VR #undef YG +// BT.2020 YUV to RGB reference +// R = (Y - 16) * 1.164384 - V * -1.67867 +// G = (Y - 16) * 1.164384 - U * 0.187326 - V * -0.65042 +// B = (Y - 16) * 1.164384 - U * -2.14177 + +// Y contribution to R,G,B. Scale and bias. +#define YG 19003 /* round(1.164384 * 64 * 256 * 256 / 257) */ +#define YGB -1160 /* 1.164384 * 64 * -16 + 64 / 2 */ + +#define UB -128 /* max(-128, round(-2.142 * 64)) */ +#define UG 12 /* round(0.187326 * 64) */ +#define VG 42 /* round(0.65042 * 64) */ +#define VR -107 /* round(-1.67867 * 64) */ + +// Bias values to round, and subtract 128 from U and V. +#define BB (UB * 128 + YGB) +#define BG (UG * 128 + VG * 128 + YGB) +#define BR (VR * 128 + YGB) + +#if defined(__aarch64__) +const struct YuvConstants SIMD_ALIGNED(kYuv2020Constants) = { + {-UB, -VR, -UB, -VR, -UB, -VR, -UB, -VR}, + {-UB, -VR, -UB, -VR, -UB, -VR, -UB, -VR}, + {UG, VG, UG, VG, UG, VG, UG, VG}, + {UG, VG, UG, VG, UG, VG, UG, VG}, + {BB, BG, BR, 0, 0, 0, 0, 0}, + {0x0101 * YG, 0, 0, 0}}; +const struct YuvConstants SIMD_ALIGNED(kYvu2020Constants) = { + {-VR, -UB, -VR, -UB, -VR, -UB, -VR, -UB}, + {-VR, -UB, -VR, -UB, -VR, -UB, -VR, -UB}, + {VG, UG, VG, UG, VG, UG, VG, UG}, + {VG, UG, VG, UG, VG, UG, VG, UG}, + {BR, BG, BB, 0, 0, 0, 0, 0}, + {0x0101 * YG, 0, 0, 0}}; +#elif defined(__arm__) +const struct YuvConstants SIMD_ALIGNED(kYuv2020Constants) = { + {-UB, -UB, -UB, -UB, -VR, -VR, -VR, -VR, 0, 0, 0, 0, 0, 0, 0, 0}, + {UG, UG, UG, UG, VG, VG, VG, VG, 0, 0, 0, 0, 0, 0, 0, 0}, + {BB, BG, BR, 0, 0, 0, 0, 0}, + {0x0101 * YG, 0, 0, 0}}; +const struct YuvConstants SIMD_ALIGNED(kYvu2020Constants) = { + {-VR, -VR, -VR, -VR, -UB, -UB, -UB, -UB, 0, 0, 0, 0, 0, 0, 0, 0}, + {VG, VG, VG, VG, UG, UG, UG, UG, 0, 0, 0, 0, 0, 0, 0, 0}, + {BR, BG, BB, 0, 0, 0, 0, 0}, + {0x0101 * YG, 0, 0, 0}}; +#else +const struct YuvConstants SIMD_ALIGNED(kYuv2020Constants) = { + {UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, + UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0}, + {UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, + UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG}, + {0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, + 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR}, + {BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB}, + {BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG}, + {BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR}, + {YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG}}; +const struct YuvConstants SIMD_ALIGNED(kYvu2020Constants) = { + {VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, + VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0}, + {VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, + VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG}, + {0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, + 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB}, + {BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR, BR}, + {BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG, BG}, + {BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB, BB}, + {YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG}}; +#endif + +#undef BB +#undef BG +#undef BR +#undef YGB +#undef UB +#undef UG +#undef VG +#undef VR +#undef YG + // C reference code that mimics the YUV assembly. // Reads 8 bit YUV and leaves result as 16 bit.