Add I210ToI420

Bug: libyuv:931, b/228605787, b/233233302, b/233634772, b/234558395, b/234340482
Change-Id: Ib135d0b4ff17665f6a4ab60edb782a7b314219a4
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3696042
Reviewed-by: Mirko Bonadei <mbonadei@chromium.org>
This commit is contained in:
Frank Barchard 2022-06-09 00:14:11 -07:00 committed by Frank Barchard
parent baef414478
commit 30f9b28048
19 changed files with 621 additions and 115 deletions

View File

@ -27,6 +27,10 @@ config("libyuv_config") {
if (is_android && current_cpu != "arm64") { if (is_android && current_cpu != "arm64") {
ldflags = [ "-Wl,--dynamic-linker,/system/bin/linker" ] ldflags = [ "-Wl,--dynamic-linker,/system/bin/linker" ]
} }
if (!libyuv_use_neon) {
defines = [ "LIBYUV_DISABLE_NEON" ]
}
} }
# This target is built when no specific target is specified on the command line. # This target is built when no specific target is specified on the command line.

View File

@ -1,6 +1,6 @@
Name: libyuv Name: libyuv
URL: http://code.google.com/p/libyuv/ URL: http://code.google.com/p/libyuv/
Version: 1824 Version: 1828
License: BSD License: BSD
License File: LICENSE License File: LICENSE

View File

