From dab3111c8aa21f3d584084742d89356cf5039197 Mon Sep 17 00:00:00 2001 From: Sida Zhu Date: Tue, 15 Sep 2026 09:56:04 +0800 Subject: [PATCH] convert, scale: Add P010/P210/P410 convert paths and NV16/NV24/Px10 scale P010 and P210 were auxiliary FourCCs and P410 did not exist, so ConvertToI420, ConvertToARGB, and ConvertFromI420 could not consume or produce them. Callers had to pick I010/I210 helpers and pack UV by hand. NV12Scale existed, but NV16/NV24 and P010/P210/P410 had no Scale APIs. Promote P010 and P210 to primary FourCCs, add FOURCC_P410, and add the I4xx/Px10 converters those entry points need: I410ToP410, I412ToP412, P210ToI210, P410ToI410, P010/P210/P410ToI420, I420ToP010/P210/P410, I422ToP210, I444ToP410, and P410ToARGB/AR30Matrix. Add NV16Scale (same path as NV12Scale: ScalePlane + UVScale). Add P010Scale, P210Scale, and P410Scale: 10-bit samples stay in the MSBs, so Y/UV convert to 10-bit LSB, ScalePlane_16, then back to MSB. UVScale_16 cannot general-scale, so UV is split, scaled as planes, and merged. Packed-plane layout, odd-width strides, ConvertTo/From round-trips, and the existing scale factor/size/swap tests cover the new APIs. Bug: 537818862 Change-Id: I1ae5e6cad5e6d83acf1d0df69f94c23688bbe0e8 Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8346569 Reviewed-by: Frank Barchard Reviewed-by: richard winterton --- docs/formats.md | 45 +++- include/libyuv/convert.h | 105 ++++++++ include/libyuv/convert_argb.h | 32 +++ include/libyuv/convert_from.h | 75 ++++++ include/libyuv/scale.h | 68 ++++- include/libyuv/video_common.h | 10 +- source/convert.cc | 455 +++++++++++++++++++++++++++++++++ source/convert_argb.cc | 136 ++++++++++ source/convert_from.cc | 349 +++++++++++++++++++++++++ source/convert_to_argb.cc | 36 +++ source/convert_to_i420.cc | 36 +++ source/scale.cc | 296 ++++++++++++++++++--- unit_test/convert_argb_test.cc | 180 ++++++++++--- unit_test/convert_test.cc | 344 ++++++++++++++++++------- unit_test/scale_test.cc | 405 +++++++++++++++++++++++++++-- unit_test/video_common_test.cc | 1 + 16 files changed, 2377 insertions(+), 196 deletions(-) diff --git a/docs/formats.md b/docs/formats.md index 1c7299a9e..b0a81a94b 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -38,7 +38,7 @@ This is how OSX formats map to libyuv The following is extracted from video_common.h as a complete list of formats supported by libyuv. enum FourCC { - // 12 Primary YUV formats: 5 planar, 4 biplanar, 2 packed. + // 15 Primary YUV formats: 6 planar, 7 biplanar, 2 packed. FOURCC_I420 = FOURCC('I', '4', '2', '0'), FOURCC_I422 = FOURCC('I', '4', '2', '2'), FOURCC_I444 = FOURCC('I', '4', '4', '4'), @@ -47,11 +47,13 @@ The following is extracted from video_common.h as a complete list of formats sup FOURCC_NV12 = FOURCC('N', 'V', '1', '2'), FOURCC_NV16 = FOURCC('N', 'V', '1', '6'), FOURCC_NV24 = FOURCC('N', 'V', '2', '4'), + FOURCC_P010 = FOURCC('P', '0', '1', '0'), + FOURCC_P210 = FOURCC('P', '2', '1', '0'), + FOURCC_P410 = FOURCC('P', '4', '1', '0'), FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'), FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'), - FOURCC_H010 = FOURCC('H', '0', '1', '0'), // unofficial fourcc. 10 bit lsb - FOURCC_U010 = FOURCC('U', '0', '1', '0'), // bt.2020, unofficial fourcc. - // 10 bit lsb + FOURCC_I010 = FOURCC('I', '0', '1', '0'), // bt.601 10 bit 420 + FOURCC_I210 = FOURCC('I', '2', '1', '0'), // bt.601 10 bit 422 // 1 Secondary YUV format: row biplanar. FOURCC_M420 = FOURCC('M', '4', '2', '0'), // deprecated. @@ -74,18 +76,34 @@ The following is extracted from video_common.h as a complete list of formats sup // 1 Primary Compressed YUV format. FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'), - // 11 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias. + // 23 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias. FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'), FOURCC_YV16 = FOURCC('Y', 'V', '1', '6'), FOURCC_YV24 = FOURCC('Y', 'V', '2', '4'), FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'), // Linux version of I420. - FOURCC_J420 = FOURCC('J', '4', '2', '0'), - FOURCC_J400 = FOURCC('J', '4', '0', '0'), // unofficial fourcc - FOURCC_H420 = FOURCC('H', '4', '2', '0'), // unofficial fourcc - FOURCC_H422 = FOURCC('H', '4', '2', '2'), // unofficial fourcc + FOURCC_J420 = + FOURCC('J', '4', '2', '0'), // jpeg (bt.601 full), unofficial fourcc + FOURCC_J422 = + FOURCC('J', '4', '2', '2'), // jpeg (bt.601 full), unofficial fourcc + FOURCC_J444 = + FOURCC('J', '4', '4', '4'), // jpeg (bt.601 full), unofficial fourcc + FOURCC_J400 = + FOURCC('J', '4', '0', '0'), // jpeg (bt.601 full), unofficial fourcc + FOURCC_F420 = FOURCC('F', '4', '2', '0'), // bt.709 full, unofficial fourcc + FOURCC_F422 = FOURCC('F', '4', '2', '2'), // bt.709 full, unofficial fourcc + FOURCC_F444 = FOURCC('F', '4', '4', '4'), // bt.709 full, unofficial fourcc + FOURCC_H420 = FOURCC('H', '4', '2', '0'), // bt.709, unofficial fourcc + FOURCC_H422 = FOURCC('H', '4', '2', '2'), // bt.709, unofficial fourcc + FOURCC_H444 = FOURCC('H', '4', '4', '4'), // bt.709, unofficial fourcc FOURCC_U420 = FOURCC('U', '4', '2', '0'), // bt.2020, unofficial fourcc FOURCC_U422 = FOURCC('U', '4', '2', '2'), // bt.2020, unofficial fourcc FOURCC_U444 = FOURCC('U', '4', '4', '4'), // bt.2020, unofficial fourcc + FOURCC_F010 = FOURCC('F', '0', '1', '0'), // bt.709 full range 10 bit 420 + FOURCC_H010 = FOURCC('H', '0', '1', '0'), // bt.709 10 bit 420 + FOURCC_U010 = FOURCC('U', '0', '1', '0'), // bt.2020 10 bit 420 + FOURCC_F210 = FOURCC('F', '2', '1', '0'), // bt.709 full range 10 bit 422 + FOURCC_H210 = FOURCC('H', '2', '1', '0'), // bt.709 10 bit 422 + FOURCC_U210 = FOURCC('U', '2', '1', '0'), // bt.2020 10 bit 422 // 14 Auxiliary aliases. CanonicalFourCC() maps these to canonical fourcc. FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'), // Alias for I420. @@ -108,11 +126,12 @@ The following is extracted from video_common.h as a complete list of formats sup # Planar YUV The following formats contains a full size Y plane followed by 1 or 2 - planes for UV: I420, I422, I444, I400, NV21, NV12, I400 + planes for UV: I420, I422, I444, I400, NV21, NV12, NV16, NV24, + P010, P210, P410 The size (subsampling) of the UV varies. - I420, NV12 and NV21 are half width, half height - I422, NV16 and NV61 are half width, full height - I444, NV24 and NV42 are full width, full height + I420, NV12, NV21 and P010 are half width, half height + I422, NV16, NV61 and P210 are half width, full height + I444, NV24, NV42 and P410 are full width, full height I400 and J400 have no chroma channel. # Color space diff --git a/include/libyuv/convert.h b/include/libyuv/convert.h index 8828db35f..a09369783 100644 --- a/include/libyuv/convert.h +++ b/include/libyuv/convert.h @@ -560,6 +560,21 @@ int I210ToP210(const uint16_t* src_y, int width, int height); +// Convert I410 to P410 +LIBYUV_API +int I410ToP410(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + // Convert I012 to P012 LIBYUV_API int I012ToP012(const uint16_t* src_y, @@ -590,6 +605,21 @@ int I212ToP212(const uint16_t* src_y, int width, int height); +// Convert I412 to P412 +LIBYUV_API +int I412ToP412(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + // Convert I400 (grey) to I420. LIBYUV_API int I400ToI420(const uint8_t* src_y, @@ -704,6 +734,81 @@ int P010ToI010(const uint16_t* src_y, int width, int height); +// Convert P210 to I210. +LIBYUV_API +int P210ToI210(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_u, + int dst_stride_u, + uint16_t* dst_v, + int dst_stride_v, + int width, + int height); + +// Convert P410 to I410. +LIBYUV_API +int P410ToI410(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_u, + int dst_stride_u, + uint16_t* dst_v, + int dst_stride_v, + int width, + int height); + +// Convert P010 to I420. +LIBYUV_API +int P010ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height); + +// Convert P210 to I420. +LIBYUV_API +int P210ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height); + +// Convert P410 to I420. +LIBYUV_API +int P410ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height); + // Convert P012 to I012. LIBYUV_API int P012ToI012(const uint16_t* src_y, diff --git a/include/libyuv/convert_argb.h b/include/libyuv/convert_argb.h index 053bfde61..bcb2099d6 100644 --- a/include/libyuv/convert_argb.h +++ b/include/libyuv/convert_argb.h @@ -1702,6 +1702,18 @@ int P210ToARGBMatrix(const uint16_t* src_y, int width, int height); +// Convert P410 to ARGB with matrix. +LIBYUV_API +int P410ToARGBMatrix(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_argb, + int dst_stride_argb, + const struct YuvConstants* yuvconstants, + int width, + int height); + // Convert P010 to AR30 with matrix. LIBYUV_API int P010ToAR30Matrix(const uint16_t* src_y, @@ -1726,6 +1738,18 @@ int P210ToAR30Matrix(const uint16_t* src_y, int width, int height); +// Convert P410 to AR30 with matrix. +LIBYUV_API +int P410ToAR30Matrix(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_ar30, + int dst_stride_ar30, + const struct YuvConstants* yuvconstants, + int width, + int height); + // P012 and P010 use most significant bits so the conversion is the same. // Convert P012 to ARGB with matrix. #define P012ToARGBMatrix P010ToARGBMatrix @@ -1735,6 +1759,10 @@ int P210ToAR30Matrix(const uint16_t* src_y, #define P212ToARGBMatrix P210ToARGBMatrix // Convert P212 to AR30 with matrix. #define P212ToAR30Matrix P210ToAR30Matrix +// Convert P412 to ARGB with matrix. +#define P412ToARGBMatrix P410ToARGBMatrix +// Convert P412 to AR30 with matrix. +#define P412ToAR30Matrix P410ToAR30Matrix // Convert P016 to ARGB with matrix. #define P016ToARGBMatrix P010ToARGBMatrix @@ -1744,6 +1772,10 @@ int P210ToAR30Matrix(const uint16_t* src_y, #define P216ToARGBMatrix P210ToARGBMatrix // Convert P216 to AR30 with matrix. #define P216ToAR30Matrix P210ToAR30Matrix +// Convert P416 to ARGB with matrix. +#define P416ToARGBMatrix P410ToARGBMatrix +// Convert P416 to AR30 with matrix. +#define P416ToAR30Matrix P410ToAR30Matrix // Convert I420 with Alpha to preattenuated ARGB with matrix. LIBYUV_API diff --git a/include/libyuv/convert_from.h b/include/libyuv/convert_from.h index e9aa489e8..a746629af 100644 --- a/include/libyuv/convert_from.h +++ b/include/libyuv/convert_from.h @@ -128,6 +128,81 @@ int I420ToNV21(const uint8_t* src_y, int width, int height); +// Convert I420 to P010. +LIBYUV_API +int I420ToP010(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + +// Convert I422 to P210. +LIBYUV_API +int I422ToP210(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + +// Convert I444 to P410. +LIBYUV_API +int I444ToP410(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + +// Convert I420 to P210. +LIBYUV_API +int I420ToP210(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + +// Convert I420 to P410. +LIBYUV_API +int I420ToP410(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height); + LIBYUV_API int I420ToYUY2(const uint8_t* src_y, int src_stride_y, diff --git a/include/libyuv/scale.h b/include/libyuv/scale.h index e648c370e..799dbc70f 100644 --- a/include/libyuv/scale.h +++ b/include/libyuv/scale.h @@ -264,7 +264,7 @@ int I422Scale_12(const uint16_t* src_y, int dst_height, enum FilterMode filtering); -// Scales an NV12 image from the src width and height to the +// Scales an NV12, NV16 or NV24 image from the src width and height to the // dst width and height. // If filtering is kFilterNone, a simple nearest-neighbor algorithm is // used. This produces basic (blocky) quality at the fastest speed. @@ -289,6 +289,21 @@ int NV12Scale(const uint8_t* src_y, int dst_height, enum FilterMode filtering); +LIBYUV_API +int NV16Scale(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering); + LIBYUV_API int NV24Scale(const uint8_t* src_y, int src_stride_y, @@ -304,6 +319,57 @@ int NV24Scale(const uint8_t* src_y, int dst_height, enum FilterMode filtering); +// Scales a P010, P210 or P410 image (10-bit, samples in the MSBs of uint16). +// Strides are in uint16 elements. Y and UV are converted to 10-bit LSB, +// scaled with ScalePlane_16 (kFilterBox included), then converted back to +// 10-bit MSB. +// Returns 0 if successful. + +LIBYUV_API +int P010Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering); + +LIBYUV_API +int P210Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering); + +LIBYUV_API +int P410Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering); + #ifdef __cplusplus // Legacy API. Deprecated. LIBYUV_API diff --git a/include/libyuv/video_common.h b/include/libyuv/video_common.h index 0cdfd5217..e60707ae5 100644 --- a/include/libyuv/video_common.h +++ b/include/libyuv/video_common.h @@ -53,7 +53,7 @@ extern "C" { // Secondary formats are converted in 2 steps. // Auxilliary formats call primary converters. enum FourCC { - // 12 Primary YUV formats: 5 planar, 4 biplanar, 2 packed. + // 15 Primary YUV formats: 6 planar, 7 biplanar, 2 packed. FOURCC_I420 = FOURCC('I', '4', '2', '0'), FOURCC_I422 = FOURCC('I', '4', '2', '2'), FOURCC_I444 = FOURCC('I', '4', '4', '4'), @@ -62,6 +62,9 @@ enum FourCC { FOURCC_NV12 = FOURCC('N', 'V', '1', '2'), FOURCC_NV16 = FOURCC('N', 'V', '1', '6'), FOURCC_NV24 = FOURCC('N', 'V', '2', '4'), + FOURCC_P010 = FOURCC('P', '0', '1', '0'), + FOURCC_P210 = FOURCC('P', '2', '1', '0'), + FOURCC_P410 = FOURCC('P', '4', '1', '0'), FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'), FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'), FOURCC_I010 = FOURCC('I', '0', '1', '0'), // bt.601 10 bit 420 @@ -88,7 +91,7 @@ enum FourCC { // 1 Primary Compressed YUV format. FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'), - // 14 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias. + // 23 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias. FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'), FOURCC_YV16 = FOURCC('Y', 'V', '1', '6'), FOURCC_YV24 = FOURCC('Y', 'V', '2', '4'), @@ -116,8 +119,6 @@ enum FourCC { FOURCC_F210 = FOURCC('F', '2', '1', '0'), // bt.709 full range 10 bit 422 FOURCC_H210 = FOURCC('H', '2', '1', '0'), // bt.709 10 bit 422 FOURCC_U210 = FOURCC('U', '2', '1', '0'), // bt.2020 10 bit 422 - FOURCC_P010 = FOURCC('P', '0', '1', '0'), - FOURCC_P210 = FOURCC('P', '2', '1', '0'), // 14 Auxiliary aliases. CanonicalFourCC() maps these to canonical fourcc. FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'), // Alias for I420. @@ -197,6 +198,7 @@ enum FourCCBpp { FOURCC_BPP_H210 = 20, FOURCC_BPP_P010 = 15, FOURCC_BPP_P210 = 20, + FOURCC_BPP_P410 = 30, FOURCC_BPP_MJPG = 0, // 0 means unknown. FOURCC_BPP_H264 = 0, FOURCC_BPP_IYUV = 12, diff --git a/source/convert.cc b/source/convert.cc index 20445caf7..0ac2f40d2 100644 --- a/source/convert.cc +++ b/source/convert.cc @@ -1047,6 +1047,24 @@ int I210ToP210(const uint16_t* src_y, width, height, 1, 0, 10); } +LIBYUV_API +int I410ToP410(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return IxxxToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, 0, 0, 10); +} + LIBYUV_API int I012ToP012(const uint16_t* src_y, int src_stride_y, @@ -1083,6 +1101,24 @@ int I212ToP212(const uint16_t* src_y, width, height, 1, 0, 12); } +LIBYUV_API +int I412ToP412(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return IxxxToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, 0, 0, 12); +} + // 422 chroma is 1/2 width, 1x height // 420 chroma is 1/2 width, 1/2 height LIBYUV_API @@ -1838,6 +1874,425 @@ int P010ToI010(const uint16_t* src_y, width, height, 1, 1, 10); } +LIBYUV_API +int P210ToI210(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_u, + int dst_stride_u, + uint16_t* dst_v, + int dst_stride_v, + int width, + int height) { + return PxxxToIxxx(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + width, height, 1, 0, 10); +} + +LIBYUV_API +int P410ToI410(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_u, + int dst_stride_u, + uint16_t* dst_v, + int dst_stride_v, + int width, + int height) { + return PxxxToIxxx(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + width, height, 0, 0, 10); +} + +static void InvertPx10(const uint16_t** src_y, + int* src_stride_y, + const uint16_t** src_uv, + int* src_stride_uv, + int* height, + int subsample_y) { + const int abs_height = Abs(*height); + const int src_uv_height = SUBSAMPLE(abs_height, subsample_y, subsample_y); + if (*src_y) { + *src_y = *src_y + (ptrdiff_t)(abs_height - 1) * *src_stride_y; + *src_stride_y = -*src_stride_y; + } + *src_uv = *src_uv + (ptrdiff_t)(src_uv_height - 1) * *src_stride_uv; + *src_stride_uv = -*src_stride_uv; + *height = abs_height; +} + +static int Px10ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height, + int subsample_x, + int subsample_y) { + const int scale16 = 256; // P*10 stores 10-bit in the MSBs. + int src_uv_width; + int dst_uv_width; + if ((!src_y && dst_y) || !src_uv || !dst_u || !dst_v || width <= 0 || + height == 0 || height == INT_MIN) { + return -1; + } + if (height < 0) { + InvertPx10(&src_y, &src_stride_y, &src_uv, &src_stride_uv, &height, + subsample_y); + } + src_uv_width = SUBSAMPLE(width, subsample_x, subsample_x); + dst_uv_width = SUBSAMPLE(width, 1, 1); + + if (dst_y) { + Convert16To8Plane(src_y, src_stride_y, dst_y, dst_stride_y, scale16, width, + height); + } + + if (subsample_x && subsample_y) { + int y; + const int uv_packed = src_uv_width * 2; + const int uv_height = SUBSAMPLE(height, 1, 1); + void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale, + int width) = Convert16To8Row_C; + void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v, + int width) = SplitUVRow_C; + +#if defined(HAS_CONVERT16TO8ROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + Convert16To8Row = Convert16To8Row_Any_NEON; + if (IS_ALIGNED(uv_packed, 16)) { + Convert16To8Row = Convert16To8Row_NEON; + } + } +#endif +#if defined(HAS_CONVERT16TO8ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + Convert16To8Row = Convert16To8Row_SVE2; + } +#endif +#if defined(HAS_CONVERT16TO8ROW_SME) + if (TestCpuFlag(kCpuHasSME)) { + Convert16To8Row = Convert16To8Row_SME; + } +#endif +#if defined(HAS_CONVERT16TO8ROW_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3)) { + Convert16To8Row = Convert16To8Row_Any_SSSE3; + if (IS_ALIGNED(uv_packed, 16)) { + Convert16To8Row = Convert16To8Row_SSSE3; + } + } +#endif +#if defined(HAS_CONVERT16TO8ROW_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + Convert16To8Row = Convert16To8Row_Any_AVX2; + if (IS_ALIGNED(uv_packed, 32)) { + Convert16To8Row = Convert16To8Row_AVX2; + } + } +#endif +#if defined(HAS_CONVERT16TO8ROW_AVX512BW) + if (TestCpuFlag(kCpuHasAVX512BW)) { + Convert16To8Row = Convert16To8Row_Any_AVX512BW; + if (IS_ALIGNED(uv_packed, 64)) { + Convert16To8Row = Convert16To8Row_AVX512BW; + } + } +#endif +#if defined(HAS_CONVERT16TO8ROW_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + Convert16To8Row = Convert16To8Row_RVV; + } +#endif +#if defined(HAS_SPLITUVROW_SSE2) + if (TestCpuFlag(kCpuHasSSE2)) { + SplitUVRow = SplitUVRow_Any_SSE2; + if (IS_ALIGNED(src_uv_width, 16)) { + SplitUVRow = SplitUVRow_SSE2; + } + } +#endif +#if defined(HAS_SPLITUVROW_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + SplitUVRow = SplitUVRow_Any_AVX2; + if (IS_ALIGNED(src_uv_width, 32)) { + SplitUVRow = SplitUVRow_AVX2; + } + } +#endif +#if defined(HAS_SPLITUVROW_AVX512BW) + if (TestCpuFlag(kCpuHasAVX512BW)) { + SplitUVRow = SplitUVRow_Any_AVX512BW; + if (IS_ALIGNED(src_uv_width, 64)) { + SplitUVRow = SplitUVRow_AVX512BW; + } + } +#endif +#if defined(HAS_SPLITUVROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + SplitUVRow = SplitUVRow_Any_NEON; + if (IS_ALIGNED(src_uv_width, 16)) { + SplitUVRow = SplitUVRow_NEON; + } + } +#endif +#if defined(HAS_SPLITUVROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + SplitUVRow = SplitUVRow_SVE2; + } +#endif +#if defined(HAS_SPLITUVROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + SplitUVRow = SplitUVRow_Any_LSX; + if (IS_ALIGNED(src_uv_width, 32)) { + SplitUVRow = SplitUVRow_LSX; + } + } +#endif +#if defined(HAS_SPLITUVROW_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + SplitUVRow = SplitUVRow_RVV; + } +#endif + + align_buffer_64(row_uv, ((uv_packed + 63) & ~63)); + if (!row_uv) { + return 1; + } + for (y = 0; y < uv_height; ++y) { + Convert16To8Row(src_uv, row_uv, scale16, uv_packed); + SplitUVRow(row_uv, dst_u, dst_v, src_uv_width); + src_uv += src_stride_uv; + dst_u += dst_stride_u; + dst_v += dst_stride_v; + } + free_aligned_buffer_64(row_uv); + return 0; + } + + { + int y; + const int scale = 16384; // 10-bit LSB after SplitUVRow_16. + int row_stride; + void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u, + uint16_t* dst_v, int depth, int width) = + SplitUVRow_16_C; + void (*Half16To8)(const uint16_t* src_uv, ptrdiff_t src_uv_stride, + uint8_t* dst_uv, int scale, int width) = + subsample_x ? HalfRow_16To8_C + : ((width & 1) ? HalfWidthRow_16To8_Odd_C + : HalfWidthRow_16To8_C); + +#if defined(HAS_SPLITUVROW_16_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + SplitUVRow_16 = SplitUVRow_16_Any_AVX2; + if (IS_ALIGNED(src_uv_width, 16)) { + SplitUVRow_16 = SplitUVRow_16_AVX2; + } + } +#endif +#if defined(HAS_SPLITUVROW_16_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + SplitUVRow_16 = SplitUVRow_16_Any_NEON; + if (IS_ALIGNED(src_uv_width, 8)) { + SplitUVRow_16 = SplitUVRow_16_NEON; + } + } +#endif + if (subsample_x) { +#if defined(HAS_HALFROW_16TO8_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + Half16To8 = HalfRow_16To8_Any_NEON; + if (IS_ALIGNED(dst_uv_width, 16)) { + Half16To8 = HalfRow_16To8_NEON; + } + } +#endif +#if defined(HAS_HALFROW_16TO8_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + Half16To8 = HalfRow_16To8_SVE2; + } +#endif +#if defined(HAS_HALFROW_16TO8_SME) + if (TestCpuFlag(kCpuHasSME)) { + Half16To8 = HalfRow_16To8_SME; + } +#endif +#if defined(HAS_HALFROW_16TO8_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3)) { + Half16To8 = HalfRow_16To8_Any_SSSE3; + if (IS_ALIGNED(dst_uv_width, 16)) { + Half16To8 = HalfRow_16To8_SSSE3; + } + } +#endif +#if defined(HAS_HALFROW_16TO8_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + Half16To8 = HalfRow_16To8_Any_AVX2; + if (IS_ALIGNED(dst_uv_width, 32)) { + Half16To8 = HalfRow_16To8_AVX2; + } + } +#endif +#if defined(HAS_HALFROW_16TO8_AVX512BW) + if (TestCpuFlag(kCpuHasAVX512BW)) { + Half16To8 = HalfRow_16To8_Any_AVX512BW; + if (IS_ALIGNED(dst_uv_width, 64)) { + Half16To8 = HalfRow_16To8_AVX512BW; + } + } +#endif +#if defined(HAS_HALFROW_16TO8_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + Half16To8 = HalfRow_16To8_RVV; + } +#endif + } else if (IS_ALIGNED(width, 2)) { +#if defined(HAS_HALFWIDTHROW_16TO8_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + Half16To8 = HalfWidthRow_16To8_Any_NEON; + if (IS_ALIGNED(dst_uv_width, 8)) { + Half16To8 = HalfWidthRow_16To8_NEON; + } + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + Half16To8 = HalfWidthRow_16To8_SVE2; + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_SME) + if (TestCpuFlag(kCpuHasSME)) { + Half16To8 = HalfWidthRow_16To8_SME; + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3)) { + Half16To8 = HalfWidthRow_16To8_Any_SSSE3; + if (IS_ALIGNED(dst_uv_width, 8)) { + Half16To8 = HalfWidthRow_16To8_SSSE3; + } + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + Half16To8 = HalfWidthRow_16To8_Any_AVX2; + if (IS_ALIGNED(dst_uv_width, 16)) { + Half16To8 = HalfWidthRow_16To8_AVX2; + } + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_AVX512BW) + if (TestCpuFlag(kCpuHasAVX512BW)) { + Half16To8 = HalfWidthRow_16To8_Any_AVX512BW; + if (IS_ALIGNED(dst_uv_width, 32)) { + Half16To8 = HalfWidthRow_16To8_AVX512BW; + } + } +#endif +#if defined(HAS_HALFWIDTHROW_16TO8_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + Half16To8 = HalfWidthRow_16To8_RVV; + } +#endif + } + + row_stride = (src_uv_width + 31) & ~31; + align_buffer_64(row_u, (size_t)row_stride * sizeof(uint16_t) * 4u); + if (!row_u) { + return 1; + } + { + uint16_t* row_u0 = (uint16_t*)row_u; + uint16_t* row_u1 = row_u0 + row_stride; + uint16_t* row_v0 = row_u1 + row_stride; + uint16_t* row_v1 = row_v0 + row_stride; + for (y = 0; y < height - 1; y += 2) { + SplitUVRow_16(src_uv, row_u0, row_v0, 10, src_uv_width); + SplitUVRow_16(src_uv + src_stride_uv, row_u1, row_v1, 10, src_uv_width); + Half16To8(row_u0, row_stride, dst_u, scale, dst_uv_width); + Half16To8(row_v0, row_stride, dst_v, scale, dst_uv_width); + src_uv += src_stride_uv * 2; + dst_u += dst_stride_u; + dst_v += dst_stride_v; + } + if (height & 1) { + SplitUVRow_16(src_uv, row_u0, row_v0, 10, src_uv_width); + Half16To8(row_u0, 0, dst_u, scale, dst_uv_width); + Half16To8(row_v0, 0, dst_v, scale, dst_uv_width); + } + } + free_aligned_buffer_64(row_u); + return 0; + } +} + +LIBYUV_API +int P010ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + return Px10ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + width, height, 1, 1); +} + +LIBYUV_API +int P210ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + return Px10ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + width, height, 1, 0); +} + +LIBYUV_API +int P410ToI420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + return Px10ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + width, height, 0, 0); +} + LIBYUV_API int P012ToI012(const uint16_t* src_y, int src_stride_y, diff --git a/source/convert_argb.cc b/source/convert_argb.cc index 4f2cc62f5..238aea2b2 100644 --- a/source/convert_argb.cc +++ b/source/convert_argb.cc @@ -2079,6 +2079,74 @@ int P210ToARGBMatrix(const uint16_t* src_y, return 0; } +LIBYUV_API +int P410ToARGBMatrix(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_argb, + int dst_stride_argb, + const struct YuvConstants* yuvconstants, + int width, + int height) { + int y; + void (*P410ToARGBRow)( + const uint16_t* y_buf, const uint16_t* uv_buf, uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, int width) = P410ToARGBRow_C; + assert(yuvconstants); + if (!src_y || !src_uv || !dst_argb || width <= 0 || height == 0 || + height == INT_MIN) { + return -1; + } + // Negative height means invert the image. + if (height < 0) { + height = -height; + dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb; + dst_stride_argb = -dst_stride_argb; + } +#if defined(HAS_P410TOARGBROW_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3)) { + P410ToARGBRow = P410ToARGBRow_Any_SSSE3; + if (IS_ALIGNED(width, 8)) { + P410ToARGBRow = P410ToARGBRow_SSSE3; + } + } +#endif +#if defined(HAS_P410TOARGBROW_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + P410ToARGBRow = P410ToARGBRow_Any_AVX2; + if (IS_ALIGNED(width, 16)) { + P410ToARGBRow = P410ToARGBRow_AVX2; + } + } +#endif +#if defined(HAS_P410TOARGBROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + P410ToARGBRow = P410ToARGBRow_Any_NEON; + if (IS_ALIGNED(width, 8)) { + P410ToARGBRow = P410ToARGBRow_NEON; + } + } +#endif +#if defined(HAS_P410TOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + P410ToARGBRow = P410ToARGBRow_SVE2; + } +#endif +#if defined(HAS_P410TOARGBROW_SME) + if (TestCpuFlag(kCpuHasSME)) { + P410ToARGBRow = P410ToARGBRow_SME; + } +#endif + for (y = 0; y < height; ++y) { + P410ToARGBRow(src_y, src_uv, dst_argb, yuvconstants, width); + dst_argb += dst_stride_argb; + src_y += src_stride_y; + src_uv += src_stride_uv; + } + return 0; +} + LIBYUV_API int P010ToAR30Matrix(const uint16_t* src_y, int src_stride_y, @@ -2217,6 +2285,74 @@ int P210ToAR30Matrix(const uint16_t* src_y, return 0; } +LIBYUV_API +int P410ToAR30Matrix(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + uint8_t* dst_ar30, + int dst_stride_ar30, + const struct YuvConstants* yuvconstants, + int width, + int height) { + int y; + void (*P410ToAR30Row)( + const uint16_t* y_buf, const uint16_t* uv_buf, uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, int width) = P410ToAR30Row_C; + assert(yuvconstants); + if (!src_y || !src_uv || !dst_ar30 || width <= 0 || height == 0 || + height == INT_MIN) { + return -1; + } + // Negative height means invert the image. + if (height < 0) { + height = -height; + dst_ar30 = dst_ar30 + (ptrdiff_t)(height - 1) * dst_stride_ar30; + dst_stride_ar30 = -dst_stride_ar30; + } +#if defined(HAS_P410TOAR30ROW_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3)) { + P410ToAR30Row = P410ToAR30Row_Any_SSSE3; + if (IS_ALIGNED(width, 8)) { + P410ToAR30Row = P410ToAR30Row_SSSE3; + } + } +#endif +#if defined(HAS_P410TOAR30ROW_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + P410ToAR30Row = P410ToAR30Row_Any_AVX2; + if (IS_ALIGNED(width, 16)) { + P410ToAR30Row = P410ToAR30Row_AVX2; + } + } +#endif +#if defined(HAS_P410TOAR30ROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + P410ToAR30Row = P410ToAR30Row_Any_NEON; + if (IS_ALIGNED(width, 8)) { + P410ToAR30Row = P410ToAR30Row_NEON; + } + } +#endif +#if defined(HAS_P410TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + P410ToAR30Row = P410ToAR30Row_SVE2; + } +#endif +#if defined(HAS_P410TOAR30ROW_SME) + if (TestCpuFlag(kCpuHasSME)) { + P410ToAR30Row = P410ToAR30Row_SME; + } +#endif + for (y = 0; y < height; ++y) { + P410ToAR30Row(src_y, src_uv, dst_ar30, yuvconstants, width); + dst_ar30 += dst_stride_ar30; + src_y += src_stride_y; + src_uv += src_stride_uv; + } + return 0; +} + // Convert I420 with Alpha to preattenuated ARGB with matrix. LIBYUV_API int I420AlphaToARGBMatrix(const uint8_t* src_y, diff --git a/source/convert_from.cc b/source/convert_from.cc index 7e5a24443..2e6e84945 100644 --- a/source/convert_from.cc +++ b/source/convert_from.cc @@ -11,6 +11,7 @@ #include "libyuv/convert_from.h" #include +#include #include "libyuv/convert.h" // For I420Copy #include "libyuv/cpu_id.h" @@ -710,6 +711,296 @@ int I420ToNV21(const uint8_t* src_y, width, height); } +static int I4xxToPxxx(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height, + int subsample_x, + int subsample_y) { + int y; + int uv_width; + int uv_height; + int row_stride; + void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int bits, + int width) = Convert8To16Row_C; + void (*MergeUVRow_16)(const uint16_t* src_u, const uint16_t* src_v, + uint16_t* dst_uv, int depth, int width) = + MergeUVRow_16_C; + if ((!src_y && dst_y) || !src_u || !src_v || !dst_y || !dst_uv || + width <= 0 || height == 0 || height == INT_MIN) { + return -1; + } + if (height < 0) { + height = -height; + uv_height = SUBSAMPLE(height, subsample_y, subsample_y); + src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y; + src_u = src_u + (ptrdiff_t)(uv_height - 1) * src_stride_u; + src_v = src_v + (ptrdiff_t)(uv_height - 1) * src_stride_v; + src_stride_y = -src_stride_y; + src_stride_u = -src_stride_u; + src_stride_v = -src_stride_v; + } else { + uv_height = SUBSAMPLE(height, subsample_y, subsample_y); + } + uv_width = SUBSAMPLE(width, subsample_x, subsample_x); + + Convert8To16Plane(src_y, src_stride_y, dst_y, dst_stride_y, 10, width, + height); + ConvertToMSBPlane_16(dst_y, dst_stride_y, dst_y, dst_stride_y, width, height, + 10); + +#if defined(HAS_CONVERT8TO16ROW_SSE2) + if (TestCpuFlag(kCpuHasSSE2)) { + Convert8To16Row = Convert8To16Row_Any_SSE2; + if (IS_ALIGNED(uv_width, 16)) { + Convert8To16Row = Convert8To16Row_SSE2; + } + } +#endif +#if defined(HAS_CONVERT8TO16ROW_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + Convert8To16Row = Convert8To16Row_Any_AVX2; + if (IS_ALIGNED(uv_width, 32)) { + Convert8To16Row = Convert8To16Row_AVX2; + } + } +#endif +#if defined(HAS_CONVERT8TO16ROW_AVX512BW) + if (TestCpuFlag(kCpuHasAVX512BW)) { + Convert8To16Row = Convert8To16Row_Any_AVX512BW; + if (IS_ALIGNED(uv_width, 64)) { + Convert8To16Row = Convert8To16Row_AVX512BW; + } + } +#endif +#if defined(HAS_CONVERT8TO16ROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + Convert8To16Row = Convert8To16Row_Any_NEON; + if (IS_ALIGNED(uv_width, 16)) { + Convert8To16Row = Convert8To16Row_NEON; + } + } +#endif +#if defined(HAS_CONVERT8TO16ROW_SME) + if (TestCpuFlag(kCpuHasSME)) { + Convert8To16Row = Convert8To16Row_SME; + } +#endif +#if defined(HAS_CONVERT8TO16ROW_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + Convert8To16Row = Convert8To16Row_RVV; + } +#endif +#if defined(HAS_MERGEUVROW_16_AVX2) + if (TestCpuFlag(kCpuHasAVX2)) { + MergeUVRow_16 = MergeUVRow_16_Any_AVX2; + if (IS_ALIGNED(uv_width, 8)) { + MergeUVRow_16 = MergeUVRow_16_AVX2; + } + } +#endif +#if defined(HAS_MERGEUVROW_16_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + MergeUVRow_16 = MergeUVRow_16_Any_NEON; + if (IS_ALIGNED(uv_width, 8)) { + MergeUVRow_16 = MergeUVRow_16_NEON; + } + } +#endif +#if defined(HAS_MERGEUVROW_16_SME) + if (TestCpuFlag(kCpuHasSME)) { + MergeUVRow_16 = MergeUVRow_16_SME; + } +#endif + + row_stride = (uv_width + 31) & ~31; + align_buffer_64(row_u, (size_t)row_stride * sizeof(uint16_t) * 2u); + if (!row_u) { + return 1; + } + { + uint16_t* row_u16 = (uint16_t*)row_u; + uint16_t* row_v16 = row_u16 + row_stride; + for (y = 0; y < uv_height; ++y) { + Convert8To16Row(src_u, row_u16, 10, uv_width); + Convert8To16Row(src_v, row_v16, 10, uv_width); + MergeUVRow_16(row_u16, row_v16, dst_uv, 10, uv_width); + src_u += src_stride_u; + src_v += src_stride_v; + dst_uv += dst_stride_uv; + } + } + free_aligned_buffer_64(row_u); + return 0; +} + +LIBYUV_API +int I420ToP010(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return I4xxToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, 1, 1); +} + +LIBYUV_API +int I422ToP210(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return I4xxToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, 1, 0); +} + +LIBYUV_API +int I444ToP410(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return I4xxToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, 0, 0); +} + +static int I420ToPxxx(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height, + int dst_uv_width, + int (*i4xx_to_pxxx)(const uint8_t*, + int, + const uint8_t*, + int, + const uint8_t*, + int, + uint16_t*, + int, + uint16_t*, + int, + int, + int)) { + int r; + int abs_height; + uint64_t uv_size; + uint64_t total; + if ((!src_y && dst_y) || !src_u || !src_v || !dst_y || !dst_uv || + width <= 0 || height == 0 || height == INT_MIN) { + return -1; + } + abs_height = Abs(height); + if (height < 0) { + const int src_uv_height = SUBSAMPLE(abs_height, 1, 1); + src_y = src_y + (ptrdiff_t)(abs_height - 1) * src_stride_y; + src_u = src_u + (ptrdiff_t)(src_uv_height - 1) * src_stride_u; + src_v = src_v + (ptrdiff_t)(src_uv_height - 1) * src_stride_v; + src_stride_y = -src_stride_y; + src_stride_u = -src_stride_u; + src_stride_v = -src_stride_v; + height = abs_height; + } + uv_size = (uint64_t)dst_uv_width * (uint64_t)abs_height; + total = uv_size * 2u; + if (total > SIZE_MAX) { + return 1; + } + align_buffer_64(plane_u, (size_t)total); + if (!plane_u) { + return 1; + } + { + uint8_t* tmp_u = plane_u; + uint8_t* tmp_v = plane_u + uv_size; + r = I420ToI4xx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, NULL, 0, tmp_u, dst_uv_width, tmp_v, + dst_uv_width, width, height, dst_uv_width, abs_height); + if (!r) { + r = i4xx_to_pxxx(src_y, src_stride_y, tmp_u, dst_uv_width, tmp_v, + dst_uv_width, dst_y, dst_stride_y, dst_uv, + dst_stride_uv, width, height); + } + } + free_aligned_buffer_64(plane_u); + return r; +} + +LIBYUV_API +int I420ToP210(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return I420ToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, (Abs(width) + 1) >> 1, I422ToP210); +} + +LIBYUV_API +int I420ToP410(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, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + return I420ToPxxx(src_y, src_stride_y, src_u, src_stride_u, src_v, + src_stride_v, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + width, height, Abs(width), I444ToP410); +} + // Convert I420 to specified format LIBYUV_API int ConvertFromI420(const uint8_t* y, @@ -851,6 +1142,64 @@ int ConvertFromI420(const uint8_t* y, free_aligned_buffer_64(temp_v); break; } + case FOURCC_P010: + case FOURCC_P210: { + const int abs_height = Abs(height); + const int uv_min = ((width + 1) / 2) * 2; + int dst_stride_y; + int dst_stride_uv; + uint16_t* dst_y16; + uint16_t* dst_uv16; + if (dst_sample_stride == 0) { + dst_stride_y = width; + dst_stride_uv = uv_min; + } else { + if (dst_sample_stride & 1) { + return -1; + } + dst_stride_y = dst_sample_stride / 2; + dst_stride_uv = dst_sample_stride / 2; + if (Abs(dst_stride_uv) < uv_min) { + return -1; + } + } + dst_y16 = (uint16_t*)dst_sample; + dst_uv16 = dst_y16 + (ptrdiff_t)dst_stride_y * abs_height; + if (format == FOURCC_P010) { + r = I420ToP010(y, y_stride, u, u_stride, v, v_stride, dst_y16, + dst_stride_y, dst_uv16, dst_stride_uv, width, height); + } else { + r = I420ToP210(y, y_stride, u, u_stride, v, v_stride, dst_y16, + dst_stride_y, dst_uv16, dst_stride_uv, width, height); + } + break; + } + case FOURCC_P410: { + const int abs_height = Abs(height); + const int uv_min = width * 2; + int dst_stride_y; + int dst_stride_uv; + uint16_t* dst_y16; + uint16_t* dst_uv16; + if (dst_sample_stride == 0) { + dst_stride_y = width; + dst_stride_uv = uv_min; + } else { + if (dst_sample_stride & 1) { + return -1; + } + dst_stride_y = dst_sample_stride / 2; + dst_stride_uv = dst_sample_stride; + if (Abs(dst_stride_uv) < uv_min) { + return -1; + } + } + dst_y16 = (uint16_t*)dst_sample; + dst_uv16 = dst_y16 + (ptrdiff_t)dst_stride_y * abs_height; + r = I420ToP410(y, y_stride, u, u_stride, v, v_stride, dst_y16, + dst_stride_y, dst_uv16, dst_stride_uv, width, height); + break; + } // Triplanar formats case FOURCC_I420: case FOURCC_YV12: { diff --git a/source/convert_to_argb.cc b/source/convert_to_argb.cc index 16b538a2e..1cca7a780 100644 --- a/source/convert_to_argb.cc +++ b/source/convert_to_argb.cc @@ -435,6 +435,42 @@ int ConvertToARGB(const uint8_t* sample, r = NV24ToARGB(src, src_width, src_uv, aligned_src_width * 2, dst_argb, dst_stride_argb, crop_width, inv_crop_height); break; + case FOURCC_P010: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)(crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2); + r = P010ToARGBMatrix(src_y16, src_width, src_uv16, aligned_src_width, + dst_argb, dst_stride_argb, &kYuvI601Constants, + crop_width, inv_crop_height); + break; + } + case FOURCC_P210: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)crop_y * aligned_src_width) + ((crop_x / 2) * 2); + r = P210ToARGBMatrix(src_y16, src_width, src_uv16, aligned_src_width, + dst_argb, dst_stride_argb, &kYuvI601Constants, + crop_width, inv_crop_height); + break; + } + case FOURCC_P410: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)crop_y * src_width * 2) + crop_x * 2; + r = P410ToARGBMatrix(src_y16, src_width, src_uv16, src_width * 2, + dst_argb, dst_stride_argb, &kYuvI601Constants, + crop_width, inv_crop_height); + break; + } // Triplanar formats case FOURCC_I420: case FOURCC_YV12: { diff --git a/source/convert_to_i420.cc b/source/convert_to_i420.cc index 91cd507c0..7fac092a0 100644 --- a/source/convert_to_i420.cc +++ b/source/convert_to_i420.cc @@ -378,6 +378,42 @@ int ConvertToI420(const uint8_t* sample, dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, crop_width, inv_crop_height); break; + case FOURCC_P010: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)(crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2); + r = P010ToI420(src_y16, src_width, src_uv16, aligned_src_width, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + crop_width, inv_crop_height); + break; + } + case FOURCC_P210: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)crop_y * aligned_src_width) + ((crop_x / 2) * 2); + r = P210ToI420(src_y16, src_width, src_uv16, aligned_src_width, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + crop_width, inv_crop_height); + break; + } + case FOURCC_P410: { + const uint16_t* src16 = (const uint16_t*)sample; + const uint16_t* src_y16 = + src16 + ((ptrdiff_t)src_width * crop_y + crop_x); + const uint16_t* src_uv16 = + src16 + ((ptrdiff_t)src_width * abs_src_height) + + ((ptrdiff_t)crop_y * src_width * 2) + crop_x * 2; + r = P410ToI420(src_y16, src_width, src_uv16, src_width * 2, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, + crop_width, inv_crop_height); + break; + } // Triplanar formats case FOURCC_I420: case FOURCC_YV12: { diff --git a/source/scale.cc b/source/scale.cc index 81e5aea90..114514197 100644 --- a/source/scale.cc +++ b/source/scale.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include "libyuv/cpu_id.h" @@ -2599,8 +2600,187 @@ int I422Scale_12(const uint16_t* src_y, return r; } -// Scale an NV12 image. -// This function in turn calls a scaling function for each plane. +// Scale NV12 / NV16 / NV24. subsample_x/y are 1 to half that axis, else 0. + +static int NVScale(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering, + int subsample_x, + int subsample_y) { + int r; + int src_uv_width; + int src_uv_height; + int dst_uv_width; + int dst_uv_height; + + if (!src_y || !src_uv || src_width <= 0 || src_height == 0 || + src_height == INT_MIN || !dst_y || !dst_uv || dst_width <= 0 || + dst_height <= 0) { + return -1; + } + src_uv_width = SUBSAMPLE(src_width, subsample_x, subsample_x); + src_uv_height = SUBSAMPLE(src_height, subsample_y, subsample_y); + dst_uv_width = SUBSAMPLE(dst_width, subsample_x, subsample_x); + dst_uv_height = SUBSAMPLE(dst_height, subsample_y, subsample_y); + + r = ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, + dst_stride_y, dst_width, dst_height, filtering); + if (r != 0) { + return r; + } + r = UVScale(src_uv, src_stride_uv, src_uv_width, src_uv_height, dst_uv, + dst_stride_uv, dst_uv_width, dst_uv_height, filtering); + return r; +} + +// Scale P010 / P210 / P410. subsample_x/y are 1 to half that axis, else 0. +// Samples are 10-bit in the MSBs. Convert to 10-bit LSB, ScalePlane_16, then +// back to MSB. UVScale_16 cannot general-scale, so UV is split then merged. + +static int PxxxScale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering, + int subsample_x, + int subsample_y) { + int r; + int src_uv_width; + int src_uv_height; + int dst_uv_width; + int dst_uv_height; + size_t src_uv_plane; + size_t dst_uv_plane; + size_t src_uv_bytes; + size_t dst_uv_bytes; + + if (!src_y || !src_uv || src_width <= 0 || src_height == 0 || + src_height == INT_MIN || !dst_y || !dst_uv || dst_width <= 0 || + dst_height <= 0) { + return -1; + } + // ConvertToLSBPlane_16 / SplitUVPlane_16 invert dst, not src. Point both + // planes at the last row so ScalePlane_16 and the UV split see a top-down + // image. + if (src_height < 0) { + const int abs_height = -src_height; + const int src_uv_h = SUBSAMPLE(abs_height, subsample_y, subsample_y); + src_y = src_y + (ptrdiff_t)(abs_height - 1) * src_stride_y; + src_stride_y = -src_stride_y; + src_uv = src_uv + (ptrdiff_t)(src_uv_h - 1) * src_stride_uv; + src_stride_uv = -src_stride_uv; + src_height = abs_height; + } + + src_uv_width = SUBSAMPLE(src_width, subsample_x, subsample_x); + src_uv_height = SUBSAMPLE(src_height, subsample_y, subsample_y); + dst_uv_width = SUBSAMPLE(dst_width, subsample_x, subsample_x); + dst_uv_height = SUBSAMPLE(dst_height, subsample_y, subsample_y); + + if (src_width == dst_width && src_height == dst_height) { + r = ScalePlane_16(src_y, src_stride_y, src_width, src_height, dst_y, + dst_stride_y, dst_width, dst_height, filtering); + } else { + size_t src_y_plane; + size_t src_y_bytes; + if ((size_t)src_width > SIZE_MAX / (size_t)src_height) { + return -1; + } + src_y_plane = (size_t)src_width * (size_t)src_height; + if (src_y_plane > SIZE_MAX / sizeof(uint16_t)) { + return 1; + } + src_y_bytes = src_y_plane * sizeof(uint16_t); + align_buffer_64(src_y_lsb, src_y_bytes); + if (!src_y_lsb) { + return 1; + } + ConvertToLSBPlane_16(src_y, src_stride_y, (uint16_t*)src_y_lsb, src_width, + src_width, src_height, 10); + r = ScalePlane_16((uint16_t*)src_y_lsb, src_width, src_width, src_height, + dst_y, dst_stride_y, dst_width, dst_height, filtering); + if (r == 0) { + ConvertToMSBPlane_16(dst_y, dst_stride_y, dst_y, dst_stride_y, dst_width, + dst_height, 10); + } + free_aligned_buffer_64(src_y_lsb); + } + if (r != 0) { + return r; + } + + if (src_uv_width == dst_uv_width && src_uv_height == dst_uv_height && + src_uv_width <= INT_MAX / 2) { + CopyPlane_16(src_uv, src_stride_uv, dst_uv, dst_stride_uv, src_uv_width * 2, + src_uv_height); + return 0; + } + + if (src_uv_width <= 0 || src_uv_height <= 0 || dst_uv_width <= 0 || + dst_uv_height <= 0 || + (size_t)src_uv_width > SIZE_MAX / (size_t)src_uv_height || + (size_t)dst_uv_width > SIZE_MAX / (size_t)dst_uv_height) { + return -1; + } + src_uv_plane = (size_t)src_uv_width * (size_t)src_uv_height; + dst_uv_plane = (size_t)dst_uv_width * (size_t)dst_uv_height; + if (src_uv_plane > SIZE_MAX / (2u * sizeof(uint16_t)) || + dst_uv_plane > SIZE_MAX / (2u * sizeof(uint16_t))) { + return 1; + } + src_uv_bytes = src_uv_plane * sizeof(uint16_t) * 2u; + dst_uv_bytes = dst_uv_plane * sizeof(uint16_t) * 2u; + + align_buffer_64(src_uv_planes, src_uv_bytes); + align_buffer_64(dst_uv_planes, dst_uv_bytes); + if (!src_uv_planes || !dst_uv_planes) { + free_aligned_buffer_64(src_uv_planes); + free_aligned_buffer_64(dst_uv_planes); + return 1; + } + + { + uint16_t* src_u = (uint16_t*)src_uv_planes; + uint16_t* src_v = src_u + src_uv_plane; + uint16_t* dst_u = (uint16_t*)dst_uv_planes; + uint16_t* dst_v = dst_u + dst_uv_plane; + + SplitUVPlane_16(src_uv, src_stride_uv, src_u, src_uv_width, src_v, + src_uv_width, src_uv_width, src_uv_height, 10); + r = ScalePlane_16(src_u, src_uv_width, src_uv_width, src_uv_height, dst_u, + dst_uv_width, dst_uv_width, dst_uv_height, filtering); + if (r == 0) { + r = ScalePlane_16(src_v, src_uv_width, src_uv_width, src_uv_height, dst_v, + dst_uv_width, dst_uv_width, dst_uv_height, filtering); + } + if (r == 0) { + MergeUVPlane_16(dst_u, dst_uv_width, dst_v, dst_uv_width, dst_uv, + dst_stride_uv, dst_uv_width, dst_uv_height, 10); + } + } + + free_aligned_buffer_64(src_uv_planes); + free_aligned_buffer_64(dst_uv_planes); + return r; +} LIBYUV_API int NV12Scale(const uint8_t* src_y, @@ -2616,26 +2796,28 @@ int NV12Scale(const uint8_t* src_y, int dst_width, int dst_height, enum FilterMode filtering) { - int r; + return NVScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 1, 1); +} - if (!src_y || !src_uv || src_width <= 0 || src_height == 0 || - src_height == INT_MIN || !dst_y || !dst_uv || dst_width <= 0 || - dst_height <= 0) { - return -1; - } - int src_halfwidth = SUBSAMPLE(src_width, 1, 1); - int src_halfheight = SUBSAMPLE(src_height, 1, 1); - int dst_halfwidth = SUBSAMPLE(dst_width, 1, 1); - int dst_halfheight = SUBSAMPLE(dst_height, 1, 1); - - r = ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, - dst_stride_y, dst_width, dst_height, filtering); - if (r != 0) { - return r; - } - r = UVScale(src_uv, src_stride_uv, src_halfwidth, src_halfheight, dst_uv, - dst_stride_uv, dst_halfwidth, dst_halfheight, filtering); - return r; +LIBYUV_API +int NV16Scale(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering) { + return NVScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 1, 0); } LIBYUV_API @@ -2652,22 +2834,66 @@ int NV24Scale(const uint8_t* src_y, int dst_width, int dst_height, enum FilterMode filtering) { - int r; + return NVScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 0, 0); +} - if (!src_y || !src_uv || src_width <= 0 || src_height == 0 || - src_height == INT_MIN || !dst_y || !dst_uv || dst_width <= 0 || - dst_height <= 0) { - return -1; - } +LIBYUV_API +int P010Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering) { + return PxxxScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 1, 1); +} - r = ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, - dst_stride_y, dst_width, dst_height, filtering); - if (r != 0) { - return r; - } - r = UVScale(src_uv, src_stride_uv, src_width, src_height, dst_uv, - dst_stride_uv, dst_width, dst_height, filtering); - return r; +LIBYUV_API +int P210Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering) { + return PxxxScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 1, 0); +} + +LIBYUV_API +int P410Scale(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering) { + return PxxxScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, + src_height, dst_y, dst_stride_y, dst_uv, dst_stride_uv, + dst_width, dst_height, filtering, 0, 0); } // Deprecated api diff --git a/unit_test/convert_argb_test.cc b/unit_test/convert_argb_test.cc index 44b5bda18..bdc71a697 100644 --- a/unit_test/convert_argb_test.cc +++ b/unit_test/convert_argb_test.cc @@ -177,6 +177,11 @@ TESTBPTOP(NV21, uint8_t, 1, 2, 2, I420, uint8_t, 1, 2, 2, 8, 1, 1) TESTBPTOP(MM21, uint8_t, 1, 2, 2, I420, uint8_t, 1, 2, 2, 8, 16, 32) TESTBPTOP(P010, uint16_t, 2, 2, 2, I010, uint16_t, 2, 2, 2, 10, 1, 1) TESTBPTOP(P012, uint16_t, 2, 2, 2, I012, uint16_t, 2, 2, 2, 12, 1, 1) +TESTBPTOP(P210, uint16_t, 2, 2, 1, I210, uint16_t, 2, 2, 1, 10, 1, 1) +TESTBPTOP(P410, uint16_t, 2, 1, 1, I410, uint16_t, 2, 1, 1, 10, 1, 1) +TESTBPTOP(P010, uint16_t, 2, 2, 2, I420, uint8_t, 1, 2, 2, 10, 1, 1) +TESTBPTOP(P210, uint16_t, 2, 2, 1, I420, uint8_t, 1, 2, 2, 10, 1, 1) +TESTBPTOP(P410, uint16_t, 2, 1, 1, I420, uint8_t, 1, 2, 2, 10, 1, 1) // Provide matrix wrappers for full range bt.709 #define F420ToABGR(a, b, c, d, e, f, g, h, i, j) \ @@ -499,6 +504,60 @@ TESTBPTOB(NV21, 2, 2, YUV24, RAW, 3) TESTBPTOB(NV12, 2, 2, RGB565, RGB565, 2) #endif +#define TESTBP16TOBTRANSITIVE(FMT_BP, FMT_PLANAR) \ + TEST_F(LibYUVConvertTest, FMT_BP##ToARGB_Matches##FMT_PLANAR) { \ + const int kWidth = benchmark_width_ | 1; \ + const int kHeight = benchmark_height_; \ + const int kPixels = kWidth * kHeight; \ + align_buffer_page_end(src_y, kPixels * 2); \ + align_buffer_page_end(src_uv, kPixels * 4); \ + align_buffer_page_end(plane_y, kPixels * 2); \ + align_buffer_page_end(plane_u, kPixels * 2); \ + align_buffer_page_end(plane_v, kPixels * 2); \ + align_buffer_page_end(dst_bp, kPixels * 4); \ + align_buffer_page_end(dst_planar, kPixels * 4); \ + uint16_t* src_y16 = reinterpret_cast(src_y); \ + uint16_t* src_uv16 = reinterpret_cast(src_uv); \ + uint16_t* plane_y16 = reinterpret_cast(plane_y); \ + uint16_t* plane_u16 = reinterpret_cast(plane_u); \ + uint16_t* plane_v16 = reinterpret_cast(plane_v); \ + for (int i = 0; i < kPixels; ++i) { \ + src_y16[i] = static_cast((fastrand() & 0x3ff) << 6); \ + } \ + for (int i = 0; i < kPixels * 2; ++i) { \ + src_uv16[i] = static_cast((fastrand() & 0x3ff) << 6); \ + } \ + EXPECT_EQ(0, FMT_BP##To##FMT_PLANAR(src_y16, kWidth, src_uv16, kWidth * 2, \ + plane_y16, kWidth, plane_u16, kWidth, \ + plane_v16, kWidth, kWidth, kHeight)); \ + EXPECT_EQ(0, FMT_BP##ToARGBMatrix(src_y16, kWidth, src_uv16, kWidth * 2, \ + dst_bp, kWidth * 4, &kYuvI601Constants, \ + kWidth, kHeight)); \ + EXPECT_EQ( \ + 0, FMT_PLANAR##ToARGBMatrix(plane_y16, kWidth, plane_u16, kWidth, \ + plane_v16, kWidth, dst_planar, kWidth * 4, \ + &kYuvI601Constants, kWidth, kHeight)); \ + int max_diff = 0; \ + for (int i = 0; i < kPixels * 4; ++i) { \ + const int diff = Abs(dst_bp[i] - dst_planar[i]); \ + if (diff > max_diff) { \ + max_diff = diff; \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_uv); \ + free_aligned_buffer_page_end(plane_y); \ + free_aligned_buffer_page_end(plane_u); \ + free_aligned_buffer_page_end(plane_v); \ + free_aligned_buffer_page_end(dst_bp); \ + free_aligned_buffer_page_end(dst_planar); \ + } + +TESTBP16TOBTRANSITIVE(P010, I010) +TESTBP16TOBTRANSITIVE(P210, I210) +TESTBP16TOBTRANSITIVE(P410, I410) + #define TESTATOBI(FMT_A, TYPE_A, EPP_A, STRIDE_A, HEIGHT_A, FMT_B, TYPE_B, \ EPP_B, STRIDE_B, HEIGHT_B, W1280, N, NEG, OFF) \ TEST_F(LibYUVConvertTest, FMT_A##To##FMT_B##N) { \ @@ -1686,41 +1745,82 @@ TEST_F(LibYUVConvertTest, RotateWithARGBSource) { ASSERT_EQ(dst[3], src[1]); } -TEST_F(LibYUVConvertTest, ConvertToARGB_NV16) { - const int kWidth = 64; - const int kHeight = 48; - const int sample_size = kWidth * kHeight * 2; - align_buffer_page_end(src, sample_size); - align_buffer_page_end(dst, kWidth * kHeight * 4); - MemRandomize(src, sample_size); - memset(dst, 0, kWidth * kHeight * 4); +#define TESTCONVERTTOARGBSMOKE(FMT, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TEST_F(LibYUVConvertTest, ConvertToARGB_##FMT) { \ + const int kWidth = 64; \ + const int kHeight = 48; \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kSampleSize = \ + (kWidth * kHeight + kUvStride * kUvHeight) * BPC; \ + align_buffer_page_end(src, kSampleSize); \ + align_buffer_page_end(dst, kWidth * kHeight * 4); \ + MemRandomize(src, kSampleSize); \ + memset(dst, 0, kWidth * kHeight * 4); \ + EXPECT_EQ(0, ConvertToARGB(src, kSampleSize, dst, kWidth * 4, 0, 0, \ + kWidth, kHeight, kWidth, kHeight, kRotate0, \ + FOURCC_##FMT)); \ + free_aligned_buffer_page_end(src); \ + free_aligned_buffer_page_end(dst); \ + } - int r = ConvertToARGB(src, sample_size, dst, kWidth * 4, - 0, 0, kWidth, kHeight, kWidth, kHeight, - kRotate0, FOURCC_NV16); - EXPECT_EQ(0, r); +TESTCONVERTTOARGBSMOKE(NV16, 1, 2, 1) +TESTCONVERTTOARGBSMOKE(NV24, 1, 1, 1) - free_aligned_buffer_page_end(src); - free_aligned_buffer_page_end(dst); -} +#define TESTCONVERTTOARGBI(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, W1280, N, \ + NEG) \ + TEST_F(LibYUVConvertTest, ConvertToARGB_##FMT##N) { \ + const int kWidth = W1280; \ + const int kHeight = benchmark_height_; \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kYPixels = kWidth * kHeight; \ + const int kUvPixels = kUvStride * kUvHeight; \ + align_buffer_page_end(src, (kYPixels + kUvPixels) * BPC); \ + align_buffer_page_end(dst, kWidth * kHeight * 4); \ + align_buffer_page_end(ref, kWidth * kHeight * 4); \ + TYPE* src_p = reinterpret_cast(src); \ + MemRandomize(src, (kYPixels + kUvPixels) * BPC); \ + if (BPC == 2) { \ + for (int i = 0; i < kYPixels + kUvPixels; ++i) { \ + src_p[i] = static_cast(src_p[i] & 0xffc0); \ + } \ + } \ + memset(dst, 1, kWidth * kHeight * 4); \ + memset(ref, 2, kWidth * kHeight * 4); \ + EXPECT_EQ(0, FMT##ToARGBMatrix(src_p, kWidth, src_p + kYPixels, kUvStride, \ + ref, kWidth * 4, &kYuvI601Constants, \ + kWidth, NEG kHeight)); \ + EXPECT_EQ(0, ConvertToARGB(src, 0, dst, kWidth * 4, 0, 0, kWidth, \ + NEG kHeight, kWidth, kHeight, kRotate0, \ + FOURCC_##FMT)); \ + for (int i = 0; i < kWidth * kHeight * 4; ++i) { \ + ASSERT_EQ(dst[i], ref[i]); \ + } \ + free_aligned_buffer_page_end(src); \ + free_aligned_buffer_page_end(dst); \ + free_aligned_buffer_page_end(ref); \ + } -TEST_F(LibYUVConvertTest, ConvertToARGB_NV24) { - const int kWidth = 64; - const int kHeight = 48; - const int sample_size = kWidth * kHeight * 3; - align_buffer_page_end(src, sample_size); - align_buffer_page_end(dst, kWidth * kHeight * 4); - MemRandomize(src, sample_size); - memset(dst, 0, kWidth * kHeight * 4); +#if defined(ENABLE_FULL_TESTS) +#define TESTCONVERTTOARGB(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTTOARGBI(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ + 1, _Any, +) \ + TESTCONVERTTOARGBI(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -) \ + TESTCONVERTTOARGBI(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#else +#define TESTCONVERTTOARGB(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTTOARGBI(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#endif - int r = ConvertToARGB(src, sample_size, dst, kWidth * 4, - 0, 0, kWidth, kHeight, kWidth, kHeight, - kRotate0, FOURCC_NV24); - EXPECT_EQ(0, r); - - free_aligned_buffer_page_end(src); - free_aligned_buffer_page_end(dst); -} +TESTCONVERTTOARGB(P010, uint16_t, 2, 2, 2) +TESTCONVERTTOARGB(P210, uint16_t, 2, 2, 1) +TESTCONVERTTOARGB(P410, uint16_t, 2, 1, 1) #ifdef HAS_ARGBTOAR30ROW_AVX2 TEST_F(LibYUVConvertTest, ARGBToAR30Row_Opt) { @@ -2253,28 +2353,40 @@ TESTQPLANAR16TOB(I210Alpha, 2, 1, ARGBFilter, 4, 4, 1, 10) P010ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P210ToARGB(a, b, c, d, e, f, g, h) \ P210ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P410ToARGB(a, b, c, d, e, f, g, h) \ + P410ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P010ToAR30(a, b, c, d, e, f, g, h) \ P010ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P210ToAR30(a, b, c, d, e, f, g, h) \ P210ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P410ToAR30(a, b, c, d, e, f, g, h) \ + P410ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P012ToARGB(a, b, c, d, e, f, g, h) \ P012ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P212ToARGB(a, b, c, d, e, f, g, h) \ P212ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P412ToARGB(a, b, c, d, e, f, g, h) \ + P412ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P012ToAR30(a, b, c, d, e, f, g, h) \ P012ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P212ToAR30(a, b, c, d, e, f, g, h) \ P212ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P412ToAR30(a, b, c, d, e, f, g, h) \ + P412ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P016ToARGB(a, b, c, d, e, f, g, h) \ P016ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P216ToARGB(a, b, c, d, e, f, g, h) \ P216ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P416ToARGB(a, b, c, d, e, f, g, h) \ + P416ToARGBMatrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P016ToAR30(a, b, c, d, e, f, g, h) \ P016ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P216ToAR30(a, b, c, d, e, f, g, h) \ P216ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) +#define P416ToAR30(a, b, c, d, e, f, g, h) \ + P416ToAR30Matrix(a, b, c, d, e, f, &kYuvH709Constants, g, h) #define P010ToARGBFilter(a, b, c, d, e, f, g, h) \ P010ToARGBMatrixFilter(a, b, c, d, e, f, &kYuvH709Constants, g, h, \ @@ -2292,19 +2404,25 @@ TESTQPLANAR16TOB(I210Alpha, 2, 1, ARGBFilter, 4, 4, 1, 10) #if !defined(DISABLE_SLOW_TESTS) || defined(__x86_64__) || defined(__i386__) TESTBP16TOB(P010, 2, 2, ARGB, 4, 4, 1, 10) TESTBP16TOB(P210, 2, 1, ARGB, 4, 4, 1, 10) +TESTBP16TOB(P410, 1, 1, ARGB, 4, 4, 1, 10) TESTBP16TOB(P012, 2, 2, ARGB, 4, 4, 1, 12) TESTBP16TOB(P212, 2, 1, ARGB, 4, 4, 1, 12) +TESTBP16TOB(P412, 1, 1, ARGB, 4, 4, 1, 12) TESTBP16TOB(P016, 2, 2, ARGB, 4, 4, 1, 16) TESTBP16TOB(P216, 2, 1, ARGB, 4, 4, 1, 16) +TESTBP16TOB(P416, 1, 1, ARGB, 4, 4, 1, 16) TESTBP16TOB(P010, 2, 2, ARGBFilter, 4, 4, 1, 10) TESTBP16TOB(P210, 2, 1, ARGBFilter, 4, 4, 1, 10) #ifdef LITTLE_ENDIAN_ONLY_TEST TESTBP16TOB(P010, 2, 2, AR30, 4, 4, 1, 10) TESTBP16TOB(P210, 2, 1, AR30, 4, 4, 1, 10) +TESTBP16TOB(P410, 1, 1, AR30, 4, 4, 1, 10) TESTBP16TOB(P012, 2, 2, AR30, 4, 4, 1, 12) TESTBP16TOB(P212, 2, 1, AR30, 4, 4, 1, 12) +TESTBP16TOB(P412, 1, 1, AR30, 4, 4, 1, 12) TESTBP16TOB(P016, 2, 2, AR30, 4, 4, 1, 16) TESTBP16TOB(P216, 2, 1, AR30, 4, 4, 1, 16) +TESTBP16TOB(P416, 1, 1, AR30, 4, 4, 1, 16) TESTBP16TOB(P010, 2, 2, AR30Filter, 4, 4, 1, 10) TESTBP16TOB(P210, 2, 1, AR30Filter, 4, 4, 1, 10) #endif // LITTLE_ENDIAN_ONLY_TEST diff --git a/unit_test/convert_test.cc b/unit_test/convert_test.cc index 0e59e3723..e23fa0def 100644 --- a/unit_test/convert_test.cc +++ b/unit_test/convert_test.cc @@ -463,6 +463,200 @@ TESTPLANARTOBP(I010, uint16_t, 2, 2, 2, P010, uint16_t, 2, 2, 2, 10) TESTPLANARTOBP(I210, uint16_t, 2, 2, 1, P210, uint16_t, 2, 2, 1, 10) TESTPLANARTOBP(I012, uint16_t, 2, 2, 2, P012, uint16_t, 2, 2, 2, 12) TESTPLANARTOBP(I212, uint16_t, 2, 2, 1, P212, uint16_t, 2, 2, 1, 12) +TESTPLANARTOBP(I420, uint8_t, 1, 2, 2, P010, uint16_t, 2, 2, 2, 8) +TESTPLANARTOBP(I422, uint8_t, 1, 2, 1, P210, uint16_t, 2, 2, 1, 8) +TESTPLANARTOBP(I444, uint8_t, 1, 1, 1, P410, uint16_t, 2, 1, 1, 8) +TESTPLANARTOBP(I420, uint8_t, 1, 2, 2, P210, uint16_t, 2, 2, 1, 8) +TESTPLANARTOBP(I420, uint8_t, 1, 2, 2, P410, uint16_t, 2, 1, 1, 8) +TESTPLANARTOBP(I410, uint16_t, 2, 1, 1, P410, uint16_t, 2, 1, 1, 10) +TESTPLANARTOBP(I412, uint16_t, 2, 1, 1, P412, uint16_t, 2, 1, 1, 12) + +#define TESTCONVERTFROMI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, W1280, N, \ + NEG) \ + TEST_F(LibYUVConvertTest, ConvertFromI420_##FMT##N) { \ + const int kWidth = W1280; \ + const int kHeight = benchmark_height_; \ + const int kHalfWidth = SUBSAMPLE(kWidth, 2); \ + const int kHalfHeight = SUBSAMPLE(kHeight, 2); \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kSamplePixels = kWidth * kHeight + kUvStride * kUvHeight; \ + align_buffer_page_end(src_y, kWidth * kHeight); \ + align_buffer_page_end(src_u, kHalfWidth * kHalfHeight); \ + align_buffer_page_end(src_v, kHalfWidth * kHalfHeight); \ + align_buffer_page_end(dst, kSamplePixels * BPC); \ + align_buffer_page_end(ref, kSamplePixels * BPC); \ + MemRandomize(src_y, kWidth * kHeight); \ + MemRandomize(src_u, kHalfWidth * kHalfHeight); \ + MemRandomize(src_v, kHalfWidth * kHalfHeight); \ + memset(dst, 0, kSamplePixels * BPC); \ + memset(ref, 1, kSamplePixels * BPC); \ + TYPE* dst_p = reinterpret_cast(dst); \ + TYPE* ref_p = reinterpret_cast(ref); \ + EXPECT_EQ(0, I420To##FMT(src_y, kWidth, src_u, kHalfWidth, src_v, \ + kHalfWidth, ref_p, kWidth, \ + ref_p + kWidth * kHeight, kUvStride, kWidth, \ + NEG kHeight)); \ + EXPECT_EQ(0, ConvertFromI420(src_y, kWidth, src_u, kHalfWidth, src_v, \ + kHalfWidth, dst, 0, kWidth, NEG kHeight, \ + FOURCC_##FMT)); \ + for (int i = 0; i < kSamplePixels; ++i) { \ + ASSERT_EQ(dst_p[i], ref_p[i]); \ + } \ + if (BPC == 2) { \ + EXPECT_EQ(-1, ConvertFromI420(src_y, kWidth, src_u, kHalfWidth, src_v, \ + kHalfWidth, dst, 1, kWidth, kHeight, \ + FOURCC_##FMT)); \ + } \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(dst); \ + free_aligned_buffer_page_end(ref); \ + } + +#define TESTCONVERTTOI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, W1280, N, \ + NEG) \ + TEST_F(LibYUVConvertTest, ConvertToI420_##FMT##N) { \ + const int kWidth = W1280; \ + const int kHeight = benchmark_height_; \ + const int kHalfWidth = SUBSAMPLE(kWidth, 2); \ + const int kHalfHeight = SUBSAMPLE(kHeight, 2); \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kYPixels = kWidth * kHeight; \ + const int kUvPixels = kUvStride * kUvHeight; \ + align_buffer_page_end(src, (kYPixels + kUvPixels) * BPC); \ + align_buffer_page_end(dst_y, kWidth * kHeight); \ + align_buffer_page_end(dst_u, kHalfWidth * kHalfHeight); \ + align_buffer_page_end(dst_v, kHalfWidth * kHalfHeight); \ + align_buffer_page_end(ref_y, kWidth * kHeight); \ + align_buffer_page_end(ref_u, kHalfWidth * kHalfHeight); \ + align_buffer_page_end(ref_v, kHalfWidth * kHalfHeight); \ + TYPE* src_p = reinterpret_cast(src); \ + MemRandomize(src, (kYPixels + kUvPixels) * BPC); \ + if (BPC == 2) { \ + for (int i = 0; i < kYPixels + kUvPixels; ++i) { \ + src_p[i] = static_cast(src_p[i] & 0xffc0); \ + } \ + } \ + memset(dst_y, 1, kWidth * kHeight); \ + memset(dst_u, 2, kHalfWidth * kHalfHeight); \ + memset(dst_v, 3, kHalfWidth * kHalfHeight); \ + memset(ref_y, 4, kWidth * kHeight); \ + memset(ref_u, 5, kHalfWidth * kHalfHeight); \ + memset(ref_v, 6, kHalfWidth * kHalfHeight); \ + EXPECT_EQ(0, FMT##ToI420(src_p, kWidth, src_p + kYPixels, kUvStride, \ + ref_y, kWidth, ref_u, kHalfWidth, ref_v, \ + kHalfWidth, kWidth, NEG kHeight)); \ + EXPECT_EQ(0, ConvertToI420(src, 0, dst_y, kWidth, dst_u, kHalfWidth, \ + dst_v, kHalfWidth, 0, 0, kWidth, NEG kHeight,\ + kWidth, kHeight, kRotate0, FOURCC_##FMT)); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + ASSERT_EQ(dst_y[i], ref_y[i]); \ + } \ + for (int i = 0; i < kHalfWidth * kHalfHeight; ++i) { \ + ASSERT_EQ(dst_u[i], ref_u[i]); \ + ASSERT_EQ(dst_v[i], ref_v[i]); \ + } \ + free_aligned_buffer_page_end(src); \ + free_aligned_buffer_page_end(dst_y); \ + free_aligned_buffer_page_end(dst_u); \ + free_aligned_buffer_page_end(dst_v); \ + free_aligned_buffer_page_end(ref_y); \ + free_aligned_buffer_page_end(ref_u); \ + free_aligned_buffer_page_end(ref_v); \ + } + +#if defined(ENABLE_FULL_TESTS) +#define TESTCONVERTFROMI420(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTFROMI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ + 1, _Any, +) \ + TESTCONVERTFROMI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -) \ + TESTCONVERTFROMI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#define TESTCONVERTTOI420(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTTOI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ + 1, _Any, +) \ + TESTCONVERTTOI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -) \ + TESTCONVERTTOI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#else +#define TESTCONVERTFROMI420(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTFROMI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#define TESTCONVERTTOI420(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TESTCONVERTTOI420I(FMT, TYPE, BPC, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +) +#endif + +TESTCONVERTFROMI420(P010, uint16_t, 2, 2, 2) +TESTCONVERTFROMI420(P210, uint16_t, 2, 2, 1) +TESTCONVERTFROMI420(P410, uint16_t, 2, 1, 1) +TESTCONVERTTOI420(P010, uint16_t, 2, 2, 2) +TESTCONVERTTOI420(P210, uint16_t, 2, 2, 1) +TESTCONVERTTOI420(P410, uint16_t, 2, 1, 1) + +#define TESTCONVERTTOI420SMOKE(FMT, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TEST_F(LibYUVConvertTest, ConvertToI420_##FMT) { \ + const int kWidth = 64; \ + const int kHeight = 48; \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kSampleSize = \ + (kWidth * kHeight + kUvStride * kUvHeight) * BPC; \ + align_buffer_page_end(src, kSampleSize); \ + align_buffer_page_end(dst_y, kWidth * kHeight); \ + align_buffer_page_end(dst_u, (kWidth / 2) * (kHeight / 2)); \ + align_buffer_page_end(dst_v, (kWidth / 2) * (kHeight / 2)); \ + MemRandomize(src, kSampleSize); \ + memset(dst_y, 0, kWidth * kHeight); \ + memset(dst_u, 0, (kWidth / 2) * (kHeight / 2)); \ + memset(dst_v, 0, (kWidth / 2) * (kHeight / 2)); \ + EXPECT_EQ(0, ConvertToI420(src, kSampleSize, dst_y, kWidth, dst_u, \ + kWidth / 2, dst_v, kWidth / 2, 0, 0, kWidth, \ + kHeight, kWidth, kHeight, kRotate0, \ + FOURCC_##FMT)); \ + free_aligned_buffer_page_end(src); \ + free_aligned_buffer_page_end(dst_y); \ + free_aligned_buffer_page_end(dst_u); \ + free_aligned_buffer_page_end(dst_v); \ + } + +#define TESTCONVERTFROMI420SMOKE(FMT, BPC, SUBSAMP_X, SUBSAMP_Y) \ + TEST_F(LibYUVConvertTest, ConvertFromI420_##FMT) { \ + const int kWidth = 64; \ + const int kHeight = 48; \ + const int kUvStride = \ + (SUBSAMP_X == 1) ? kWidth * 2 : ((kWidth + 1) & ~1); \ + const int kUvHeight = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kDstSize = (kWidth * kHeight + kUvStride * kUvHeight) * BPC; \ + align_buffer_page_end(src_y, kWidth * kHeight); \ + align_buffer_page_end(src_u, (kWidth / 2) * (kHeight / 2)); \ + align_buffer_page_end(src_v, (kWidth / 2) * (kHeight / 2)); \ + align_buffer_page_end(dst, kDstSize); \ + MemRandomize(src_y, kWidth * kHeight); \ + MemRandomize(src_u, (kWidth / 2) * (kHeight / 2)); \ + MemRandomize(src_v, (kWidth / 2) * (kHeight / 2)); \ + memset(dst, 0, kDstSize); \ + EXPECT_EQ(0, ConvertFromI420(src_y, kWidth, src_u, kWidth / 2, src_v, \ + kWidth / 2, dst, kWidth * BPC, kWidth, \ + kHeight, FOURCC_##FMT)); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(dst); \ + } + +TESTCONVERTTOI420SMOKE(NV16, 1, 2, 1) +TESTCONVERTTOI420SMOKE(NV24, 1, 1, 1) +TESTCONVERTFROMI420SMOKE(NV16, 1, 2, 1) +TESTCONVERTFROMI420SMOKE(NV24, 1, 1, 1) #define TESTBPTOBPI(SRC_FMT_PLANAR, SRC_T, SRC_BPC, SRC_SUBSAMP_X, \ SRC_SUBSAMP_Y, FMT_PLANAR, DST_T, DST_BPC, DST_SUBSAMP_X, \ @@ -1039,6 +1233,62 @@ TESTATOBP16FROM8(ARGB, P010, NV12, 2, 2) TESTATOBP16FROM8(ARGB, P210, NV16, 2, 1) TESTATOBP16FROM8(ARGB, P410, NV24, 1, 1) +#define TESTATOBP16TRANSITIVE(FMT_A, FMT_I, FMT_P, SUBSAMP_X, SUBSAMP_Y) \ + TEST_F(LibYUVConvertTest, Test##FMT_A##To##FMT_P##Transitive) { \ + const int kWidth = benchmark_width_ | 1; \ + const int kHeight = benchmark_height_; \ + const int kWidthUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kHeightUV = SUBSAMPLE(kHeight, SUBSAMP_Y); \ + const int kYSize = kWidth * kHeight; \ + const int kUVSize = kWidthUV * kHeightUV; \ + align_buffer_page_end(src_argb, kYSize * 4); \ + align_buffer_page_end(dst_p_y, kYSize * 2); \ + align_buffer_page_end(dst_p_uv, kUVSize * 2 * 2); \ + align_buffer_page_end(ref_y, kYSize); \ + align_buffer_page_end(ref_u, kUVSize); \ + align_buffer_page_end(ref_v, kUVSize); \ + align_buffer_page_end(ref_p_y, kYSize * 2); \ + align_buffer_page_end(ref_p_uv, kUVSize * 2 * 2); \ + uint16_t* dst_p_y16 = reinterpret_cast(dst_p_y); \ + uint16_t* dst_p_uv16 = reinterpret_cast(dst_p_uv); \ + uint16_t* ref_p_y16 = reinterpret_cast(ref_p_y); \ + uint16_t* ref_p_uv16 = reinterpret_cast(ref_p_uv); \ + MemRandomize(src_argb, kYSize * 4); \ + memset(dst_p_y, 1, kYSize * 2); \ + memset(dst_p_uv, 2, kUVSize * 2 * 2); \ + memset(ref_y, 3, kYSize); \ + memset(ref_u, 4, kUVSize); \ + memset(ref_v, 5, kUVSize); \ + memset(ref_p_y, 6, kYSize * 2); \ + memset(ref_p_uv, 7, kUVSize * 2 * 2); \ + EXPECT_EQ(0, FMT_A##To##FMT_P(src_argb, kWidth * 4, dst_p_y16, kWidth, \ + dst_p_uv16, kWidthUV * 2, kWidth, kHeight)); \ + EXPECT_EQ(0, \ + FMT_A##To##FMT_I(src_argb, kWidth * 4, ref_y, kWidth, ref_u, \ + kWidthUV, ref_v, kWidthUV, kWidth, kHeight)); \ + EXPECT_EQ(0, FMT_I##To##FMT_P(ref_y, kWidth, ref_u, kWidthUV, ref_v, \ + kWidthUV, ref_p_y16, kWidth, ref_p_uv16, \ + kWidthUV * 2, kWidth, kHeight)); \ + for (int i = 0; i < kYSize; ++i) { \ + ASSERT_EQ(dst_p_y16[i], ref_p_y16[i]); \ + } \ + for (int i = 0; i < kUVSize * 2; ++i) { \ + ASSERT_EQ(dst_p_uv16[i], ref_p_uv16[i]); \ + } \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_p_y); \ + free_aligned_buffer_page_end(dst_p_uv); \ + free_aligned_buffer_page_end(ref_y); \ + free_aligned_buffer_page_end(ref_u); \ + free_aligned_buffer_page_end(ref_v); \ + free_aligned_buffer_page_end(ref_p_y); \ + free_aligned_buffer_page_end(ref_p_uv); \ + } + +TESTATOBP16TRANSITIVE(ARGB, I420, P010, 2, 2) +TESTATOBP16TRANSITIVE(ARGB, I422, P210, 2, 1) +TESTATOBP16TRANSITIVE(ARGB, I444, P410, 1, 1) + #if !defined(LEAN_TESTS) #ifdef HAVE_JPEG @@ -2081,100 +2331,6 @@ TEST_F(LibYUVConvertTest, NV12Crop) { free_aligned_buffer_page_end(src_y); } -TEST_F(LibYUVConvertTest, ConvertToI420_NV16) { - const int kWidth = 64; - const int kHeight = 48; - const int sample_size = kWidth * kHeight * 2; - align_buffer_page_end(src, sample_size); - align_buffer_page_end(dst_y, kWidth * kHeight); - align_buffer_page_end(dst_u, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(dst_v, (kWidth / 2) * (kHeight / 2)); - MemRandomize(src, sample_size); - memset(dst_y, 0, kWidth * kHeight); - memset(dst_u, 0, (kWidth / 2) * (kHeight / 2)); - memset(dst_v, 0, (kWidth / 2) * (kHeight / 2)); - - int r = ConvertToI420(src, sample_size, dst_y, kWidth, dst_u, kWidth / 2, - dst_v, kWidth / 2, 0, 0, kWidth, kHeight, kWidth, - kHeight, kRotate0, FOURCC_NV16); - EXPECT_EQ(0, r); - - free_aligned_buffer_page_end(src); - free_aligned_buffer_page_end(dst_y); - free_aligned_buffer_page_end(dst_u); - free_aligned_buffer_page_end(dst_v); -} - -TEST_F(LibYUVConvertTest, ConvertToI420_NV24) { - const int kWidth = 64; - const int kHeight = 48; - const int sample_size = kWidth * kHeight * 3; - align_buffer_page_end(src, sample_size); - align_buffer_page_end(dst_y, kWidth * kHeight); - align_buffer_page_end(dst_u, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(dst_v, (kWidth / 2) * (kHeight / 2)); - MemRandomize(src, sample_size); - memset(dst_y, 0, kWidth * kHeight); - memset(dst_u, 0, (kWidth / 2) * (kHeight / 2)); - memset(dst_v, 0, (kWidth / 2) * (kHeight / 2)); - - int r = ConvertToI420(src, sample_size, dst_y, kWidth, dst_u, kWidth / 2, - dst_v, kWidth / 2, 0, 0, kWidth, kHeight, kWidth, - kHeight, kRotate0, FOURCC_NV24); - EXPECT_EQ(0, r); - - free_aligned_buffer_page_end(src); - free_aligned_buffer_page_end(dst_y); - free_aligned_buffer_page_end(dst_u); - free_aligned_buffer_page_end(dst_v); -} - -TEST_F(LibYUVConvertTest, ConvertFromI420_NV16) { - const int kWidth = 64; - const int kHeight = 48; - const int dst_size = kWidth * kHeight * 2; - align_buffer_page_end(src_y, kWidth * kHeight); - align_buffer_page_end(src_u, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(src_v, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(dst, dst_size); - MemRandomize(src_y, kWidth * kHeight); - MemRandomize(src_u, (kWidth / 2) * (kHeight / 2)); - MemRandomize(src_v, (kWidth / 2) * (kHeight / 2)); - memset(dst, 0, dst_size); - - int r = ConvertFromI420(src_y, kWidth, src_u, kWidth / 2, src_v, kWidth / 2, - dst, kWidth, kWidth, kHeight, FOURCC_NV16); - EXPECT_EQ(0, r); - - free_aligned_buffer_page_end(src_y); - free_aligned_buffer_page_end(src_u); - free_aligned_buffer_page_end(src_v); - free_aligned_buffer_page_end(dst); -} - -TEST_F(LibYUVConvertTest, ConvertFromI420_NV24) { - const int kWidth = 64; - const int kHeight = 48; - const int dst_size = kWidth * kHeight * 3; - align_buffer_page_end(src_y, kWidth * kHeight); - align_buffer_page_end(src_u, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(src_v, (kWidth / 2) * (kHeight / 2)); - align_buffer_page_end(dst, dst_size); - MemRandomize(src_y, kWidth * kHeight); - MemRandomize(src_u, (kWidth / 2) * (kHeight / 2)); - MemRandomize(src_v, (kWidth / 2) * (kHeight / 2)); - memset(dst, 0, dst_size); - - int r = ConvertFromI420(src_y, kWidth, src_u, kWidth / 2, src_v, kWidth / 2, - dst, kWidth, kWidth, kHeight, FOURCC_NV24); - EXPECT_EQ(0, r); - - free_aligned_buffer_page_end(src_y); - free_aligned_buffer_page_end(src_u); - free_aligned_buffer_page_end(src_v); - free_aligned_buffer_page_end(dst); -} - TEST_F(LibYUVConvertTest, I420CropOddY) { const int SUBSAMP_X = 2; const int SUBSAMP_Y = 2; diff --git a/unit_test/scale_test.cc b/unit_test/scale_test.cc index 323094f3f..e634b6c0f 100644 --- a/unit_test/scale_test.cc +++ b/unit_test/scale_test.cc @@ -740,21 +740,37 @@ static int I444TestFilter_16(int src_width, } // Test scaling with C vs Opt and return maximum pixel difference. 0 = exact. -static int NV12TestFilter(int src_width, - int src_height, - int dst_width, - int dst_height, - FilterMode f, - int benchmark_iterations, - int disable_cpu_flags, - int benchmark_cpu_info) { +static int NVTestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int subsample_x, + int subsample_y, + int (*NVScale)(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering), + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { if (!SizeValid(src_width, src_height, dst_width, dst_height)) { return 0; } int i, j; - int src_width_uv = (Abs(src_width) + 1) >> 1; - int src_height_uv = (Abs(src_height) + 1) >> 1; + int src_width_uv = subsample_x ? ((Abs(src_width) + 1) >> 1) : Abs(src_width); + int src_height_uv = + subsample_y ? ((Abs(src_height) + 1) >> 1) : Abs(src_height); int64_t src_y_plane_size = (Abs(src_width)) * (Abs(src_height)); int64_t src_uv_plane_size = (src_width_uv) * (src_height_uv) * 2; @@ -771,8 +787,8 @@ static int NV12TestFilter(int src_width, MemRandomize(src_y, src_y_plane_size); MemRandomize(src_uv, src_uv_plane_size); - int dst_width_uv = (dst_width + 1) >> 1; - int dst_height_uv = (dst_height + 1) >> 1; + int dst_width_uv = subsample_x ? ((dst_width + 1) >> 1) : dst_width; + int dst_height_uv = subsample_y ? ((dst_height + 1) >> 1) : dst_height; int64_t dst_y_plane_size = (dst_width) * (dst_height); int64_t dst_uv_plane_size = (dst_width_uv) * (dst_height_uv) * 2; @@ -791,17 +807,17 @@ static int NV12TestFilter(int src_width, MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. double c_time = get_time(); - NV12Scale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, src_height, - dst_y_c, dst_stride_y, dst_uv_c, dst_stride_uv, dst_width, - dst_height, f); + NVScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, src_height, + dst_y_c, dst_stride_y, dst_uv_c, dst_stride_uv, dst_width, dst_height, + f); c_time = (get_time() - c_time); MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. double opt_time = get_time(); for (i = 0; i < benchmark_iterations; ++i) { - NV12Scale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, src_height, - dst_y_opt, dst_stride_y, dst_uv_opt, dst_stride_uv, dst_width, - dst_height, f); + NVScale(src_y, src_stride_y, src_uv, src_stride_uv, src_width, src_height, + dst_y_opt, dst_stride_y, dst_uv_opt, dst_stride_uv, dst_width, + dst_height, f); } opt_time = (get_time() - opt_time) / benchmark_iterations; // Report performance of C vs OPT. @@ -843,6 +859,219 @@ static int NV12TestFilter(int src_width, return max_diff; } +static int NV12TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return NVTestFilter(src_width, src_height, dst_width, dst_height, f, 1, 1, + NV12Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + +static int NV16TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return NVTestFilter(src_width, src_height, dst_width, dst_height, f, 1, 0, + NV16Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + +static int NV24TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return NVTestFilter(src_width, src_height, dst_width, dst_height, f, 0, 0, + NV24Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + +static int PxxxTestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int subsample_x, + int subsample_y, + int (*PxxxScale)(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_uv, + int src_stride_uv, + int src_width, + int src_height, + uint16_t* dst_y, + int dst_stride_y, + uint16_t* dst_uv, + int dst_stride_uv, + int dst_width, + int dst_height, + enum FilterMode filtering), + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + if (!SizeValid(src_width, src_height, dst_width, dst_height)) { + return 0; + } + + int i, j; + int src_width_uv = subsample_x ? ((Abs(src_width) + 1) >> 1) : Abs(src_width); + int src_height_uv = + subsample_y ? ((Abs(src_height) + 1) >> 1) : Abs(src_height); + + int64_t src_y_plane_size = (Abs(src_width)) * (Abs(src_height)); + int64_t src_uv_plane_size = (src_width_uv) * (src_height_uv) * 2; + + int src_stride_y = Abs(src_width); + int src_stride_uv = src_width_uv * 2; + + align_buffer_page_end(src_y, src_y_plane_size * 2); + align_buffer_page_end(src_uv, src_uv_plane_size * 2); + if (!src_y || !src_uv) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + uint16_t* p_src_y = reinterpret_cast(src_y); + uint16_t* p_src_uv = reinterpret_cast(src_uv); + MemRandomize(src_y, src_y_plane_size * 2); + MemRandomize(src_uv, src_uv_plane_size * 2); + for (i = 0; i < src_y_plane_size; ++i) { + p_src_y[i] = static_cast(p_src_y[i] & 0xffc0); + } + for (i = 0; i < src_uv_plane_size; ++i) { + p_src_uv[i] = static_cast(p_src_uv[i] & 0xffc0); + } + + int dst_width_uv = subsample_x ? ((dst_width + 1) >> 1) : dst_width; + int dst_height_uv = subsample_y ? ((dst_height + 1) >> 1) : dst_height; + + int64_t dst_y_plane_size = (dst_width) * (dst_height); + int64_t dst_uv_plane_size = (dst_width_uv) * (dst_height_uv) * 2; + + int dst_stride_y = dst_width; + int dst_stride_uv = dst_width_uv * 2; + + align_buffer_page_end(dst_y_c, dst_y_plane_size * 2); + align_buffer_page_end(dst_uv_c, dst_uv_plane_size * 2); + align_buffer_page_end(dst_y_opt, dst_y_plane_size * 2); + align_buffer_page_end(dst_uv_opt, dst_uv_plane_size * 2); + if (!dst_y_c || !dst_uv_c || !dst_y_opt || !dst_uv_opt) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + uint16_t* p_dst_y_c = reinterpret_cast(dst_y_c); + uint16_t* p_dst_uv_c = reinterpret_cast(dst_uv_c); + uint16_t* p_dst_y_opt = reinterpret_cast(dst_y_opt); + uint16_t* p_dst_uv_opt = reinterpret_cast(dst_uv_opt); + + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + double c_time = get_time(); + PxxxScale(p_src_y, src_stride_y, p_src_uv, src_stride_uv, src_width, + src_height, p_dst_y_c, dst_stride_y, p_dst_uv_c, dst_stride_uv, + dst_width, dst_height, f); + c_time = (get_time() - c_time); + + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + double opt_time = get_time(); + for (i = 0; i < benchmark_iterations; ++i) { + PxxxScale(p_src_y, src_stride_y, p_src_uv, src_stride_uv, src_width, + src_height, p_dst_y_opt, dst_stride_y, p_dst_uv_opt, + dst_stride_uv, dst_width, dst_height, f); + } + opt_time = (get_time() - opt_time) / benchmark_iterations; + printf("filter %d - %8d us C - %8d us OPT\n", f, + static_cast(c_time * 1e6), static_cast(opt_time * 1e6)); + + int max_diff = 0; + for (i = 0; i < dst_height; ++i) { + for (j = 0; j < dst_width; ++j) { + uint16_t c = p_dst_y_c[(i * dst_stride_y) + j]; + uint16_t opt = p_dst_y_opt[(i * dst_stride_y) + j]; + // Scaled output is converted back to canonical 10-bit MSB. + if ((c & 0x3f) || (opt & 0x3f)) { + max_diff = 999; + } + int abs_diff = Abs((c >> 6) - (opt >> 6)); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + for (i = 0; i < dst_height_uv; ++i) { + for (j = 0; j < (dst_width_uv * 2); ++j) { + uint16_t c = p_dst_uv_c[(i * dst_stride_uv) + j]; + uint16_t opt = p_dst_uv_opt[(i * dst_stride_uv) + j]; + // MergeUVPlane_16 depth=10 must produce canonical 10-bit MSB samples. + if ((c & 0x3f) || (opt & 0x3f)) { + max_diff = 999; + } + int abs_diff = Abs((c >> 6) - (opt >> 6)); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + free_aligned_buffer_page_end(dst_y_c); + free_aligned_buffer_page_end(dst_uv_c); + free_aligned_buffer_page_end(dst_y_opt); + free_aligned_buffer_page_end(dst_uv_opt); + free_aligned_buffer_page_end(src_y); + free_aligned_buffer_page_end(src_uv); + + return max_diff; +} + +static int P010TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return PxxxTestFilter(src_width, src_height, dst_width, dst_height, f, 1, 1, + P010Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + +static int P210TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return PxxxTestFilter(src_width, src_height, dst_width, dst_height, f, 1, 0, + P210Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + +static int P410TestFilter(int src_width, + int src_height, + int dst_width, + int dst_height, + FilterMode f, + int benchmark_iterations, + int disable_cpu_flags, + int benchmark_cpu_info) { + return PxxxTestFilter(src_width, src_height, dst_width, dst_height, f, 0, 0, + P410Scale, benchmark_iterations, disable_cpu_flags, + benchmark_cpu_info); +} + // The following adjustments in dimensions ensure the scale factor will be // exactly achieved. // 2 is chroma subsample. @@ -889,6 +1118,46 @@ static int NV12TestFilter(int src_width, kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ benchmark_cpu_info_); \ ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV16ScaleDownBy##name##_##filter) { \ + int diff = NV16TestFilter( \ + SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV24ScaleDownBy##name##_##filter) { \ + int diff = NV24TestFilter( \ + SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P010ScaleDownBy##name##_##filter) { \ + int diff = P010TestFilter( \ + SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P210ScaleDownBy##name##_##filter) { \ + int diff = P210TestFilter( \ + SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P410ScaleDownBy##name##_##filter) { \ + int diff = P410TestFilter( \ + SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ } // Test a scale factor with all 4 filters. Expect unfiltered to be exact, but @@ -973,6 +1242,36 @@ TEST_FACTOR(3, 1, 3, 0) disable_cpu_flags_, benchmark_cpu_info_); \ ASSERT_LE(diff, max_diff); \ } \ + TEST_F(LibYUVScaleTest, NV16##name##To##width##x##height##_##filter) { \ + int diff = NV16TestFilter(benchmark_width_, benchmark_height_, width, \ + height, kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV24##name##To##width##x##height##_##filter) { \ + int diff = NV24TestFilter(benchmark_width_, benchmark_height_, width, \ + height, kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P010##name##To##width##x##height##_##filter) { \ + int diff = P010TestFilter(benchmark_width_, benchmark_height_, width, \ + height, kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P210##name##To##width##x##height##_##filter) { \ + int diff = P210TestFilter(benchmark_width_, benchmark_height_, width, \ + height, kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P410##name##To##width##x##height##_##filter) { \ + int diff = P410TestFilter(benchmark_width_, benchmark_height_, width, \ + height, kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ TEST_F(LibYUVScaleTest, I420##name##From##width##x##height##_##filter) { \ int diff = I420TestFilter(width, height, Abs(benchmark_width_), \ Abs(benchmark_height_), kFilter##filter, \ @@ -1025,6 +1324,41 @@ TEST_FACTOR(3, 1, 3, 0) benchmark_iterations_, disable_cpu_flags_, \ benchmark_cpu_info_); \ ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV16##name##From##width##x##height##_##filter) { \ + int diff = NV16TestFilter(width, height, Abs(benchmark_width_), \ + Abs(benchmark_height_), kFilter##filter, \ + benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV24##name##From##width##x##height##_##filter) { \ + int diff = NV24TestFilter(width, height, Abs(benchmark_width_), \ + Abs(benchmark_height_), kFilter##filter, \ + benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P010##name##From##width##x##height##_##filter) { \ + int diff = P010TestFilter(width, height, Abs(benchmark_width_), \ + Abs(benchmark_height_), kFilter##filter, \ + benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P210##name##From##width##x##height##_##filter) { \ + int diff = P210TestFilter(width, height, Abs(benchmark_width_), \ + Abs(benchmark_height_), kFilter##filter, \ + benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P410##name##From##width##x##height##_##filter) { \ + int diff = P410TestFilter(width, height, Abs(benchmark_width_), \ + Abs(benchmark_height_), kFilter##filter, \ + benchmark_iterations_, disable_cpu_flags_, \ + benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ } #ifndef DISABLE_SLOW_TESTS @@ -1111,6 +1445,41 @@ TEST_SCALETO(Scale, 1080, 1920) // for rotated phones kFilter##filter, benchmark_iterations_, \ disable_cpu_flags_, benchmark_cpu_info_); \ ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV16##name##SwapXY_##filter) { \ + int diff = NV16TestFilter(benchmark_width_, benchmark_height_, \ + benchmark_height_, benchmark_width_, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, NV24##name##SwapXY_##filter) { \ + int diff = NV24TestFilter(benchmark_width_, benchmark_height_, \ + benchmark_height_, benchmark_width_, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P010##name##SwapXY_##filter) { \ + int diff = P010TestFilter(benchmark_width_, benchmark_height_, \ + benchmark_height_, benchmark_width_, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P210##name##SwapXY_##filter) { \ + int diff = P210TestFilter(benchmark_width_, benchmark_height_, \ + benchmark_height_, benchmark_width_, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, P410##name##SwapXY_##filter) { \ + int diff = P410TestFilter(benchmark_width_, benchmark_height_, \ + benchmark_height_, benchmark_width_, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + ASSERT_LE(diff, max_diff); \ } // Test scale to a specified size with all 4 filters. diff --git a/unit_test/video_common_test.cc b/unit_test/video_common_test.cc index 2577ef7f7..ca91246b6 100644 --- a/unit_test/video_common_test.cc +++ b/unit_test/video_common_test.cc @@ -90,6 +90,7 @@ TEST_F(LibYUVBaseTest, TestFourCC) { ASSERT_TRUE(TestValidFourCC(FOURCC_I210, FOURCC_BPP_I210)); ASSERT_TRUE(TestValidFourCC(FOURCC_P010, FOURCC_BPP_P010)); ASSERT_TRUE(TestValidFourCC(FOURCC_P210, FOURCC_BPP_P210)); + ASSERT_TRUE(TestValidFourCC(FOURCC_P410, FOURCC_BPP_P410)); ASSERT_TRUE(TestValidFourCC(FOURCC_MJPG, FOURCC_BPP_MJPG)); ASSERT_TRUE(TestValidFourCC(FOURCC_YV12, FOURCC_BPP_YV12)); ASSERT_TRUE(TestValidFourCC(FOURCC_YV16, FOURCC_BPP_YV16));