Add ARGBToI4xxMatrix variants

This was implemented by Gemini followed by manual review and some
tweaking for style. The 601 and JPEG constants are fully verified
against the existing non-matrix implementations. On x86 the C-only
versions appear to be about 25% slower than the optimized ones.

Bug: libyuv:42280902
Change-Id: Ia5b7cb499bad5c76faec53f36086ebb18f2b530f
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/7512030
Reviewed-by: Frank Barchard <fbarchard@chromium.org>
Reviewed-by: Wan-Teh Chang <wtc@google.com>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
This commit is contained in:
Dale Curtis 2026-03-03 23:52:03 +00:00 committed by libyuv LUCI CQ
parent 6067afde56
commit 30809ff64a
8 changed files with 579 additions and 0 deletions

View File

@ -831,6 +831,20 @@ int ARGBToI420(const uint8_t* src_argb,
int width,
int height);
// ARGB little endian (bgra in memory) to I420 with matrix.
LIBYUV_API
int ARGBToI420Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height);
// Convert ARGB to I420 with Alpha
LIBYUV_API
int ARGBToI420Alpha(const uint8_t* src_argb,

View File

@ -29,6 +29,15 @@ LIBYUV_API extern const struct YuvConstants kYuvF709Constants; // BT.709 full
LIBYUV_API extern const struct YuvConstants kYuv2020Constants; // BT.2020
LIBYUV_API extern const struct YuvConstants kYuvV2020Constants; // BT.2020 full
// Conversion matrix for RGB to YUV
LIBYUV_API extern const struct ArgbConstants kArgbI601Constants; // BT.601
LIBYUV_API extern const struct ArgbConstants kArgbJPEGConstants; // BT.601 full
LIBYUV_API extern const struct ArgbConstants kArgbH709Constants; // BT.709
LIBYUV_API extern const struct ArgbConstants kArgbF709Constants; // BT.709 full
LIBYUV_API extern const struct ArgbConstants kArgbU2020Constants; // BT.2020
LIBYUV_API extern const struct ArgbConstants
kArgbV2020Constants; // BT.2020 full
// Conversion matrix for YVU to BGR
LIBYUV_API extern const struct YuvConstants kYvuI601Constants; // BT.601
LIBYUV_API extern const struct YuvConstants kYvuJPEGConstants; // BT.601 full

View File

@ -153,6 +153,20 @@ int ARGBToI444(const uint8_t* src_argb,
int width,
int height);
// Convert ARGB To I444 with matrix.
LIBYUV_API
int ARGBToI444Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height);
// Convert ARGB to AR64.
LIBYUV_API
int ARGBToAR64(const uint8_t* src_argb,
@ -190,6 +204,20 @@ int ARGBToI422(const uint8_t* src_argb,
int width,
int height);
// Convert ARGB To I422 with matrix.
LIBYUV_API
int ARGBToI422Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height);
// Convert ARGB To I420. (also in convert.h)
LIBYUV_API
int ARGBToI420(const uint8_t* src_argb,

View File

@ -971,6 +971,14 @@ typedef uint32_t ulvec32[8];
typedef uint8_t ulvec8[32];
#endif
struct ArgbConstants {
uint8_t kRGBToY[32];
int16_t kRGBToU[16];
int16_t kRGBToV[16];
uint16_t kAddY[16];
uint16_t kAddUV[16];
};
#if defined(__aarch64__) || defined(__arm__) || defined(__riscv)
// This struct is for ARM and RISC-V color conversion.
struct YuvConstants {
@ -2074,6 +2082,22 @@ void RAWToYJRow_LASX(const uint8_t* src_raw, uint8_t* dst_yj, int width);
void ARGBToYRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width);
void ARGBToYJRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width);
void ARGBToYMatrixRow_C(const uint8_t* src_argb,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c);
void ARGBToUVMatrixRow_C(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c);
void ARGBToUV444MatrixRow_C(const uint8_t* src_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c);
void ABGRToYJRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width);
void RGBAToYJRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width);
void BGRAToYRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width);