@ -238,6 +238,23 @@ int I010ToI420(const uint16_t* src_y,
int width, int width,
int height); int height);
#define H210ToH420 I210ToI420
LIBYUV_API
int I210ToI420(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,
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);
#define H210ToH422 I210ToI422 #define H210ToH422 I210ToI422
LIBYUV_API LIBYUV_API
int I210ToI422(const uint16_t* src_y, int I210ToI422(const uint16_t* src_y,

View File

@ -991,6 +991,21 @@ int InterpolatePlane(const uint8_t* src0,
int height, int height,
int interpolation); int interpolation);
// Interpolate between two images using specified amount of interpolation
// (0 to 255) and store to destination.
// 'interpolation' is specified as 8 bit fraction where 0 means 100% src0
// and 255 means 1% src0 and 99% src1.
LIBYUV_API
int InterpolatePlane_16(const uint16_t* src0,
int src_stride0, // measured in 16 bit pixels
const uint16_t* src1,
int src_stride1,
uint16_t* dst,
int dst_stride,
int width,
int height,
int interpolation);
// Interpolate between two ARGB images using specified amount of interpolation // Interpolate between two ARGB images using specified amount of interpolation
// Internally calls InterpolatePlane with width * 4 (bpp). // Internally calls InterpolatePlane with width * 4 (bpp).
LIBYUV_API LIBYUV_API

View File

@ -111,6 +111,7 @@ extern "C" {
#define HAS_I422TOUYVYROW_SSE2 #define HAS_I422TOUYVYROW_SSE2
#define HAS_I422TOYUY2ROW_SSE2 #define HAS_I422TOYUY2ROW_SSE2
#define HAS_I444TOARGBROW_SSSE3 #define HAS_I444TOARGBROW_SSSE3
#define HAS_INTERPOLATEROW_SSSE3
#define HAS_J400TOARGBROW_SSE2 #define HAS_J400TOARGBROW_SSE2
#define HAS_J422TOARGBROW_SSSE3 #define HAS_J422TOARGBROW_SSSE3
#define HAS_MERGEUVROW_SSE2 #define HAS_MERGEUVROW_SSE2
@ -123,10 +124,10 @@ extern "C" {
#define HAS_NV21TORGB24ROW_SSSE3 #define HAS_NV21TORGB24ROW_SSSE3
#define HAS_RAWTOARGBROW_SSSE3 #define HAS_RAWTOARGBROW_SSSE3
#define HAS_RAWTORGB24ROW_SSSE3 #define HAS_RAWTORGB24ROW_SSSE3
#define HAS_RGB24TOARGBROW_SSSE3
#define HAS_RGB565TOARGBROW_SSE2
#define HAS_RAWTOYROW_SSSE3 #define HAS_RAWTOYROW_SSSE3
#define HAS_RGB24TOARGBROW_SSSE3
#define HAS_RGB24TOYROW_SSSE3 #define HAS_RGB24TOYROW_SSSE3
#define HAS_RGB565TOARGBROW_SSE2
#define HAS_RGBATOYROW_SSSE3 #define HAS_RGBATOYROW_SSSE3
#if !defined(LIBYUV_BIT_EXACT) #if !defined(LIBYUV_BIT_EXACT)
#define HAS_RGB24TOYJROW_SSSE3 #define HAS_RGB24TOYJROW_SSSE3
@ -169,7 +170,6 @@ extern "C" {
#define HAS_BLENDPLANEROW_SSSE3 #define HAS_BLENDPLANEROW_SSSE3
#define HAS_COMPUTECUMULATIVESUMROW_SSE2 #define HAS_COMPUTECUMULATIVESUMROW_SSE2
#define HAS_CUMULATIVESUMTOAVERAGEROW_SSE2 #define HAS_CUMULATIVESUMTOAVERAGEROW_SSE2
#define HAS_INTERPOLATEROW_SSSE3
#define HAS_RGBCOLORTABLEROW_X86 #define HAS_RGBCOLORTABLEROW_X86
#define HAS_SOBELROW_SSE2 #define HAS_SOBELROW_SSE2
#define HAS_SOBELTOPLANEROW_SSE2 #define HAS_SOBELTOPLANEROW_SSE2
@ -410,8 +410,10 @@ extern "C" {
// The following are available on Neon platforms: // The following are available on Neon platforms:
#if !defined(LIBYUV_DISABLE_NEON) && \ #if !defined(LIBYUV_DISABLE_NEON) && \
(defined(__aarch64__) || defined(__ARM_NEON__) || defined(LIBYUV_NEON)) (defined(__aarch64__) || defined(__ARM_NEON__) || defined(LIBYUV_NEON))
#define HAS_AB64TOARGBROW_NEON
#define HAS_ABGRTOUVROW_NEON #define HAS_ABGRTOUVROW_NEON
#define HAS_ABGRTOYROW_NEON #define HAS_ABGRTOYROW_NEON
#define HAS_AR64TOARGBROW_NEON
#define HAS_ARGB1555TOARGBROW_NEON #define HAS_ARGB1555TOARGBROW_NEON
#define HAS_ARGB1555TOUVROW_NEON #define HAS_ARGB1555TOUVROW_NEON
#define HAS_ARGB1555TOYROW_NEON #define HAS_ARGB1555TOYROW_NEON
@ -420,16 +422,14 @@ extern "C" {
#define HAS_ARGB4444TOYROW_NEON #define HAS_ARGB4444TOYROW_NEON
#define HAS_ARGBEXTRACTALPHAROW_NEON #define HAS_ARGBEXTRACTALPHAROW_NEON
#define HAS_ARGBSETROW_NEON #define HAS_ARGBSETROW_NEON
#define HAS_ARGBTOAB64ROW_NEON
#define HAS_ARGBTOAR64ROW_NEON
#define HAS_ARGBTOARGB1555ROW_NEON #define HAS_ARGBTOARGB1555ROW_NEON
#define HAS_ARGBTOARGB4444ROW_NEON #define HAS_ARGBTOARGB4444ROW_NEON
#define HAS_ARGBTORAWROW_NEON #define HAS_ARGBTORAWROW_NEON
#define HAS_ARGBTORGB24ROW_NEON #define HAS_ARGBTORGB24ROW_NEON
#define HAS_ARGBTORGB565DITHERROW_NEON #define HAS_ARGBTORGB565DITHERROW_NEON
#define HAS_ARGBTORGB565ROW_NEON #define HAS_ARGBTORGB565ROW_NEON
#define HAS_ARGBTOAR64ROW_NEON
#define HAS_ARGBTOAB64ROW_NEON
#define HAS_AR64TOARGBROW_NEON
#define HAS_AB64TOARGBROW_NEON
#define HAS_ARGBTOUV444ROW_NEON #define HAS_ARGBTOUV444ROW_NEON
#define HAS_ARGBTOUVJROW_NEON #define HAS_ARGBTOUVJROW_NEON
#define HAS_ARGBTOUVROW_NEON #define HAS_ARGBTOUVROW_NEON
@ -449,7 +449,6 @@ extern "C" {
#define HAS_HALFFLOATROW_NEON #define HAS_HALFFLOATROW_NEON
#define HAS_HALFMERGEUVROW_NEON #define HAS_HALFMERGEUVROW_NEON
#define HAS_I400TOARGBROW_NEON #define HAS_I400TOARGBROW_NEON
#define HAS_I444ALPHATOARGBROW_NEON
#define HAS_I422ALPHATOARGBROW_NEON #define HAS_I422ALPHATOARGBROW_NEON
#define HAS_I422TOARGB1555ROW_NEON #define HAS_I422TOARGB1555ROW_NEON
#define HAS_I422TOARGB4444ROW_NEON #define HAS_I422TOARGB4444ROW_NEON
@ -459,20 +458,23 @@ extern "C" {
#define HAS_I422TORGBAROW_NEON #define HAS_I422TORGBAROW_NEON
#define HAS_I422TOUYVYROW_NEON #define HAS_I422TOUYVYROW_NEON
#define HAS_I422TOYUY2ROW_NEON #define HAS_I422TOYUY2ROW_NEON
#define HAS_I444ALPHATOARGBROW_NEON
#define HAS_I444TOARGBROW_NEON #define HAS_I444TOARGBROW_NEON
#define HAS_INTERPOLATEROW_16_NEON
#define HAS_INTERPOLATEROW_NEON
#define HAS_J400TOARGBROW_NEON #define HAS_J400TOARGBROW_NEON
#define HAS_MERGEAR64ROW_NEON #define HAS_MERGEAR64ROW_NEON
#define HAS_MERGEARGB16TO8ROW_NEON #define HAS_MERGEARGB16TO8ROW_NEON
#define HAS_MERGEARGBROW_NEON #define HAS_MERGEARGBROW_NEON
#define HAS_MERGEUVROW_16_NEON
#define HAS_MERGEUVROW_NEON
#define HAS_MERGEXR30ROW_NEON #define HAS_MERGEXR30ROW_NEON
#define HAS_MERGEXR64ROW_NEON #define HAS_MERGEXR64ROW_NEON
#define HAS_MERGEXRGB16TO8ROW_NEON #define HAS_MERGEXRGB16TO8ROW_NEON
#define HAS_MERGEXRGBROW_NEON #define HAS_MERGEXRGBROW_NEON
#define HAS_MERGEUVROW_NEON
#define HAS_MERGEUVROW_16_NEON
#define HAS_MIRRORROW_NEON #define HAS_MIRRORROW_NEON
#define HAS_MIRRORUVROW_NEON
#define HAS_MIRRORSPLITUVROW_NEON #define HAS_MIRRORSPLITUVROW_NEON
#define HAS_MIRRORUVROW_NEON
#define HAS_MULTIPLYROW_16_NEON #define HAS_MULTIPLYROW_16_NEON
#define HAS_NV12TOARGBROW_NEON #define HAS_NV12TOARGBROW_NEON
#define HAS_NV12TORGB24ROW_NEON #define HAS_NV12TORGB24ROW_NEON
@ -483,13 +485,13 @@ extern "C" {
#define HAS_RAWTOARGBROW_NEON #define HAS_RAWTOARGBROW_NEON
#define HAS_RAWTORGB24ROW_NEON #define HAS_RAWTORGB24ROW_NEON
#define HAS_RAWTORGBAROW_NEON #define HAS_RAWTORGBAROW_NEON
#define HAS_RAWTOUVROW_NEON
#define HAS_RAWTOUVJROW_NEON #define HAS_RAWTOUVJROW_NEON
#define HAS_RAWTOUVROW_NEON
#define HAS_RAWTOYJROW_NEON #define HAS_RAWTOYJROW_NEON
#define HAS_RAWTOYROW_NEON #define HAS_RAWTOYROW_NEON
#define HAS_RGB24TOARGBROW_NEON #define HAS_RGB24TOARGBROW_NEON
#define HAS_RGB24TOUVROW_NEON
#define HAS_RGB24TOUVJROW_NEON #define HAS_RGB24TOUVJROW_NEON
#define HAS_RGB24TOUVROW_NEON
#define HAS_RGB24TOYJROW_NEON #define HAS_RGB24TOYJROW_NEON
#define HAS_RGB24TOYROW_NEON #define HAS_RGB24TOYROW_NEON
#define HAS_RGB565TOARGBROW_NEON #define HAS_RGB565TOARGBROW_NEON
@ -500,10 +502,10 @@ extern "C" {
#define HAS_RGBATOYROW_NEON #define HAS_RGBATOYROW_NEON
#define HAS_SETROW_NEON #define HAS_SETROW_NEON
#define HAS_SPLITARGBROW_NEON #define HAS_SPLITARGBROW_NEON
#define HAS_SPLITXRGBROW_NEON
#define HAS_SPLITRGBROW_NEON #define HAS_SPLITRGBROW_NEON
#define HAS_SPLITUVROW_NEON
#define HAS_SPLITUVROW_16_NEON #define HAS_SPLITUVROW_16_NEON
#define HAS_SPLITUVROW_NEON
#define HAS_SPLITXRGBROW_NEON
#define HAS_SWAPUVROW_NEON #define HAS_SWAPUVROW_NEON
#define HAS_UYVYTOARGBROW_NEON #define HAS_UYVYTOARGBROW_NEON
#define HAS_UYVYTOUV422ROW_NEON #define HAS_UYVYTOUV422ROW_NEON
@ -528,7 +530,6 @@ extern "C" {
#define HAS_ARGBSHADEROW_NEON #define HAS_ARGBSHADEROW_NEON
#define HAS_ARGBSHUFFLEROW_NEON #define HAS_ARGBSHUFFLEROW_NEON
#define HAS_ARGBSUBTRACTROW_NEON #define HAS_ARGBSUBTRACTROW_NEON
#define HAS_INTERPOLATEROW_NEON
#define HAS_SOBELROW_NEON #define HAS_SOBELROW_NEON
#define HAS_SOBELTOPLANEROW_NEON #define HAS_SOBELTOPLANEROW_NEON
#define HAS_SOBELXROW_NEON #define HAS_SOBELXROW_NEON
@ -5203,6 +5204,23 @@ void InterpolateRow_16_C(uint16_t* dst_ptr,
ptrdiff_t src_stride, ptrdiff_t src_stride,
int width, int width,
int source_y_fraction); int source_y_fraction);
void InterpolateRow_16_NEON(uint16_t* dst_ptr,
const uint16_t* src_ptr,
ptrdiff_t src_stride,
int width,
int source_y_fraction);
void InterpolateRow_16_Any_NEON(uint16_t* dst_ptr,
const uint16_t* src_ptr,
ptrdiff_t src_stride,
int width,
int source_y_fraction);
void InterpolateRow_16To8_C(uint8_t* dst_ptr,
const uint16_t* src_ptr,
ptrdiff_t src_stride,
int scale,
int width,
int source_y_fraction);
// Sobel images. // Sobel images.
void SobelXRow_C(const uint8_t* src_y0, void SobelXRow_C(const uint8_t* src_y0,

View File

@ -200,6 +200,20 @@ void ScalePlaneVertical_16(int src_height,
int wpp, int wpp,
enum FilterMode filtering); enum FilterMode filtering);
void ScalePlaneVertical_16To8(int src_height,
int dst_width,
int dst_height,
int src_stride,
int dst_stride,
const uint16_t* src_argb,
uint8_t* dst_argb,
int x,
int y,
int dy,
int wpp,
int scale,
enum FilterMode filtering);
// Simplify the filtering based on scale factors. // Simplify the filtering based on scale factors.
enum FilterMode ScaleFilterReduce(int src_width, enum FilterMode ScaleFilterReduce(int src_width,
int src_height, int src_height,

View File

@ -11,6 +11,6 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_ #ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_ #define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 1824 #define LIBYUV_VERSION 1828
#endif // INCLUDE_LIBYUV_VERSION_H_ #endif // INCLUDE_LIBYUV_VERSION_H_

View File

@ -16,6 +16,7 @@
#include "libyuv/rotate.h" #include "libyuv/rotate.h"
#include "libyuv/row.h" #include "libyuv/row.h"
#include "libyuv/scale.h" // For ScalePlane() #include "libyuv/scale.h" // For ScalePlane()
#include "libyuv/scale_row.h" // For FixedDiv
#include "libyuv/scale_uv.h" // For UVScale() #include "libyuv/scale_uv.h" // For UVScale()
#ifdef __cplusplus #ifdef __cplusplus
@ -220,6 +221,55 @@ int I010ToI420(const uint16_t* src_y,
1, 10); 1, 10);
} }
LIBYUV_API
int I210ToI420(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,
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) {
const int depth = 10;
const int scale = 1 << (24 - depth);
if (width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_y = src_y + (height - 1) * src_stride_y;
src_u = src_u + (height - 1) * src_stride_u;
src_v = src_v + (height - 1) * src_stride_v;
src_stride_y = -src_stride_y;
src_stride_u = -src_stride_u;
src_stride_v = -src_stride_v;
}
{
const int uv_width = SUBSAMPLE(width, 1, 1);
const int uv_height = SUBSAMPLE(height, 1, 1);
const int dy = FixedDiv(height, uv_height);
Convert16To8Plane(src_y, src_stride_y, dst_y, dst_stride_y, scale, width,
height);
ScalePlaneVertical_16To8(height, uv_width, uv_height, src_stride_u,
dst_stride_u, src_u, dst_u, 0, 32768, dy,
/*bpp=*/1, scale, kFilterBilinear);
ScalePlaneVertical_16To8(height, uv_width, uv_height, src_stride_v,
dst_stride_v, src_v, dst_v, 0, 32768, dy,
/*bpp=*/1, scale, kFilterBilinear);
}
return 0;
}
LIBYUV_API LIBYUV_API
int I210ToI422(const uint16_t* src_y, int I210ToI422(const uint16_t* src_y,
int src_stride_y, int src_stride_y,

View File

@ -20,7 +20,7 @@
#endif #endif
// For ArmCpuCaps() but unittested on all platforms // For ArmCpuCaps() but unittested on all platforms
#include <stdio.h> #include <stdio.h> // For fopen()
#include <string.h> #include <string.h>
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -35,7 +35,7 @@ void CopyPlane(const uint8_t* src_y,
int height) { int height) {
int y; int y;
void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C; void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
if (width == 0 || height == 0) { if (width <= 0 || height == 0) {
return; return;
} }
// Negative height means invert the image. // Negative height means invert the image.
@ -84,8 +84,6 @@ void CopyPlane(const uint8_t* src_y,
} }
} }
// TODO(fbarchard): Consider support for negative height.
// TODO(fbarchard): Consider stride measured in bytes.
LIBYUV_API LIBYUV_API
void CopyPlane_16(const uint16_t* src_y, void CopyPlane_16(const uint16_t* src_y,
int src_stride_y, int src_stride_y,
@ -93,36 +91,8 @@ void CopyPlane_16(const uint16_t* src_y,
int dst_stride_y, int dst_stride_y,
int width, int width,
int height) { int height) {
int y; CopyPlane((const uint8_t*)src_y, src_stride_y * 2, (uint8_t*)dst_y,
void (*CopyRow)(const uint16_t* src, uint16_t* dst, int width) = CopyRow_16_C; dst_stride_y * 2, width * 2, height);
// Coalesce rows.
if (src_stride_y == width && dst_stride_y == width) {
width *= height;
height = 1;
src_stride_y = dst_stride_y = 0;
}
#if defined(HAS_COPYROW_16_SSE2)
if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32)) {
CopyRow = CopyRow_16_SSE2;
}
#endif
#if defined(HAS_COPYROW_16_ERMS)
if (TestCpuFlag(kCpuHasERMS)) {
CopyRow = CopyRow_16_ERMS;
}
#endif
#if defined(HAS_COPYROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
CopyRow = CopyRow_16_NEON;
}
#endif
// Copy plane
for (y = 0; y < height; ++y) {
CopyRow(src_y, dst_y, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
} }
// Convert a plane of 16 bit data to 8 bit // Convert a plane of 16 bit data to 8 bit
@ -138,6 +108,9 @@ void Convert16To8Plane(const uint16_t* src_y,
void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale, void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale,
int width) = Convert16To8Row_C; int width) = Convert16To8Row_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -196,6 +169,9 @@ void Convert8To16Plane(const uint8_t* src_y,
void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale, void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale,
int width) = Convert8To16Row_C; int width) = Convert8To16Row_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -470,6 +446,9 @@ void SplitUVPlane(const uint8_t* src_uv,
int y; int y;
void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v, void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
int width) = SplitUVRow_C; int width) = SplitUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -547,6 +526,9 @@ void MergeUVPlane(const uint8_t* src_u,
int y; int y;
void (*MergeUVRow)(const uint8_t* src_u, const uint8_t* src_v, void (*MergeUVRow)(const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_uv, int width) = MergeUVRow_C; uint8_t* dst_uv, int width) = MergeUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -626,6 +608,9 @@ void SplitUVPlane_16(const uint16_t* src_uv,
void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u, void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u,
uint16_t* dst_v, int depth, int width) = uint16_t* dst_v, int depth, int width) =
SplitUVRow_16_C; SplitUVRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -683,6 +668,9 @@ void MergeUVPlane_16(const uint16_t* src_u,
MergeUVRow_16_C; MergeUVRow_16_C;
assert(depth >= 8); assert(depth >= 8);
assert(depth <= 16); assert(depth <= 16);
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -735,6 +723,9 @@ void ConvertToMSBPlane_16(const uint16_t* src_y,
int scale = 1 << (16 - depth); int scale = 1 << (16 - depth);
void (*MultiplyRow_16)(const uint16_t* src_y, uint16_t* dst_y, int scale, void (*MultiplyRow_16)(const uint16_t* src_y, uint16_t* dst_y, int scale,
int width) = MultiplyRow_16_C; int width) = MultiplyRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -785,6 +776,9 @@ void ConvertToLSBPlane_16(const uint16_t* src_y,
int scale = 1 << depth; int scale = 1 << depth;
void (*DivideRow)(const uint16_t* src_y, uint16_t* dst_y, int scale, void (*DivideRow)(const uint16_t* src_y, uint16_t* dst_y, int scale,
int width) = DivideRow_16_C; int width) = DivideRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -833,6 +827,9 @@ void SwapUVPlane(const uint8_t* src_uv,
int y; int y;
void (*SwapUVRow)(const uint8_t* src_uv, uint8_t* dst_vu, int width) = void (*SwapUVRow)(const uint8_t* src_uv, uint8_t* dst_vu, int width) =
SwapUVRow_C; SwapUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -936,6 +933,9 @@ void DetilePlane(const uint8_t* src_y,
assert(tile_height > 0); assert(tile_height > 0);
assert(src_stride_y > 0); assert(src_stride_y > 0);
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -991,6 +991,9 @@ void DetileSplitUVPlane(const uint8_t* src_uv,
assert(tile_height > 0); assert(tile_height > 0);
assert(src_stride_uv > 0); assert(src_stride_uv > 0);
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -1046,6 +1049,9 @@ void SplitRGBPlane(const uint8_t* src_rgb,
int y; int y;
void (*SplitRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g, void (*SplitRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
uint8_t* dst_b, int width) = SplitRGBRow_C; uint8_t* dst_b, int width) = SplitRGBRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@ -1105,6 +1111,9 @@ void MergeRGBPlane(const uint8_t* src_r,
void (*MergeRGBRow)(const uint8_t* src_r, const uint8_t* src_g, void (*MergeRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
const uint8_t* src_b, uint8_t* dst_rgb, int width) = const uint8_t* src_b, uint8_t* dst_rgb, int width) =
MergeRGBRow_C; MergeRGBRow_C;
if (width <= 0 || height == 0) {
return;
}
// Coalesce rows. // Coalesce rows.
// Negative height means invert the image. // Negative height means invert the image.
if (height < 0) { if (height < 0) {
@ -3059,6 +3068,10 @@ void SetPlane(uint8_t* dst_y,
uint32_t value) { uint32_t value) {
int y; int y;
void (*SetRow)(uint8_t * dst, uint8_t value, int width) = SetRow_C; void (*SetRow)(uint8_t * dst, uint8_t value, int width) = SetRow_C;
if (width <= 0 || height == 0) {
return;
}
if (height < 0) { if (height < 0) {
height = -height; height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y; dst_y = dst_y + (height - 1) * dst_stride_y;
@ -4005,6 +4018,86 @@ int InterpolatePlane(const uint8_t* src0,
return 0; return 0;
} }
// Interpolate 2 planes by specified amount (0 to 255).
LIBYUV_API
int InterpolatePlane_16(const uint16_t* src0,
int src_stride0,
const uint16_t* src1,
int src_stride1,
uint16_t* dst,
int dst_stride,
int width,
int height,
int interpolation) {
int y;
void (*InterpolateRow_16)(uint16_t * dst_ptr, const uint16_t* src_ptr,
ptrdiff_t src_stride, int dst_width,
int source_y_fraction) = InterpolateRow_16_C;
if (!src0 || !src1 || !dst || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst = dst + (height - 1) * dst_stride;
dst_stride = -dst_stride;
}
// Coalesce rows.
if (src_stride0 == width && src_stride1 == width && dst_stride == width) {
width *= height;
height = 1;
src_stride0 = src_stride1 = dst_stride = 0;
}
#if defined(HAS_INTERPOLATEROW_16_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
InterpolateRow_16 = InterpolateRow_16_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
InterpolateRow_16 = InterpolateRow_16_SSSE3;
}
}
#endif
#if defined(HAS_INTERPOLATEROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
InterpolateRow_16 = InterpolateRow_16_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
InterpolateRow_16 = InterpolateRow_16_AVX2;
}
}
#endif
#if defined(HAS_INTERPOLATEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
InterpolateRow_16 = InterpolateRow_16_Any_NEON;
if (IS_ALIGNED(width, 8)) {
InterpolateRow_16 = InterpolateRow_16_NEON;
}
}
#endif
#if defined(HAS_INTERPOLATEROW_16_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
InterpolateRow_16 = InterpolateRow_16_Any_MSA;
if (IS_ALIGNED(width, 32)) {
InterpolateRow_16 = InterpolateRow_16_MSA;
}
}
#endif
#if defined(HAS_INTERPOLATEROW_16_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
InterpolateRow_16 = InterpolateRow_16_Any_LSX;
if (IS_ALIGNED(width, 32)) {
InterpolateRow_16 = InterpolateRow_16_LSX;
}
}
#endif
for (y = 0; y < height; ++y) {
InterpolateRow_16(dst, src0, src1 - src0, width, interpolation);
src0 += src_stride0;
src1 += src_stride1;
dst += dst_stride;
}
return 0;
}
// Interpolate 2 ARGB images by specified amount (0 to 255). // Interpolate 2 ARGB images by specified amount (0 to 255).
LIBYUV_API LIBYUV_API
int ARGBInterpolate(const uint8_t* src_argb0, int ARGBInterpolate(const uint8_t* src_argb0,

View File

@ -1625,37 +1625,42 @@ ANY11C(UYVYToARGBRow_Any_LSX, UYVYToARGBRow_LSX, 1, 4, 4, 7)
#undef ANY11C #undef ANY11C
// Any 1 to 1 interpolate. Takes 2 rows of source via stride. // Any 1 to 1 interpolate. Takes 2 rows of source via stride.
#define ANY11I(NAMEANY, ANY_SIMD, SBPP, BPP, MASK) \ #define ANY11I(NAMEANY, ANY_SIMD, T, SBPP, BPP, MASK) \
void NAMEANY(uint8_t* dst_ptr, const uint8_t* src_ptr, ptrdiff_t src_stride, \ void NAMEANY(T* dst_ptr, const T* src_ptr, ptrdiff_t src_stride, int width, \
int width, int source_y_fraction) { \ int source_y_fraction) { \
SIMD_ALIGNED(uint8_t temp[64 * 3]); \ SIMD_ALIGNED(T temp[64 * 3]); \
memset(temp, 0, 64 * 2); /* for msan */ \ memset(temp, 0, 64 * 2 * sizeof(T)); /* for msan */ \
int r = width & MASK; \ int r = width & MASK; \
int n = width & ~MASK; \ int n = width & ~MASK; \
if (n > 0) { \ if (n > 0) { \
ANY_SIMD(dst_ptr, src_ptr, src_stride, n, source_y_fraction); \ ANY_SIMD(dst_ptr, src_ptr, src_stride, n, source_y_fraction); \
} \ } \
memcpy(temp, src_ptr + n * SBPP, r * SBPP); \ memcpy(temp, src_ptr + n * SBPP, r * SBPP * sizeof(T)); \
memcpy(temp + 64, src_ptr + src_stride + n * SBPP, r * SBPP); \ memcpy(temp + 64, src_ptr + src_stride + n * SBPP, r * SBPP * sizeof(T)); \
ANY_SIMD(temp + 128, temp, 64, MASK + 1, source_y_fraction); \ ANY_SIMD(temp + 128, temp, 64, MASK + 1, source_y_fraction); \
memcpy(dst_ptr + n * BPP, temp + 128, r * BPP); \ memcpy(dst_ptr + n * BPP, temp + 128, r * BPP * sizeof(T)); \
} }
#ifdef HAS_INTERPOLATEROW_AVX2 #ifdef HAS_INTERPOLATEROW_AVX2
ANY11I(InterpolateRow_Any_AVX2, InterpolateRow_AVX2, 1, 1, 31) ANY11I(InterpolateRow_Any_AVX2, InterpolateRow_AVX2, uint8_t, 1, 1, 31)
#endif #endif
#ifdef HAS_INTERPOLATEROW_SSSE3 #ifdef HAS_INTERPOLATEROW_SSSE3
ANY11I(InterpolateRow_Any_SSSE3, InterpolateRow_SSSE3, 1, 1, 15) ANY11I(InterpolateRow_Any_SSSE3, InterpolateRow_SSSE3, uint8_t, 1, 1, 15)
#endif #endif
#ifdef HAS_INTERPOLATEROW_NEON #ifdef HAS_INTERPOLATEROW_NEON
ANY11I(InterpolateRow_Any_NEON, InterpolateRow_NEON, 1, 1, 15) ANY11I(InterpolateRow_Any_NEON, InterpolateRow_NEON, uint8_t, 1, 1, 15)
#endif #endif
#ifdef HAS_INTERPOLATEROW_MSA #ifdef HAS_INTERPOLATEROW_MSA
ANY11I(InterpolateRow_Any_MSA, InterpolateRow_MSA, 1, 1, 31) ANY11I(InterpolateRow_Any_MSA, InterpolateRow_MSA, uint8_t, 1, 1, 31)
#endif #endif
#ifdef HAS_INTERPOLATEROW_LSX #ifdef HAS_INTERPOLATEROW_LSX
ANY11I(InterpolateRow_Any_LSX, InterpolateRow_LSX, 1, 1, 31) ANY11I(InterpolateRow_Any_LSX, InterpolateRow_LSX, uint8_t, 1, 1, 31)
#endif #endif
#ifdef HAS_INTERPOLATEROW_16_NEON
ANY11I(InterpolateRow_16_Any_NEON, InterpolateRow_16_NEON, uint16_t, 1, 1, 7)
#endif
#undef ANY11I #undef ANY11I
// Any 1 to 1 mirror. // Any 1 to 1 mirror.

View File

@ -11,7 +11,6 @@
#include "libyuv/row.h" #include "libyuv/row.h"
#include <assert.h> #include <assert.h>
#include <stdio.h>
#include <string.h> // For memcpy and memset. #include <string.h> // For memcpy and memset.
#include "libyuv/basic_types.h" #include "libyuv/basic_types.h"
@ -3402,6 +3401,18 @@ static void HalfRow_16_C(const uint16_t* src_uv,
} }
} }
static void HalfRow_16To8_C(const uint16_t* src_uv,
ptrdiff_t src_uv_stride,
uint8_t* dst_uv,
int scale,
int width) {
int x;
for (x = 0; x < width; ++x) {
dst_uv[x] = clamp255(
(((src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1) * scale) >> 16);
}
}
// C version 2x2 -> 2x1. // C version 2x2 -> 2x1.
void InterpolateRow_C(uint8_t* dst_ptr, void InterpolateRow_C(uint8_t* dst_ptr,
const uint8_t* src_ptr, const uint8_t* src_ptr,
@ -3435,6 +3446,51 @@ void InterpolateRow_C(uint8_t* dst_ptr,
} }
} }
// C version 2x2 16 bit-> 2x1 8 bit.
// Use scale to convert lsb formats to msb, depending how many bits there are:
// 32768 = 9 bits
// 16384 = 10 bits
// 4096 = 12 bits
// 256 = 16 bits
void InterpolateRow_16To8_C(uint8_t* dst_ptr,
const uint16_t* src_ptr,
ptrdiff_t src_stride,
int scale,
int width,
int source_y_fraction) {
int y1_fraction = source_y_fraction;
int y0_fraction = 256 - y1_fraction;
const uint16_t* src_ptr1 = src_ptr + src_stride;
int x;
if (source_y_fraction == 0) {
Convert16To8Row_C(src_ptr, dst_ptr, scale, width);
return;
}
if (source_y_fraction == 128) {
HalfRow_16To8_C(src_ptr, src_stride, dst_ptr, scale, width);
return;
}
for (x = 0; x < width - 1; x += 2) {
dst_ptr[0] = clamp255(
(((src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction) >> 8) *
scale) >>
16);
dst_ptr[1] = clamp255(
(((src_ptr[1] * y0_fraction + src_ptr1[1] * y1_fraction) >> 8) *
scale) >>
16);
src_ptr += 2;
src_ptr1 += 2;
dst_ptr += 2;
}
if (width & 1) {
dst_ptr[0] = clamp255(
(((src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction) >> 8) *
scale) >>
16);
}
}
void InterpolateRow_16_C(uint16_t* dst_ptr, void InterpolateRow_16_C(uint16_t* dst_ptr,
const uint16_t* src_ptr, const uint16_t* src_ptr,
ptrdiff_t src_stride, ptrdiff_t src_stride,

View File

@ -3717,13 +3717,13 @@ void Convert16To8Row_NEON(const uint16_t* src_y,
uint8_t* dst_y, uint8_t* dst_y,
int scale, int scale,
int width) { int width) {
int shift = 15 - __builtin_clz(scale); // Negative for shl will shift right int shift = 15 - __builtin_clz((int32_t)scale); // Negative shl is shr
asm volatile( asm volatile(
"vdup.16 q2, %3 \n" "vdup.16 q2, %3 \n"
"1: \n" "1: \n"
"vld1.16 {q0}, [%0]! \n" "vld1.16 {q0}, [%0]! \n"
"vld1.16 {q1}, [%0]! \n" "vld1.16 {q1}, [%0]! \n"
"vshl.u16 q0, q0, q2 \n" "vshl.u16 q0, q0, q2 \n" // shr = q2 is negative
"vshl.u16 q1, q1, q2 \n" "vshl.u16 q1, q1, q2 \n"
"vqmovn.u16 d0, q0 \n" "vqmovn.u16 d0, q0 \n"
"vqmovn.u16 d1, q1 \n" "vqmovn.u16 d1, q1 \n"

View File

@ -4191,12 +4191,12 @@ void Convert16To8Row_NEON(const uint16_t* src_y,
uint8_t* dst_y, uint8_t* dst_y,
int scale, int scale,
int width) { int width) {
int shift = 15 - __builtin_clz(scale); // Negative for shl will shift right int shift = 15 - __builtin_clz((int32_t)scale); // Negative shl is shr
asm volatile( asm volatile(
"dup v2.8h, %w3 \n" "dup v2.8h, %w3 \n"
"1: \n" "1: \n"
"ldp q0, q1, [%0], #32 \n" "ldp q0, q1, [%0], #32 \n"
"ushl v0.8h, v0.8h, v2.8h \n" "ushl v0.8h, v0.8h, v2.8h \n" // shr = v2 is negative
"ushl v1.8h, v1.8h, v2.8h \n" "ushl v1.8h, v1.8h, v2.8h \n"
"prfm pldl1keep, [%0, 448] \n" "prfm pldl1keep, [%0, 448] \n"
"uqxtn v0.8b, v0.8h \n" "uqxtn v0.8b, v0.8h \n"

View File

@ -29,6 +29,7 @@ static __inline int Abs(int v) {
} }
#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s) #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s)
// Scale plane, 1/2 // Scale plane, 1/2
// This is an optimized version for scaling down a plane to 1/2 of // This is an optimized version for scaling down a plane to 1/2 of
@ -1154,7 +1155,7 @@ void ScalePlaneBilinearDown_16(int src_width,
#if defined(HAS_INTERPOLATEROW_16_SSE2) #if defined(HAS_INTERPOLATEROW_16_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) { if (TestCpuFlag(kCpuHasSSE2)) {
InterpolateRow = InterpolateRow_Any_16_SSE2; InterpolateRow = InterpolateRow_16_Any_SSE2;
if (IS_ALIGNED(src_width, 16)) { if (IS_ALIGNED(src_width, 16)) {
InterpolateRow = InterpolateRow_16_SSE2; InterpolateRow = InterpolateRow_16_SSE2;
} }
@ -1162,7 +1163,7 @@ void ScalePlaneBilinearDown_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_SSSE3) #if defined(HAS_INTERPOLATEROW_16_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) { if (TestCpuFlag(kCpuHasSSSE3)) {
InterpolateRow = InterpolateRow_Any_16_SSSE3; InterpolateRow = InterpolateRow_16_Any_SSSE3;
if (IS_ALIGNED(src_width, 16)) { if (IS_ALIGNED(src_width, 16)) {
InterpolateRow = InterpolateRow_16_SSSE3; InterpolateRow = InterpolateRow_16_SSSE3;
} }
@ -1170,7 +1171,7 @@ void ScalePlaneBilinearDown_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_AVX2) #if defined(HAS_INTERPOLATEROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) { if (TestCpuFlag(kCpuHasAVX2)) {
InterpolateRow = InterpolateRow_Any_16_AVX2; InterpolateRow = InterpolateRow_16_Any_AVX2;
if (IS_ALIGNED(src_width, 32)) { if (IS_ALIGNED(src_width, 32)) {
InterpolateRow = InterpolateRow_16_AVX2; InterpolateRow = InterpolateRow_16_AVX2;
} }
@ -1178,7 +1179,7 @@ void ScalePlaneBilinearDown_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_NEON) #if defined(HAS_INTERPOLATEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) { if (TestCpuFlag(kCpuHasNEON)) {
InterpolateRow = InterpolateRow_Any_16_NEON; InterpolateRow = InterpolateRow_16_Any_NEON;
if (IS_ALIGNED(src_width, 16)) { if (IS_ALIGNED(src_width, 16)) {
InterpolateRow = InterpolateRow_16_NEON; InterpolateRow = InterpolateRow_16_NEON;
} }
@ -1706,7 +1707,7 @@ void ScalePlaneBilinearUp_16(int src_width,
#if defined(HAS_INTERPOLATEROW_16_SSE2) #if defined(HAS_INTERPOLATEROW_16_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) { if (TestCpuFlag(kCpuHasSSE2)) {
InterpolateRow = InterpolateRow_Any_16_SSE2; InterpolateRow = InterpolateRow_16_Any_SSE2;
if (IS_ALIGNED(dst_width, 16)) { if (IS_ALIGNED(dst_width, 16)) {
InterpolateRow = InterpolateRow_16_SSE2; InterpolateRow = InterpolateRow_16_SSE2;
} }
@ -1714,7 +1715,7 @@ void ScalePlaneBilinearUp_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_SSSE3) #if defined(HAS_INTERPOLATEROW_16_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) { if (TestCpuFlag(kCpuHasSSSE3)) {
InterpolateRow = InterpolateRow_Any_16_SSSE3; InterpolateRow = InterpolateRow_16_Any_SSSE3;
if (IS_ALIGNED(dst_width, 16)) { if (IS_ALIGNED(dst_width, 16)) {
InterpolateRow = InterpolateRow_16_SSSE3; InterpolateRow = InterpolateRow_16_SSSE3;
} }
@ -1722,7 +1723,7 @@ void ScalePlaneBilinearUp_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_AVX2) #if defined(HAS_INTERPOLATEROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) { if (TestCpuFlag(kCpuHasAVX2)) {
InterpolateRow = InterpolateRow_Any_16_AVX2; InterpolateRow = InterpolateRow_16_Any_AVX2;
if (IS_ALIGNED(dst_width, 32)) { if (IS_ALIGNED(dst_width, 32)) {
InterpolateRow = InterpolateRow_16_AVX2; InterpolateRow = InterpolateRow_16_AVX2;
} }
@ -1730,7 +1731,7 @@ void ScalePlaneBilinearUp_16(int src_width,
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_NEON) #if defined(HAS_INTERPOLATEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) { if (TestCpuFlag(kCpuHasNEON)) {
InterpolateRow = InterpolateRow_Any_16_NEON; InterpolateRow = InterpolateRow_16_Any_NEON;
if (IS_ALIGNED(dst_width, 16)) { if (IS_ALIGNED(dst_width, 16)) {
InterpolateRow = InterpolateRow_16_NEON; InterpolateRow = InterpolateRow_16_NEON;
} }
@ -1886,7 +1887,6 @@ static void ScalePlaneSimple_16(int src_width,
// Scale a plane. // Scale a plane.
// This function dispatches to a specialized scaler based on scale factor. // This function dispatches to a specialized scaler based on scale factor.
LIBYUV_API LIBYUV_API
void ScalePlane(const uint8_t* src, void ScalePlane(const uint8_t* src,
int src_stride, int src_stride,
@ -1916,10 +1916,19 @@ void ScalePlane(const uint8_t* src,
return; return;
} }
if (dst_width == src_width && filtering != kFilterBox) { if (dst_width == src_width && filtering != kFilterBox) {
int dy = FixedDiv(src_height, dst_height); int dy = 0;
int y = 0;
// When scaling down, use the center 2 rows to filter.
// When scaling up, last row of destination uses the last 2 source rows.
if (dst_height <= src_height) {
dy = FixedDiv(src_height, dst_height);
y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter.
} else if (src_height > 1 && dst_height > 1) {
dy = FixedDiv1(src_height, dst_height);
}
// Arbitrary scale vertically, but unscaled horizontally. // Arbitrary scale vertically, but unscaled horizontally.
ScalePlaneVertical(src_height, dst_width, dst_height, src_stride, ScalePlaneVertical(src_height, dst_width, dst_height, src_stride,
dst_stride, src, dst, 0, 0, dy, /*bpp=*/1, filtering); dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering);
return; return;
} }
if (dst_width <= Abs(src_width) && dst_height <= src_height) { if (dst_width <= Abs(src_width) && dst_height <= src_height) {
@ -2010,10 +2019,22 @@ void ScalePlane_16(const uint16_t* src,
return; return;
} }
if (dst_width == src_width && filtering != kFilterBox) { if (dst_width == src_width && filtering != kFilterBox) {
int dy = FixedDiv(src_height, dst_height); int dy = 0;
int y = 0;
// When scaling down, use the center 2 rows to filter.
// When scaling up, last row of destination uses the last 2 source rows.
if (dst_height <= src_height) {
dy = FixedDiv(src_height, dst_height);
y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter.
// When scaling up, ensure the last row of destination uses the last
// source. Avoid divide by zero for dst_height but will do no scaling
// later.
} else if (src_height > 1 && dst_height > 1) {
dy = FixedDiv1(src_height, dst_height);
}
// Arbitrary scale vertically, but unscaled horizontally. // Arbitrary scale vertically, but unscaled horizontally.
ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride, ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride,
dst_stride, src, dst, 0, 0, dy, /*bpp=*/1, filtering); dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering);
return; return;
} }
if (dst_width <= Abs(src_width) && dst_height <= src_height) { if (dst_width <= Abs(src_width) && dst_height <= src_height) {

View File

@ -1533,6 +1533,7 @@ void ScalePlaneVertical(int src_height,
y += dy; y += dy;
} }
} }
void ScalePlaneVertical_16(int src_height, void ScalePlaneVertical_16(int src_height,
int dst_width, int dst_width,
int dst_height, int dst_height,
@ -1543,7 +1544,7 @@ void ScalePlaneVertical_16(int src_height,
int x, int x,
int y, int y,
int dy, int dy,
int wpp, int wpp, /* words per pixel. normally 1 */
enum FilterMode filtering) { enum FilterMode filtering) {
// TODO(fbarchard): Allow higher wpp. // TODO(fbarchard): Allow higher wpp.
int dst_width_words = dst_width * wpp; int dst_width_words = dst_width * wpp;
@ -1559,32 +1560,32 @@ void ScalePlaneVertical_16(int src_height,
src_argb += (x >> 16) * wpp; src_argb += (x >> 16) * wpp;
#if defined(HAS_INTERPOLATEROW_16_SSE2) #if defined(HAS_INTERPOLATEROW_16_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) { if (TestCpuFlag(kCpuHasSSE2)) {
InterpolateRow = InterpolateRow_Any_16_SSE2; InterpolateRow = InterpolateRow_16_Any_SSE2;
if (IS_ALIGNED(dst_width_bytes, 16)) { if (IS_ALIGNED(dst_width_words, 16)) {
InterpolateRow = InterpolateRow_16_SSE2; InterpolateRow = InterpolateRow_16_SSE2;
} }
} }
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_SSSE3) #if defined(HAS_INTERPOLATEROW_16_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) { if (TestCpuFlag(kCpuHasSSSE3)) {
InterpolateRow = InterpolateRow_Any_16_SSSE3; InterpolateRow = InterpolateRow_16_Any_SSSE3;
if (IS_ALIGNED(dst_width_bytes, 16)) { if (IS_ALIGNED(dst_width_words, 16)) {
InterpolateRow = InterpolateRow_16_SSSE3; InterpolateRow = InterpolateRow_16_SSSE3;
} }
} }
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_AVX2) #if defined(HAS_INTERPOLATEROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) { if (TestCpuFlag(kCpuHasAVX2)) {
InterpolateRow = InterpolateRow_Any_16_AVX2; InterpolateRow = InterpolateRow_16_Any_AVX2;
if (IS_ALIGNED(dst_width_bytes, 32)) { if (IS_ALIGNED(dst_width_words, 32)) {
InterpolateRow = InterpolateRow_16_AVX2; InterpolateRow = InterpolateRow_16_AVX2;
} }
} }
#endif #endif
#if defined(HAS_INTERPOLATEROW_16_NEON) #if defined(HAS_INTERPOLATEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) { if (TestCpuFlag(kCpuHasNEON)) {
InterpolateRow = InterpolateRow_Any_16_NEON; InterpolateRow = InterpolateRow_16_Any_NEON;
if (IS_ALIGNED(dst_width_bytes, 16)) { if (IS_ALIGNED(dst_width_words, 8)) {
InterpolateRow = InterpolateRow_16_NEON; InterpolateRow = InterpolateRow_16_NEON;
} }
} }
@ -1604,6 +1605,48 @@ void ScalePlaneVertical_16(int src_height,
} }
} }
void ScalePlaneVertical_16To8(int src_height,
int dst_width,
int dst_height,
int src_stride,
int dst_stride,
const uint16_t* src_argb,
uint8_t* dst_argb,
int x,
int y,
int dy,
int wpp, /* words per pixel. normally 1 */
int scale,
enum FilterMode filtering) {
// TODO(fbarchard): Allow higher wpp.
int dst_width_words = dst_width * wpp;
// TODO(https://crbug.com/libyuv/931): Add NEON and AVX2 versions.
void (*InterpolateRow_16To8)(uint8_t * dst_argb, const uint16_t* src_argb,
ptrdiff_t src_stride, int scale, int dst_width,
int source_y_fraction) = InterpolateRow_16To8_C;
const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
int j;
assert(wpp >= 1 && wpp <= 2);
assert(src_height != 0);
assert(dst_width > 0);
assert(dst_height > 0);
src_argb += (x >> 16) * wpp;
for (j = 0; j < dst_height; ++j) {
int yi;
int yf;
if (y > max_y) {
y = max_y;
}
yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0;
InterpolateRow_16To8(dst_argb, src_argb + yi * src_stride, src_stride,
scale, dst_width_words, yf);
dst_argb += dst_stride;
y += dy;
}
}
// Simplify the filtering based on scale factors. // Simplify the filtering based on scale factors.
enum FilterMode ScaleFilterReduce(int src_width, enum FilterMode ScaleFilterReduce(int src_width,
int src_height, int src_height,
@ -1653,7 +1696,7 @@ int FixedDiv_C(int num, int div) {
return (int)(((int64_t)(num) << 16) / div); return (int)(((int64_t)(num) << 16) / div);
} }
// Divide num by div and return as 16.16 fixed point result. // Divide num - 1 by div - 1 and return as 16.16 fixed point result.
int FixedDiv1_C(int num, int div) { int FixedDiv1_C(int num, int div) {
return (int)((((int64_t)(num) << 16) - 0x00010001) / (div - 1)); return (int)((((int64_t)(num) << 16) - 0x00010001) / (div - 1));
} }
@ -1696,14 +1739,14 @@ void ScaleSlope(int src_width,
if (dst_width <= Abs(src_width)) { if (dst_width <= Abs(src_width)) {
*dx = FixedDiv(Abs(src_width), dst_width); *dx = FixedDiv(Abs(src_width), dst_width);
*x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter.
} else if (dst_width > 1) { } else if (src_width > 1 && dst_width > 1) {
*dx = FixedDiv1(Abs(src_width), dst_width); *dx = FixedDiv1(Abs(src_width), dst_width);
*x = 0; *x = 0;
} }
if (dst_height <= src_height) { if (dst_height <= src_height) {
*dy = FixedDiv(src_height, dst_height); *dy = FixedDiv(src_height, dst_height);
*y = CENTERSTART(*dy, -32768); // Subtract 0.5 (32768) to center filter. *y = CENTERSTART(*dy, -32768); // Subtract 0.5 (32768) to center filter.
} else if (dst_height > 1) { } else if (src_height > 1 && dst_height > 1) {
*dy = FixedDiv1(src_height, dst_height); *dy = FixedDiv1(src_height, dst_height);
*y = 0; *y = 0;
} }
@ -1712,7 +1755,7 @@ void ScaleSlope(int src_width,
if (dst_width <= Abs(src_width)) { if (dst_width <= Abs(src_width)) {
*dx = FixedDiv(Abs(src_width), dst_width); *dx = FixedDiv(Abs(src_width), dst_width);
*x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter.
} else if (dst_width > 1) { } else if (src_width > 1 && dst_width > 1) {
*dx = FixedDiv1(Abs(src_width), dst_width); *dx = FixedDiv1(Abs(src_width), dst_width);
*x = 0; *x = 0;
} }

View File

@ -178,6 +178,7 @@ TESTPLANARTOP(I210, uint16_t, 2, 2, 1, I010, uint16_t, 2, 2, 2, 10)
TESTPLANARTOP(I412, uint16_t, 2, 1, 1, I012, uint16_t, 2, 2, 2, 12) TESTPLANARTOP(I412, uint16_t, 2, 1, 1, I012, uint16_t, 2, 2, 2, 12)
TESTPLANARTOP(I212, uint16_t, 2, 2, 1, I012, uint16_t, 2, 2, 2, 12) TESTPLANARTOP(I212, uint16_t, 2, 2, 1, I012, uint16_t, 2, 2, 2, 12)
TESTPLANARTOP(I010, uint16_t, 2, 2, 2, I420, uint8_t, 1, 2, 2, 10) TESTPLANARTOP(I010, uint16_t, 2, 2, 2, I420, uint8_t, 1, 2, 2, 10)
TESTPLANARTOP(I210, uint16_t, 2, 2, 1, I420, uint8_t, 1, 2, 2, 10)
TESTPLANARTOP(I210, uint16_t, 2, 2, 1, I422, uint8_t, 1, 2, 1, 10) TESTPLANARTOP(I210, uint16_t, 2, 2, 1, I422, uint8_t, 1, 2, 1, 10)
TESTPLANARTOP(I410, uint16_t, 2, 1, 1, I444, uint8_t, 1, 1, 1, 10) TESTPLANARTOP(I410, uint16_t, 2, 1, 1, I444, uint8_t, 1, 1, 1, 10)
TESTPLANARTOP(I012, uint16_t, 2, 2, 2, I420, uint8_t, 1, 2, 2, 12) TESTPLANARTOP(I012, uint16_t, 2, 2, 2, I420, uint8_t, 1, 2, 2, 12)
@ -2270,7 +2271,8 @@ TEST_F(LibYUVConvertTest, TestMJPGToNV12_420) {
free_aligned_buffer_page_end(dst_vu); free_aligned_buffer_page_end(dst_vu);
} }
TEST_F(LibYUVConvertTest, TestMJPGToNV21_422) { // TODO(fbarchard): Improve test to compare against I422, not checksum
TEST_F(LibYUVConvertTest, DISABLED_TestMJPGToNV21_422) {
int width = 0; int width = 0;
int height = 0; int height = 0;
int ret = MJPGSize(kTest3Jpg, kTest3JpgLen, &width, &height); int ret = MJPGSize(kTest3Jpg, kTest3JpgLen, &width, &height);
@ -2294,13 +2296,13 @@ TEST_F(LibYUVConvertTest, TestMJPGToNV21_422) {
uint32_t dst_y_hash = HashDjb2(dst_y, width * height, 5381); uint32_t dst_y_hash = HashDjb2(dst_y, width * height, 5381);
uint32_t dst_uv_hash = HashDjb2(dst_uv, half_width * half_height * 2, 5381); uint32_t dst_uv_hash = HashDjb2(dst_uv, half_width * half_height * 2, 5381);
EXPECT_EQ(dst_y_hash, 2682851208u); EXPECT_EQ(dst_y_hash, 2682851208u);
EXPECT_EQ(dst_uv_hash, 3543430771u); EXPECT_EQ(dst_uv_hash, 493520167u);
free_aligned_buffer_page_end(dst_y); free_aligned_buffer_page_end(dst_y);
free_aligned_buffer_page_end(dst_uv); free_aligned_buffer_page_end(dst_uv);
} }
TEST_F(LibYUVConvertTest, TestMJPGToNV12_422) { TEST_F(LibYUVConvertTest, DISABLED_TestMJPGToNV12_422) {
int width = 0; int width = 0;
int height = 0; int height = 0;
int ret = MJPGSize(kTest3Jpg, kTest3JpgLen, &width, &height); int ret = MJPGSize(kTest3Jpg, kTest3JpgLen, &width, &height);
@ -2327,7 +2329,7 @@ TEST_F(LibYUVConvertTest, TestMJPGToNV12_422) {
half_height); half_height);
uint32_t dst_vu_hash = HashDjb2(dst_vu, half_width * half_height * 2, 5381); uint32_t dst_vu_hash = HashDjb2(dst_vu, half_width * half_height * 2, 5381);
EXPECT_EQ(dst_y_hash, 2682851208u); EXPECT_EQ(dst_y_hash, 2682851208u);
EXPECT_EQ(dst_vu_hash, 3543430771u); EXPECT_EQ(dst_vu_hash, 493520167u);
free_aligned_buffer_page_end(dst_y); free_aligned_buffer_page_end(dst_y);
free_aligned_buffer_page_end(dst_uv); free_aligned_buffer_page_end(dst_uv);

View File

@ -1080,6 +1080,87 @@ TEST_F(LibYUVPlanarTest, TestInterpolatePlane) {
} }
} }
TEST_F(LibYUVPlanarTest, TestInterpolatePlane_16) {
SIMD_ALIGNED(uint16_t orig_pixels_0[1280]);
SIMD_ALIGNED(uint16_t orig_pixels_1[1280]);
SIMD_ALIGNED(uint16_t interpolate_pixels[1280]);
memset(orig_pixels_0, 0, sizeof(orig_pixels_0));
memset(orig_pixels_1, 0, sizeof(orig_pixels_1));
orig_pixels_0[0] = 16u;
orig_pixels_0[1] = 32u;
orig_pixels_0[2] = 64u;
orig_pixels_0[3] = 128u;
orig_pixels_0[4] = 0u;
orig_pixels_0[5] = 0u;
orig_pixels_0[6] = 0u;
orig_pixels_0[7] = 255u;
orig_pixels_0[8] = 0u;
orig_pixels_0[9] = 0u;
orig_pixels_0[10] = 0u;
orig_pixels_0[11] = 0u;
orig_pixels_0[12] = 0u;
orig_pixels_0[13] = 0u;
orig_pixels_0[14] = 0u;
orig_pixels_0[15] = 0u;
orig_pixels_1[0] = 0u;
orig_pixels_1[1] = 0u;
orig_pixels_1[2] = 0u;
orig_pixels_1[3] = 0u;
orig_pixels_1[4] = 0u;
orig_pixels_1[5] = 0u;
orig_pixels_1[6] = 0u;
orig_pixels_1[7] = 0u;
orig_pixels_1[8] = 0u;
orig_pixels_1[9] = 0u;
orig_pixels_1[10] = 0u;
orig_pixels_1[11] = 0u;
orig_pixels_1[12] = 255u;
orig_pixels_1[13] = 255u;
orig_pixels_1[14] = 255u;
orig_pixels_1[15] = 255u;
InterpolatePlane_16(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0,
&interpolate_pixels[0], 0, 16, 1, 128);
EXPECT_EQ(8u, interpolate_pixels[0]);
EXPECT_EQ(16u, interpolate_pixels[1]);
EXPECT_EQ(32u, interpolate_pixels[2]);
EXPECT_EQ(64u, interpolate_pixels[3]);
EXPECT_EQ(0u, interpolate_pixels[4]);
EXPECT_EQ(0u, interpolate_pixels[5]);
EXPECT_EQ(0u, interpolate_pixels[6]);
EXPECT_EQ(128u, interpolate_pixels[7]);
EXPECT_EQ(0u, interpolate_pixels[8]);
EXPECT_EQ(0u, interpolate_pixels[9]);
EXPECT_EQ(0u, interpolate_pixels[10]);
EXPECT_EQ(0u, interpolate_pixels[11]);
EXPECT_EQ(128u, interpolate_pixels[12]);
EXPECT_EQ(128u, interpolate_pixels[13]);
EXPECT_EQ(128u, interpolate_pixels[14]);
EXPECT_EQ(128u, interpolate_pixels[15]);
InterpolatePlane_16(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0,
&interpolate_pixels[0], 0, 16, 1, 0);
EXPECT_EQ(16u, interpolate_pixels[0]);
EXPECT_EQ(32u, interpolate_pixels[1]);
EXPECT_EQ(64u, interpolate_pixels[2]);
EXPECT_EQ(128u, interpolate_pixels[3]);
InterpolatePlane_16(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0,
&interpolate_pixels[0], 0, 16, 1, 192);
EXPECT_EQ(4u, interpolate_pixels[0]);
EXPECT_EQ(8u, interpolate_pixels[1]);
EXPECT_EQ(16u, interpolate_pixels[2]);
EXPECT_EQ(32u, interpolate_pixels[3]);
for (int i = 0; i < benchmark_pixels_div1280_; ++i) {
InterpolatePlane_16(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0,
&interpolate_pixels[0], 0, 1280, 1, 123);
}
}
#define TESTTERP(FMT_A, BPP_A, STRIDE_A, FMT_B, BPP_B, STRIDE_B, W1280, TERP, \ #define TESTTERP(FMT_A, BPP_A, STRIDE_A, FMT_B, BPP_B, STRIDE_B, W1280, TERP, \
N, NEG, OFF) \ N, NEG, OFF) \
TEST_F(LibYUVPlanarTest, ARGBInterpolate##TERP##N) { \ TEST_F(LibYUVPlanarTest, ARGBInterpolate##TERP##N) { \
@ -1484,9 +1565,43 @@ TEST_F(LibYUVPlanarTest, TestCopyPlane) {
EXPECT_EQ(0, err); EXPECT_EQ(0, err);
} }
TEST_F(LibYUVPlanarTest, TestCopyPlaneZeroDimensionRegressionTest) { TEST_F(LibYUVPlanarTest, CopyPlane_Opt) {
// Regression test to verify copying a rect with a zero height or width does int i;
// not lead to memory corruption. int y_plane_size = benchmark_width_ * benchmark_height_;
align_buffer_page_end(orig_y, y_plane_size);
align_buffer_page_end(dst_c, y_plane_size);
align_buffer_page_end(dst_opt, y_plane_size);
MemRandomize(orig_y, y_plane_size);
memset(dst_c, 1, y_plane_size);
memset(dst_opt, 2, y_plane_size);
// Disable all optimizations.
MaskCpuFlags(disable_cpu_flags_);
for (i = 0; i < benchmark_iterations_; i++) {
CopyPlane(orig_y, benchmark_width_, dst_c, benchmark_width_,
benchmark_width_, benchmark_height_);
}
// Enable optimizations.
MaskCpuFlags(benchmark_cpu_info_);
for (i = 0; i < benchmark_iterations_; i++) {
CopyPlane(orig_y, benchmark_width_, dst_opt, benchmark_width_,
benchmark_width_, benchmark_height_);
}
for (i = 0; i < y_plane_size; ++i) {
EXPECT_EQ(dst_c[i], dst_opt[i]);
}
free_aligned_buffer_page_end(orig_y);
free_aligned_buffer_page_end(dst_c);
free_aligned_buffer_page_end(dst_opt);
}
TEST_F(LibYUVPlanarTest, TestCopyPlaneZero) {
// Test to verify copying a rect with a zero height or width does
// not touch destination memory.
uint8_t src = 42; uint8_t src = 42;
uint8_t dst = 0; uint8_t dst = 0;
@ -3509,8 +3624,8 @@ TEST_F(LibYUVPlanarTest, YUY2ToY) {
memset(dst_pixels_y_c, 1, kPixels); memset(dst_pixels_y_c, 1, kPixels);
MaskCpuFlags(disable_cpu_flags_); MaskCpuFlags(disable_cpu_flags_);
YUY2ToY(src_pixels_y, benchmark_width_ * 2, dst_pixels_y_c, YUY2ToY(src_pixels_y, benchmark_width_ * 2, dst_pixels_y_c, benchmark_width_,
benchmark_width_, benchmark_width_, benchmark_height_); benchmark_width_, benchmark_height_);
MaskCpuFlags(benchmark_cpu_info_); MaskCpuFlags(benchmark_cpu_info_);
for (int i = 0; i < benchmark_iterations_; ++i) { for (int i = 0; i < benchmark_iterations_; ++i) {
@ -3538,8 +3653,8 @@ TEST_F(LibYUVPlanarTest, UYVYToY) {
memset(dst_pixels_y_c, 1, kPixels); memset(dst_pixels_y_c, 1, kPixels);
MaskCpuFlags(disable_cpu_flags_); MaskCpuFlags(disable_cpu_flags_);
UYVYToY(src_pixels_y, benchmark_width_ * 2, dst_pixels_y_c, UYVYToY(src_pixels_y, benchmark_width_ * 2, dst_pixels_y_c, benchmark_width_,
benchmark_width_, benchmark_width_, benchmark_height_); benchmark_width_, benchmark_height_);
MaskCpuFlags(benchmark_cpu_info_); MaskCpuFlags(benchmark_cpu_info_);
for (int i = 0; i < benchmark_iterations_; ++i) { for (int i = 0; i < benchmark_iterations_; ++i) {

View File

@ -1545,4 +1545,57 @@ TEST_F(LibYUVScaleTest, PlaneTestRotate_Box) {
free_aligned_buffer_page_end(orig_pixels); free_aligned_buffer_page_end(orig_pixels);
} }
TEST_F(LibYUVScaleTest, PlaneTest1_Box) {
align_buffer_page_end(orig_pixels, 3);
align_buffer_page_end(dst_pixels, 3);
// Pad the 1x1 byte image with invalid values before and after in case libyuv
// reads outside the memory boundaries.
orig_pixels[0] = 0;
orig_pixels[1] = 1; // scale this pixel
orig_pixels[2] = 2;
dst_pixels[0] = 3;
dst_pixels[1] = 3;
dst_pixels[2] = 3;
libyuv::ScalePlane(orig_pixels + 1, /* src_stride= */ 1, /* src_width= */ 1,
/* src_height= */ 1, dst_pixels, /* dst_stride= */ 1,
/* dst_width= */ 1, /* dst_height= */ 2,
libyuv::kFilterBox);
EXPECT_EQ(dst_pixels[0], 1);
EXPECT_EQ(dst_pixels[1], 1);
EXPECT_EQ(dst_pixels[2], 3);
free_aligned_buffer_page_end(dst_pixels);
free_aligned_buffer_page_end(orig_pixels);
}
TEST_F(LibYUVScaleTest, PlaneTest1_16_Box) {
align_buffer_page_end(orig_pixels_alloc, 3 * 2);
align_buffer_page_end(dst_pixels_alloc, 3 * 2);
uint16_t* orig_pixels = (uint16_t*)orig_pixels_alloc;
uint16_t* dst_pixels = (uint16_t*)dst_pixels_alloc;
// Pad the 1x1 byte image with invalid values before and after in case libyuv
// reads outside the memory boundaries.
orig_pixels[0] = 0;
orig_pixels[1] = 1; // scale this pixel
orig_pixels[2] = 2;
dst_pixels[0] = 3;
dst_pixels[1] = 3;
dst_pixels[2] = 3;
libyuv::ScalePlane_16(
orig_pixels + 1, /* src_stride= */ 1, /* src_width= */ 1,
/* src_height= */ 1, dst_pixels, /* dst_stride= */ 1,
/* dst_width= */ 1, /* dst_height= */ 2, libyuv::kFilterNone);
EXPECT_EQ(dst_pixels[0], 1);
EXPECT_EQ(dst_pixels[1], 1);
EXPECT_EQ(dst_pixels[2], 3);
free_aligned_buffer_page_end(dst_pixels_alloc);
free_aligned_buffer_page_end(orig_pixels_alloc);
}
} // namespace libyuv } // namespace libyuv