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 <fbarchard@google.com>
Reviewed-by: richard winterton <rrwinterton@gmail.com>
This commit is contained in:
Sida Zhu 2026-09-15 09:56:04 +08:00 committed by Frank Barchard
parent eae15e5fdd
commit dab3111c8a
16 changed files with 2377 additions and 196 deletions

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -11,6 +11,7 @@
#include "libyuv/convert_from.h"
#include <limits.h>
#include <stdint.h>
#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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -12,6 +12,7 @@
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#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

View File

@ -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<uint16_t*>(src_y); \
uint16_t* src_uv16 = reinterpret_cast<uint16_t*>(src_uv); \
uint16_t* plane_y16 = reinterpret_cast<uint16_t*>(plane_y); \
uint16_t* plane_u16 = reinterpret_cast<uint16_t*>(plane_u); \
uint16_t* plane_v16 = reinterpret_cast<uint16_t*>(plane_v); \
for (int i = 0; i < kPixels; ++i) { \
src_y16[i] = static_cast<uint16_t>((fastrand() & 0x3ff) << 6); \
} \
for (int i = 0; i < kPixels * 2; ++i) { \
src_uv16[i] = static_cast<uint16_t>((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<TYPE*>(src); \
MemRandomize(src, (kYPixels + kUvPixels) * BPC); \
if (BPC == 2) { \
for (int i = 0; i < kYPixels + kUvPixels; ++i) { \
src_p[i] = static_cast<TYPE>(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

View File

@ -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<TYPE*>(dst); \
TYPE* ref_p = reinterpret_cast<TYPE*>(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<TYPE*>(src); \
MemRandomize(src, (kYPixels + kUvPixels) * BPC); \
if (BPC == 2) { \
for (int i = 0; i < kYPixels + kUvPixels; ++i) { \
src_p[i] = static_cast<TYPE>(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<uint16_t*>(dst_p_y); \
uint16_t* dst_p_uv16 = reinterpret_cast<uint16_t*>(dst_p_uv); \
uint16_t* ref_p_y16 = reinterpret_cast<uint16_t*>(ref_p_y); \
uint16_t* ref_p_uv16 = reinterpret_cast<uint16_t*>(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;

View File

@ -740,11 +740,26 @@ 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,
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) {
@ -753,8 +768,9 @@ static int NV12TestFilter(int src_width,
}
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,15 +807,15 @@ 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,
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);
}
@ -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<uint16_t*>(src_y);
uint16_t* p_src_uv = reinterpret_cast<uint16_t*>(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<uint16_t>(p_src_y[i] & 0xffc0);
}
for (i = 0; i < src_uv_plane_size; ++i) {
p_src_uv[i] = static_cast<uint16_t>(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<uint16_t*>(dst_y_c);
uint16_t* p_dst_uv_c = reinterpret_cast<uint16_t*>(dst_uv_c);
uint16_t* p_dst_y_opt = reinterpret_cast<uint16_t*>(dst_y_opt);
uint16_t* p_dst_uv_opt = reinterpret_cast<uint16_t*>(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<int>(c_time * 1e6), static_cast<int>(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.

View File

@ -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));