View File

@ -2144,6 +2144,55 @@ int ARGBToI420(const uint8_t* src_argb,
return 0;
}
// ARGB little endian (bgra in memory) to I420 with matrix.
LIBYUV_API
int ARGBToI420Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height) {
int y;
void (*ARGBToYMatrixRow)(const uint8_t* src_argb, uint8_t* dst_y, int width,
const struct ArgbConstants* c) = ARGBToYMatrixRow_C;
void (*ARGBToUVMatrixRow)(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_u, uint8_t* dst_v, int width,
const struct ArgbConstants* c) =
ARGBToUVMatrixRow_C;
if (!src_argb || !dst_y || !dst_u || !dst_v || !argbconstants || width <= 0 ||
height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_argb = src_argb + (height - 1) * src_stride_argb;
src_stride_argb = -src_stride_argb;
}
for (y = 0; y < height - 1; y += 2) {
ARGBToUVMatrixRow(src_argb, src_stride_argb, dst_u, dst_v, width,
argbconstants);
ARGBToYMatrixRow(src_argb, dst_y, width, argbconstants);
ARGBToYMatrixRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width,
argbconstants);
src_argb += src_stride_argb * 2;
dst_y += dst_stride_y * 2;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
if (height & 1) {
ARGBToUVMatrixRow(src_argb, 0, dst_u, dst_v, width, argbconstants);
ARGBToYMatrixRow(src_argb, dst_y, width, argbconstants);
}
return 0;
}
#ifdef USE_EXTRACTALPHA
// Convert ARGB to I420 with Alpha
// The following version calls ARGBExtractAlpha on the full image.

View File

@ -165,6 +165,48 @@ int ARGBToI444(const uint8_t* src_argb,
return 0;
}
// Convert ARGB To I444 with matrix.
LIBYUV_API
int ARGBToI444Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height) {
int y;
void (*ARGBToYMatrixRow)(const uint8_t* src_argb, uint8_t* dst_y, int width,
const struct ArgbConstants* c) = ARGBToYMatrixRow_C;
void (*ARGBToUV444MatrixRow)(const uint8_t* src_argb, uint8_t* dst_u,
uint8_t* dst_v, int width,
const struct ArgbConstants* c) =
ARGBToUV444MatrixRow_C;
if (!src_argb || !dst_y || !dst_u || !dst_v || !argbconstants || width <= 0 ||
height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_argb = src_argb + (height - 1) * src_stride_argb;
src_stride_argb = -src_stride_argb;
}
for (y = 0; y < height; ++y) {
ARGBToYMatrixRow(src_argb, dst_y, width, argbconstants);
ARGBToUV444MatrixRow(src_argb, dst_u, dst_v, width, argbconstants);
src_argb += src_stride_argb;
dst_y += dst_stride_y;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
}
// ARGB little endian (bgra in memory) to I422
LIBYUV_API
int ARGBToI422(const uint8_t* src_argb,
@ -324,6 +366,48 @@ int ARGBToI422(const uint8_t* src_argb,
return 0;
}
// Convert ARGB To I422 with matrix.
LIBYUV_API
int ARGBToI422Matrix(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
const struct ArgbConstants* argbconstants,
int width,
int height) {
int y;
void (*ARGBToYMatrixRow)(const uint8_t* src_argb, uint8_t* dst_y, int width,
const struct ArgbConstants* c) = ARGBToYMatrixRow_C;
void (*ARGBToUVMatrixRow)(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_u, uint8_t* dst_v, int width,
const struct ArgbConstants* c) =
ARGBToUVMatrixRow_C;
if (!src_argb || !dst_y || !dst_u || !dst_v || !argbconstants || width <= 0 ||
height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_argb = src_argb + (height - 1) * src_stride_argb;
src_stride_argb = -src_stride_argb;
}
for (y = 0; y < height; ++y) {
ARGBToUVMatrixRow(src_argb, 0, dst_u, dst_v, width, argbconstants);
ARGBToYMatrixRow(src_argb, dst_y, width, argbconstants);
src_argb += src_stride_argb;
dst_y += dst_stride_y;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
}
LIBYUV_API
int ARGBToNV12(const uint8_t* src_argb,
int src_stride_argb,

View File

@ -756,6 +756,92 @@ MAKEROWYJ(RGB24, 2, 1, 0, 3)
MAKEROWYJ(RAW, 0, 1, 2, 3)
#undef MAKEROWYJ
static __inline uint8_t RGBToYMatrix(uint8_t r,
uint8_t g,
uint8_t b,
const struct ArgbConstants* c) {
return (c->kRGBToY[2] * r + c->kRGBToY[1] * g + c->kRGBToY[0] * b +
c->kAddY[0]) >>
8;
}
static __inline uint8_t RGBToUMatrix(uint8_t r,
uint8_t g,
uint8_t b,
const struct ArgbConstants* c) {
return (c->kRGBToU[2] * r + c->kRGBToU[1] * g + c->kRGBToU[0] * b +
c->kAddUV[0]) >>
8;
}
static __inline uint8_t RGBToVMatrix(uint8_t r,
uint8_t g,
uint8_t b,
const struct ArgbConstants* c) {
return (c->kRGBToV[2] * r + c->kRGBToV[1] * g + c->kRGBToV[0] * b +
c->kAddUV[0]) >>
8;
}
void ARGBToYMatrixRow_C(const uint8_t* src_argb,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
int x;
for (x = 0; x < width; ++x) {
dst_y[0] = RGBToYMatrix(src_argb[2], src_argb[1], src_argb[0], c);
src_argb += 4;
dst_y += 1;
}
}
void ARGBToUVMatrixRow_C(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_argb1 = src_argb + src_stride_argb;
int x;
for (x = 0; x < width - 1; x += 2) {
uint8_t ab =
(src_argb[0] + src_argb[4] + src_argb1[0] + src_argb1[4] + 2) >> 2;
uint8_t ag =
(src_argb[1] + src_argb[5] + src_argb1[1] + src_argb1[5] + 2) >> 2;
uint8_t ar =
(src_argb[2] + src_argb[6] + src_argb1[2] + src_argb1[6] + 2) >> 2;
dst_u[0] = RGBToUMatrix(ar, ag, ab, c);
dst_v[0] = RGBToVMatrix(ar, ag, ab, c);
src_argb += 8;
src_argb1 += 8;
dst_u += 1;
dst_v += 1;
}
if (width & 1) {
uint8_t ab = (src_argb[0] + src_argb1[0] + 1) >> 1;
uint8_t ag = (src_argb[1] + src_argb1[1] + 1) >> 1;
uint8_t ar = (src_argb[2] + src_argb1[2] + 1) >> 1;
dst_u[0] = RGBToUMatrix(ar, ag, ab, c);
dst_v[0] = RGBToVMatrix(ar, ag, ab, c);
}
}
void ARGBToUV444MatrixRow_C(const uint8_t* src_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
int x;
for (x = 0; x < width; ++x) {
uint8_t ab = src_argb[0];
uint8_t ag = src_argb[1];
uint8_t ar = src_argb[2];
dst_u[0] = RGBToUMatrix(ar, ag, ab, c);
dst_v[0] = RGBToVMatrix(ar, ag, ab, c);
src_argb += 4;
dst_u += 1;
dst_v += 1;
}
}
void RGB565ToYRow_C(const uint8_t* src_rgb565, uint8_t* dst_y, int width) {
int x;
for (x = 0; x < width; ++x) {
@ -1399,6 +1485,15 @@ void J400ToARGBRow_C(const uint8_t* src_y, uint8_t* dst_argb, int width) {
{YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB}}
#endif
#define ARGBCONSTANTSBODY(RY, GY, BY, RU, GU, BU, RV, GV, BV, AY, AUV) \
{{BY, GY, RY, 0, BY, GY, RY, 0, BY, GY, RY, 0, BY, GY, RY, 0, \
BY, GY, RY, 0, BY, GY, RY, 0, BY, GY, RY, 0, BY, GY, RY, 0}, \
{BU, GU, RU, 0, BU, GU, RU, 0, BU, GU, RU, 0, BU, GU, RU, 0}, \
{BV, GV, RV, 0, BV, GV, RV, 0, BV, GV, RV, 0, BV, GV, RV, 0}, \
{AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY, AY}, \
{AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, AUV, \
AUV, AUV}}
// clang-format on
#define MAKEYUVCONSTANTS(name, YG, YB, UB, UG, VG, VR) \
@ -1407,6 +1502,94 @@ void J400ToARGBRow_C(const uint8_t* src_y, uint8_t* dst_argb, int width) {
const struct YuvConstants SIMD_ALIGNED(kYvu##name##Constants) = \
YUVCONSTANTSBODY(YG, YB, VR, VG, UG, UB);
#define MAKEARGBCONSTANTS(name, RY, GY, BY, RU, GU, BU, RV, GV, BV, AY, AUV) \
const struct ArgbConstants SIMD_ALIGNED(kArgb##name##Constants) = \
ARGBCONSTANTSBODY(RY, GY, BY, RU, GU, BU, RV, GV, BV, AY, AUV);
// BT.601 limited range RGB to YUV coefficients
// RY = round(0.299 * 219 / 255 * 256) = 66
// GY = round(0.587 * 219 / 255 * 256) = 129
// BY = round(0.114 * 219 / 255 * 256) = 25
// BU = round(0.500 * 224 / 255 * 256) = 112
// RU = round(-0.299 / (1 - 0.114) * 112.4) = -38
// GU = round(-0.587 / (1 - 0.114) * 112.4) = -74
// RV = 112
// GV = round(-0.587 / (1 - 0.299) * 112.4) = -94
// BV = round(-0.114 / (1 - 0.299) * 112.4) = -18
// AY = 16 * 256 + 128 = 4224
// AUV = 128 * 256 = 32768
MAKEARGBCONSTANTS(I601, 66, 129, 25, -38, -74, 112, 112, -94, -18, 4224, 32768)
// BT.601 full range RGB to YUV coefficients (aka JPEG)
// RY = round(0.299 * 256) = 77
// GY = round(0.587 * 256) = 150
// BY = round(0.114 * 256) = 29
// BU = 128
// RU = round(-0.299 / (1 - 0.114) * 128) = -43
// GU = round(-0.587 / (1 - 0.114) * 128) = -85
// RV = 128
// GV = round(-0.587 / (1 - 0.299) * 128) = -107
// BV = round(-0.114 / (1 - 0.299) * 128) = -21
// AY = 128
// AUV = 32768
MAKEARGBCONSTANTS(JPEG, 77, 150, 29, -43, -85, 128, 128, -107, -21, 128, 32768)
// BT.709 limited range RGB to YUV coefficients
// RY = round(0.2126 * 219 / 255 * 256) = 47
// GY = round(0.7152 * 219 / 255 * 256) = 157
// BY = round(0.0722 * 219 / 255 * 256) = 16
// BU = round(0.500 * 224 / 255 * 256) = 112
// RU = round(-0.2126 / (1 - 0.0722) * 112.4) = -26
// GU = round(-0.7152 / (1 - 0.0722) * 112.4) = -86
// RV = 112
// GV = round(-0.7152 / (1 - 0.2126) * 112.4) = -102
// BV = round(-0.0722 / (1 - 0.2126) * 112.4) = -10
// AY = 16 * 256 + 128 = 4224
// AUV = 128 * 256 = 32768
MAKEARGBCONSTANTS(H709, 47, 157, 16, -26, -86, 112, 112, -102, -10, 4224, 32768)
// BT.709 full range RGB to YUV coefficients
// RY = round(0.2126 * 256) = 54
// GY = round(0.7152 * 256) = 183
// BY = round(0.0722 * 256) = 19
// BU = 128
// RU = round(-0.2126 / (1 - 0.0722) * 128) = -29
// GU = round(-0.7152 / (1 - 0.0722) * 128) = -99
// RV = 128
// GV = round(-0.7152 / (1 - 0.2126) * 128) = -116
// BV = round(-0.0722 / (1 - 0.2126) * 128) = -12
// AY = 128
// AUV = 32768
MAKEARGBCONSTANTS(F709, 54, 183, 19, -29, -99, 128, 128, -116, -12, 128, 32768)
// BT.2020 limited range RGB to YUV coefficients
// RY = round(0.2627 * 219 / 255 * 256) = 58
// GY = round(0.6780 * 219 / 255 * 256) = 149
// BY = round(0.0593 * 219 / 255 * 256) = 13
// BU = 112
// RU = round(-0.2627 / (1 - 0.0593) * 112.4) = -31
// GU = round(-0.6780 / (1 - 0.0593) * 112.4) = -81
// RV = 112
// GV = round(-0.6780 / (1 - 0.2627) * 112.4) = -103
// BV = round(-0.0593 / (1 - 0.2627) * 112.4) = -9
// AY = 16 * 256 + 128 = 4224
// AUV = 128 * 256 = 32768
MAKEARGBCONSTANTS(U2020, 59, 148, 13, -31, -81, 112, 112, -103, -9, 4224, 32768)
// BT.2020 full range RGB to YUV coefficients
// RY = round(0.2627 * 256) = 67
// GY = round(0.6780 * 256) = 174
// BY = round(0.0593 * 256) = 15
// BU = 128
// RU = round(-0.2627 / (1 - 0.0593) * 128) = -36
// GU = round(-0.6780 / (1 - 0.0593) * 128) = -92
// RV = 128
// GV = round(-0.6780 / (1 - 0.2627) * 128) = -118
// BV = round(-0.0593 / (1 - 0.2627) * 128) = -10
// AY = 128
// AUV = 32768
MAKEARGBCONSTANTS(V2020, 67, 174, 15, -36, -92, 128, 128, -118, -10, 128, 32768)
// TODO(fbarchard): Generate SIMD structures from float matrix.
// BT.601 limited range YUV to RGB reference

View File

@ -2128,6 +2128,194 @@ TEST_F(LibYUVConvertTest, TestJ420ToI420) {
EXPECT_EQ(dst_v[2], 240);
}
TEST_F(LibYUVConvertTest, TestARGBToI420Matrix) {
const int kWidth = 16;
const int kHeight = 16;
align_buffer_page_end(src_argb, kWidth * kHeight * 4);
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_argb, kWidth * kHeight * 4);
// BT.601
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbI601Constants, kWidth, kHeight);
// Verify against non-matrix version
align_buffer_page_end(ref_y, kWidth * kHeight);
align_buffer_page_end(ref_u, kWidth / 2 * kHeight / 2);
align_buffer_page_end(ref_v, kWidth / 2 * kHeight / 2);
ARGBToI420(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v,
kWidth / 2, kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth / 2 * kHeight / 2; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
// JPEG
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbJPEGConstants, kWidth, kHeight);
// Verify against non-matrix version
ARGBToJ420(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v,
kWidth / 2, kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth / 2 * kHeight / 2; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
// BT.709
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbH709Constants, kWidth, kHeight);
// Just check if it returns 0 for now.
// In a real test we'd have reference values.
// BT.2020
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbU2020Constants, kWidth, kHeight);
// Reference BT.709 (limited range)
// Y = round(0.2126 * 219 / 255 * R + 0.7152 * 219 / 255 * G + 0.0722 * 219 / 255 * B + 16)
// Y = round(0.1826 * R + 0.6142 * G + 0.0620 * B + 16)
// 47 * 255 + 157 * 255 + 16 * 255 + 4224 = 11985 + 40035 + 4080 + 4224 = 60324
// 60324 / 256 = 235.64 -> 235. Correct.
for (int i = 0; i < kWidth * kHeight * 4; ++i) src_argb[i] = 255;
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbH709Constants, kWidth, kHeight);
EXPECT_EQ(dst_y[0], 235);
EXPECT_EQ(dst_u[0], 128);
EXPECT_EQ(dst_v[0], 128);
for (int i = 0; i < kWidth * kHeight * 4; i += 4) {
src_argb[i + 0] = 0; // B
src_argb[i + 1] = 0; // G
src_argb[i + 2] = 255; // R
src_argb[i + 3] = 255; // A
}
ARGBToI420Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbH709Constants, kWidth, kHeight);
// Y = 47 * 255 + 4224 = 11985 + 4224 = 16209. 16209 / 256 = 63.3 -> 63.
EXPECT_EQ(dst_y[0], 63);
// U = -26 * 255 + 32768 = -6630 + 32768 = 26138. 26138 / 256 = 102.1 -> 102.
EXPECT_EQ(dst_u[0], 102);
// V = 112 * 255 + 32768 = 28560 + 32768 = 61328. 61328 / 256 = 239.5 -> 239.
EXPECT_EQ(dst_v[0], 239);
free_aligned_buffer_page_end(src_argb);
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);
}
TEST_F(LibYUVConvertTest, TestARGBToI422Matrix) {
const int kWidth = 16;
const int kHeight = 16;
align_buffer_page_end(src_argb, kWidth * kHeight * 4);
align_buffer_page_end(dst_y, kWidth * kHeight);
align_buffer_page_end(dst_u, kWidth / 2 * kHeight);
align_buffer_page_end(dst_v, kWidth / 2 * kHeight);
MemRandomize(src_argb, kWidth * kHeight * 4);
// BT.601
ARGBToI422Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbI601Constants, kWidth, kHeight);
// Verify against non-matrix version
align_buffer_page_end(ref_y, kWidth * kHeight);
align_buffer_page_end(ref_u, kWidth / 2 * kHeight);
align_buffer_page_end(ref_v, kWidth / 2 * kHeight);
ARGBToI422(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v,
kWidth / 2, kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth / 2 * kHeight; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
// JPEG
ARGBToI422Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbJPEGConstants, kWidth, kHeight);
// Verify against non-matrix version
ARGBToJ422(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v,
kWidth / 2, kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth / 2 * kHeight / 2; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
free_aligned_buffer_page_end(src_argb);
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);
}
TEST_F(LibYUVConvertTest, TestARGBToI444Matrix) {
const int kWidth = 16;
const int kHeight = 16;
align_buffer_page_end(src_argb, kWidth * kHeight * 4);
align_buffer_page_end(dst_y, kWidth * kHeight);
align_buffer_page_end(dst_u, kWidth * kHeight);
align_buffer_page_end(dst_v, kWidth * kHeight);
MemRandomize(src_argb, kWidth * kHeight * 4);
// BT.601
ARGBToI444Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth, dst_v,
kWidth, &kArgbI601Constants, kWidth, kHeight);
// Verify against non-matrix version
align_buffer_page_end(ref_y, kWidth * kHeight);
align_buffer_page_end(ref_u, kWidth * kHeight);
align_buffer_page_end(ref_v, kWidth * kHeight);
ARGBToI444(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth, ref_v, kWidth,
kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
// JPEG
ARGBToI444Matrix(src_argb, kWidth * 4, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, &kArgbJPEGConstants, kWidth, kHeight);
// Verify against non-matrix version
ARGBToJ444(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v,
kWidth / 2, kWidth, kHeight);
for (int i = 0; i < kWidth * kHeight; ++i) {
ASSERT_EQ(dst_y[i], ref_y[i]);
}
for (int i = 0; i < kWidth / 2 * kHeight / 2; ++i) {
ASSERT_EQ(dst_u[i], ref_u[i]);
ASSERT_EQ(dst_v[i], ref_v[i]);
}
free_aligned_buffer_page_end(src_argb);
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);
}
#endif // !defined(LEAN_TESTS)
} // namespace libyuv