blob: cc909eb7347044f00074268fd28f0d325b4eb088 [file] [log] [blame] [edit]
/*
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "libyuv/planar_functions.h"
#include <assert.h>
#include <string.h> // for memset()
#include "libyuv/cpu_id.h"
#include "libyuv/row.h"
#include "libyuv/scale_row.h" // for ScaleRowDown2
#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif
// Copy a plane of data
LIBYUV_API
void CopyPlane(const uint8_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
int y;
void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
// Coalesce rows.
if (src_stride_y == width && dst_stride_y == width) {
width *= height;
height = 1;
src_stride_y = dst_stride_y = 0;
}
// Nothing to do.
if (src_y == dst_y && src_stride_y == dst_stride_y) {
return;
}
#if defined(HAS_COPYROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
}
#endif
#if defined(HAS_COPYROW_AVX)
if (TestCpuFlag(kCpuHasAVX)) {
CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
}
#endif
#if defined(HAS_COPYROW_AVX512BW)
if (TestCpuFlag(kCpuHasAVX512BW)) {
CopyRow = IS_ALIGNED(width, 128) ? CopyRow_AVX512BW : CopyRow_Any_AVX512BW;
}
#endif
#if defined(HAS_COPYROW_ERMS)
if (TestCpuFlag(kCpuHasERMS)) {
CopyRow = CopyRow_ERMS;
}
#endif
#if defined(HAS_COPYROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
}
#endif
#if defined(HAS_COPYROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
CopyRow = CopyRow_RVV;
}
#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;
}
}
LIBYUV_API
void CopyPlane_16(const uint16_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int width,
int height) {
CopyPlane((const uint8_t*)src_y, src_stride_y * 2, (uint8_t*)dst_y,
dst_stride_y * 2, width * 2, height);
}
// Convert a plane of 16 bit data to 8 bit
LIBYUV_API
void Convert16To8Plane(const uint16_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int scale, // 16384 for 10 bits
int width,
int height) {
int y;
void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale,
int width) = Convert16To8Row_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
// 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_CONVERT16TO8ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
Convert16To8Row = Convert16To8Row_Any_NEON;
if (IS_ALIGNED(width, 16)) {
Convert16To8Row = Convert16To8Row_NEON;
}
}
#endif
#if defined(HAS_CONVERT16TO8ROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
Convert16To8Row = Convert16To8Row_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
Convert16To8Row = Convert16To8Row_SSSE3;
}
}
#endif
#if defined(HAS_CONVERT16TO8ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
Convert16To8Row = Convert16To8Row_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
Convert16To8Row = Convert16To8Row_AVX2;
}
}
#endif
#if defined(HAS_CONVERT16TO8ROW_AVX512BW)
if (TestCpuFlag(kCpuHasAVX512BW)) {
Convert16To8Row = Convert16To8Row_Any_AVX512BW;
if (IS_ALIGNED(width, 64)) {
Convert16To8Row = Convert16To8Row_AVX512BW;
}
}
#endif
// Convert plane
for (y = 0; y < height; ++y) {
Convert16To8Row(src_y, dst_y, scale, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
}
// Convert a plane of 8 bit data to 16 bit
LIBYUV_API
void Convert8To16Plane(const uint8_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int scale, // 1024 for 10 bits
int width,
int height) {
int y;
void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale,
int width) = Convert8To16Row_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
// 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_CONVERT8TO16ROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
Convert8To16Row = Convert8To16Row_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
Convert8To16Row = Convert8To16Row_SSE2;
}
}
#endif
#if defined(HAS_CONVERT8TO16ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
Convert8To16Row = Convert8To16Row_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
Convert8To16Row = Convert8To16Row_AVX2;
}
}
#endif
// Convert plane
for (y = 0; y < height; ++y) {
Convert8To16Row(src_y, dst_y, scale, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
}
// Copy I422.
LIBYUV_API
int I422Copy(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_u,
int src_stride_u,
const uint8_t* src_v,
int src_stride_v,
uint8_t* dst_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 halfwidth = (width + 1) >> 1;
if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || 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;
}
if (dst_y) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
return 0;
}
// Copy I444.
LIBYUV_API
int I444Copy(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_u,
int src_stride_u,
const uint8_t* src_v,
int src_stride_v,
uint8_t* dst_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) {
if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || 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;
}
if (dst_y) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
return 0;
}
// Copy I210.
LIBYUV_API
int I210Copy(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_u,
int dst_stride_u,
uint16_t* dst_v,
int dst_stride_v,
int width,
int height) {
int halfwidth = (width + 1) >> 1;
if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || 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;
}
if (dst_y) {
CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
// Copy UV planes.
CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
return 0;
}
// Copy I410.
LIBYUV_API
int I410Copy(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_u,
int dst_stride_u,
uint16_t* dst_v,
int dst_stride_v,
int width,
int height) {
if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || 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;
}
if (dst_y) {
CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
return 0;
}
// Copy I400.
LIBYUV_API
int I400ToI400(const uint8_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
if (!src_y || !dst_y || 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_stride_y = -src_stride_y;
}
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
return 0;
}
// Convert I420 to I400.
LIBYUV_API
int I420ToI400(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_u,
int src_stride_u,
const uint8_t* src_v,
int src_stride_v,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
(void)src_u;
(void)src_stride_u;
(void)src_v;
(void)src_stride_v;
if (!src_y || !dst_y || 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_stride_y = -src_stride_y;
}
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
return 0;
}
// Copy NV12. Supports inverting.
LIBYUV_API
int NV12Copy(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height) {
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if (!src_y || !dst_y || !src_uv || !dst_uv || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_y = src_y + (height - 1) * src_stride_y;
src_uv = src_uv + (halfheight - 1) * src_stride_uv;
src_stride_y = -src_stride_y;
src_stride_uv = -src_stride_uv;
}
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth * 2,
halfheight);
return 0;
}
// Copy NV21. Supports inverting.
LIBYUV_API
int NV21Copy(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_vu,
int src_stride_vu,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_vu,
int dst_stride_vu,
int width,
int height) {
return NV12Copy(src_y, src_stride_y, src_vu, src_stride_vu, dst_y,
dst_stride_y, dst_vu, dst_stride_vu, width, height);
}
// Support function for NV12 etc UV channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitUVPlane(const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height) {
int y;
void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
int width) = SplitUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_u = dst_u + (height - 1) * dst_stride_u;
dst_v = dst_v + (height - 1) * dst_stride_v;
dst_stride_u = -dst_stride_u;
dst_stride_v = -dst_stride_v;
}
// Coalesce rows.
if (src_stride_uv == width * 2 && dst_stride_u == width &&
dst_stride_v == width) {
width *= height;
height = 1;
src_stride_uv = dst_stride_u = dst_stride_v = 0;
}
#if defined(HAS_SPLITUVROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
SplitUVRow = SplitUVRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_SSE2;
}
}
#endif
#if defined(HAS_SPLITUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow = SplitUVRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitUVRow = SplitUVRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_NEON;
}
}
#endif
#if defined(HAS_SPLITUVROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
SplitUVRow = SplitUVRow_Any_MSA;
if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_MSA;
}
}
#endif
#if defined(HAS_SPLITUVROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
SplitUVRow = SplitUVRow_Any_LSX;
if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_LSX;
}
}
#endif
#if defined(HAS_SPLITUVROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SplitUVRow = SplitUVRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
// Copy a row of UV.
SplitUVRow(src_uv, dst_u, dst_v, width);
dst_u += dst_stride_u;
dst_v += dst_stride_v;
src_uv += src_stride_uv;
}
}
LIBYUV_API
void MergeUVPlane(const uint8_t* src_u,
int src_stride_u,
const uint8_t* src_v,
int src_stride_v,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height) {
int y;
void (*MergeUVRow)(const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_uv, int width) = MergeUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_uv = dst_uv + (height - 1) * dst_stride_uv;
dst_stride_uv = -dst_stride_uv;
}
// Coalesce rows.
if (src_stride_u == width && src_stride_v == width &&
dst_stride_uv == width * 2) {
width *= height;
height = 1;
src_stride_u = src_stride_v = dst_stride_uv = 0;
}
#if defined(HAS_MERGEUVROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
MergeUVRow = MergeUVRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_SSE2;
}
}
#endif
#if defined(HAS_MERGEUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeUVRow = MergeUVRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_AVX2;
}
}
#endif
#if defined(HAS_MERGEUVROW_AVX512BW)
if (TestCpuFlag(kCpuHasAVX512BW)) {
MergeUVRow = MergeUVRow_Any_AVX512BW;
if (IS_ALIGNED(width, 32)) {
MergeUVRow = MergeUVRow_AVX512BW;
}
}
#endif
#if defined(HAS_MERGEUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeUVRow = MergeUVRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_NEON;
}
}
#endif
#if defined(HAS_MERGEUVROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
MergeUVRow = MergeUVRow_Any_MSA;
if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_MSA;
}
}
#endif
#if defined(HAS_MERGEUVROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
MergeUVRow = MergeUVRow_Any_LSX;
if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_LSX;
}
}
#endif
#if defined(HAS_MERGEUVROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
MergeUVRow = MergeUVRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
// Merge a row of U and V into a row of UV.
MergeUVRow(src_u, src_v, dst_uv, width);
src_u += src_stride_u;
src_v += src_stride_v;
dst_uv += dst_stride_uv;
}
}
// Support function for P010 etc UV channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitUVPlane_16(const uint16_t* src_uv,
int src_stride_uv,
uint16_t* dst_u,
int dst_stride_u,
uint16_t* dst_v,
int dst_stride_v,
int width,
int height,
int depth) {
int y;
void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u,
uint16_t* dst_v, int depth, int width) =
SplitUVRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_u = dst_u + (height - 1) * dst_stride_u;
dst_v = dst_v + (height - 1) * dst_stride_v;
dst_stride_u = -dst_stride_u;
dst_stride_v = -dst_stride_v;
}
// Coalesce rows.
if (src_stride_uv == width * 2 && dst_stride_u == width &&
dst_stride_v == width) {
width *= height;
height = 1;
src_stride_uv = dst_stride_u = dst_stride_v = 0;
}
#if defined(HAS_SPLITUVROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow_16 = SplitUVRow_16_Any_AVX2;
if (IS_ALIGNED(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(width, 8)) {
SplitUVRow_16 = SplitUVRow_16_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
// Copy a row of UV.
SplitUVRow_16(src_uv, dst_u, dst_v, depth, width);
dst_u += dst_stride_u;
dst_v += dst_stride_v;
src_uv += src_stride_uv;
}
}
LIBYUV_API
void MergeUVPlane_16(const uint16_t* src_u,
int src_stride_u,
const uint16_t* src_v,
int src_stride_v,
uint16_t* dst_uv,
int dst_stride_uv,
int width,
int height,
int depth) {
int y;
void (*MergeUVRow_16)(const uint16_t* src_u, const uint16_t* src_v,
uint16_t* dst_uv, int depth, int width) =
MergeUVRow_16_C;
assert(depth >= 8);
assert(depth <= 16);
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_uv = dst_uv + (height - 1) * dst_stride_uv;
dst_stride_uv = -dst_stride_uv;
}
// Coalesce rows.
if (src_stride_u == width && src_stride_v == width &&
dst_stride_uv == width * 2) {
width *= height;
height = 1;
src_stride_u = src_stride_v = dst_stride_uv = 0;
}
#if defined(HAS_MERGEUVROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeUVRow_16 = MergeUVRow_16_Any_AVX2;
if (IS_ALIGNED(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(width, 8)) {
MergeUVRow_16 = MergeUVRow_16_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
// Merge a row of U and V into a row of UV.
MergeUVRow_16(src_u, src_v, dst_uv, depth, width);
src_u += src_stride_u;
src_v += src_stride_v;
dst_uv += dst_stride_uv;
}
}
// Convert plane from lsb to msb
LIBYUV_API
void ConvertToMSBPlane_16(const uint16_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int width,
int height,
int depth) {
int y;
int scale = 1 << (16 - depth);
void (*MultiplyRow_16)(const uint16_t* src_y, uint16_t* dst_y, int scale,
int width) = MultiplyRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
// 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_MULTIPLYROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MultiplyRow_16 = MultiplyRow_16_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
MultiplyRow_16 = MultiplyRow_16_AVX2;
}
}
#endif
#if defined(HAS_MULTIPLYROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MultiplyRow_16 = MultiplyRow_16_Any_NEON;
if (IS_ALIGNED(width, 16)) {
MultiplyRow_16 = MultiplyRow_16_NEON;
}
}
#endif
#if defined(HAS_MULTIPLYROW_16_SME)
if (TestCpuFlag(kCpuHasSME)) {
MultiplyRow_16 = MultiplyRow_16_SME;
}
#endif
for (y = 0; y < height; ++y) {
MultiplyRow_16(src_y, dst_y, scale, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
}
// Convert plane from msb to lsb
LIBYUV_API
void ConvertToLSBPlane_16(const uint16_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int width,
int height,
int depth) {
int y;
int scale = 1 << depth;
void (*DivideRow)(const uint16_t* src_y, uint16_t* dst_y, int scale,
int width) = DivideRow_16_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
// 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_DIVIDEROW_16_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
DivideRow = DivideRow_16_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
DivideRow = DivideRow_16_AVX2;
}
}
#endif
#if defined(HAS_DIVIDEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
DivideRow = DivideRow_16_Any_NEON;
if (IS_ALIGNED(width, 16)) {
DivideRow = DivideRow_16_NEON;
}
}
#endif
#if defined(HAS_DIVIDEROW_16_SVE2)
if (TestCpuFlag(kCpuHasSVE2)) {
DivideRow = DivideRow_16_SVE2;
}
#endif
for (y = 0; y < height; ++y) {
DivideRow(src_y, dst_y, scale, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
}
// Swap U and V channels in interleaved UV plane.
LIBYUV_API
void SwapUVPlane(const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_vu,
int dst_stride_vu,
int width,
int height) {
int y;
void (*SwapUVRow)(const uint8_t* src_uv, uint8_t* dst_vu, int width) =
SwapUVRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_uv = src_uv + (height - 1) * src_stride_uv;
src_stride_uv = -src_stride_uv;
}
// Coalesce rows.
if (src_stride_uv == width * 2 && dst_stride_vu == width * 2) {
width *= height;
height = 1;
src_stride_uv = dst_stride_vu = 0;
}
#if defined(HAS_SWAPUVROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
SwapUVRow = SwapUVRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
SwapUVRow = SwapUVRow_SSSE3;
}
}
#endif
#if defined(HAS_SWAPUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SwapUVRow = SwapUVRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
SwapUVRow = SwapUVRow_AVX2;
}
}
#endif
#if defined(HAS_SWAPUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SwapUVRow = SwapUVRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SwapUVRow = SwapUVRow_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
SwapUVRow(src_uv, dst_vu, width);
src_uv += src_stride_uv;
dst_vu += dst_stride_vu;
}
}
// Convert NV21 to NV12.
LIBYUV_API
int NV21ToNV12(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_vu,
int src_stride_vu,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height) {
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if (!src_vu || !dst_uv || width <= 0 || height == 0) {
return -1;
}
if (dst_y) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_vu = src_vu + (halfheight - 1) * src_stride_vu;
src_stride_vu = -src_stride_vu;
}
SwapUVPlane(src_vu, src_stride_vu, dst_uv, dst_stride_uv, halfwidth,
halfheight);
return 0;
}
// Test if tile_height is a power of 2 (16 or 32)
#define IS_POWEROFTWO(x) (!((x) & ((x)-1)))
// Detile a plane of data
// tile width is 16 and assumed.
// tile_height is 16 or 32 for MM21.
// src_stride_y is bytes per row of source ignoring tiling. e.g. 640
// TODO: More detile row functions.
LIBYUV_API
int DetilePlane(const uint8_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height,
int tile_height) {
const ptrdiff_t src_tile_stride = 16 * tile_height;
int y;
void (*DetileRow)(const uint8_t* src, ptrdiff_t src_tile_stride, uint8_t* dst,
int width) = DetileRow_C;
if (!src_y || !dst_y || width <= 0 || height == 0 ||
!IS_POWEROFTWO(tile_height)) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
#if defined(HAS_DETILEROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
DetileRow = DetileRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
DetileRow = DetileRow_SSE2;
}
}
#endif
#if defined(HAS_DETILEROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
DetileRow = DetileRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
DetileRow = DetileRow_NEON;
}
}
#endif
// Detile plane
for (y = 0; y < height; ++y) {
DetileRow(src_y, src_tile_stride, dst_y, width);
dst_y += dst_stride_y;
src_y += 16;
// Advance to next row of tiles.
if ((y & (tile_height - 1)) == (tile_height - 1)) {
src_y = src_y - src_tile_stride + src_stride_y * tile_height;
}
}
return 0;
}
// Convert a plane of 16 bit tiles of 16 x H to linear.
// tile width is 16 and assumed.
// tile_height is 16 or 32 for MT2T.
LIBYUV_API
int DetilePlane_16(const uint16_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int width,
int height,
int tile_height) {
const ptrdiff_t src_tile_stride = 16 * tile_height;
int y;
void (*DetileRow_16)(const uint16_t* src, ptrdiff_t src_tile_stride,
uint16_t* dst, int width) = DetileRow_16_C;
if (!src_y || !dst_y || width <= 0 || height == 0 ||
!IS_POWEROFTWO(tile_height)) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_y = dst_y + (height - 1) * dst_stride_y;
dst_stride_y = -dst_stride_y;
}
#if defined(HAS_DETILEROW_16_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
DetileRow_16 = DetileRow_16_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
DetileRow_16 = DetileRow_16_SSE2;
}
}
#endif
#if defined(HAS_DETILEROW_16_AVX)
if (TestCpuFlag(kCpuHasAVX)) {
DetileRow_16 = DetileRow_16_Any_AVX;
if (IS_ALIGNED(width, 16)) {
DetileRow_16 = DetileRow_16_AVX;
}
}
#endif
#if defined(HAS_DETILEROW_16_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
DetileRow_16 = DetileRow_16_Any_NEON;
if (IS_ALIGNED(width, 16)) {
DetileRow_16 = DetileRow_16_NEON;
}
}
#endif
// Detile plane
for (y = 0; y < height; ++y) {
DetileRow_16(src_y, src_tile_stride, dst_y, width);
dst_y += dst_stride_y;
src_y += 16;
// Advance to next row of tiles.
if ((y & (tile_height - 1)) == (tile_height - 1)) {
src_y = src_y - src_tile_stride + src_stride_y * tile_height;
}
}
return 0;
}
LIBYUV_API
void DetileSplitUVPlane(const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height,
int tile_height) {
const ptrdiff_t src_tile_stride = 16 * tile_height;
int y;
void (*DetileSplitUVRow)(const uint8_t* src, ptrdiff_t src_tile_stride,
uint8_t* dst_u, uint8_t* dst_v, int width) =
DetileSplitUVRow_C;
assert(src_stride_uv >= 0);
assert(tile_height > 0);
assert(src_stride_uv > 0);
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_u = dst_u + (height - 1) * dst_stride_u;
dst_stride_u = -dst_stride_u;
dst_v = dst_v + (height - 1) * dst_stride_v;
dst_stride_v = -dst_stride_v;
}
#if defined(HAS_DETILESPLITUVROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
DetileSplitUVRow = DetileSplitUVRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
DetileSplitUVRow = DetileSplitUVRow_SSSE3;
}
}
#endif
#if defined(HAS_DETILESPLITUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
DetileSplitUVRow = DetileSplitUVRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
DetileSplitUVRow = DetileSplitUVRow_NEON;
}
}
#endif
// Detile plane
for (y = 0; y < height; ++y) {
DetileSplitUVRow(src_uv, src_tile_stride, dst_u, dst_v, width);
dst_u += dst_stride_u;
dst_v += dst_stride_v;
src_uv += 16;
// Advance to next row of tiles.
if ((y & (tile_height - 1)) == (tile_height - 1)) {
src_uv = src_uv - src_tile_stride + src_stride_uv * tile_height;
}
}
}
LIBYUV_API
void DetileToYUY2(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_yuy2,
int dst_stride_yuy2,
int width,
int height,
int tile_height) {
const ptrdiff_t src_y_tile_stride = 16 * tile_height;
const ptrdiff_t src_uv_tile_stride = src_y_tile_stride / 2;
int y;
void (*DetileToYUY2)(const uint8_t* src_y, ptrdiff_t src_y_tile_stride,
const uint8_t* src_uv, ptrdiff_t src_uv_tile_stride,
uint8_t* dst_yuy2, int width) = DetileToYUY2_C;
assert(src_stride_y >= 0);
assert(src_stride_y > 0);
assert(src_stride_uv >= 0);
assert(src_stride_uv > 0);
assert(tile_height > 0);
if (width <= 0 || height == 0 || tile_height <= 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
dst_stride_yuy2 = -dst_stride_yuy2;
}
#if defined(HAS_DETILETOYUY2_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
DetileToYUY2 = DetileToYUY2_Any_NEON;
if (IS_ALIGNED(width, 16)) {
DetileToYUY2 = DetileToYUY2_NEON;
}
}
#endif
#if defined(HAS_DETILETOYUY2_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
DetileToYUY2 = DetileToYUY2_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
DetileToYUY2 = DetileToYUY2_SSE2;
}
}
#endif
// Detile plane
for (y = 0; y < height; ++y) {
DetileToYUY2(src_y, src_y_tile_stride, src_uv, src_uv_tile_stride, dst_yuy2,
width);
dst_yuy2 += dst_stride_yuy2;
src_y += 16;
if (y & 0x1)
src_uv += 16;
// Advance to next row of tiles.
if ((y & (tile_height - 1)) == (tile_height - 1)) {
src_y = src_y - src_y_tile_stride + src_stride_y * tile_height;
src_uv = src_uv - src_uv_tile_stride + src_stride_uv * (tile_height / 2);
}
}
}
// Support function for NV12 etc RGB channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitRGBPlane(const uint8_t* src_rgb,
int src_stride_rgb,
uint8_t* dst_r,
int dst_stride_r,
uint8_t* dst_g,
int dst_stride_g,
uint8_t* dst_b,
int dst_stride_b,
int width,
int height) {
int y;
void (*SplitRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
uint8_t* dst_b, int width) = SplitRGBRow_C;
if (width <= 0 || height == 0) {
return;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_r = dst_r + (height - 1) * dst_stride_r;
dst_g = dst_g + (height - 1) * dst_stride_g;
dst_b = dst_b + (height - 1) * dst_stride_b;
dst_stride_r = -dst_stride_r;
dst_stride_g = -dst_stride_g;
dst_stride_b = -dst_stride_b;
}
// Coalesce rows.
if (src_stride_rgb == width * 3 && dst_stride_r == width &&
dst_stride_g == width && dst_stride_b == width) {
width *= height;
height = 1;
src_stride_rgb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
}
#if defined(HAS_SPLITRGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
SplitRGBRow = SplitRGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_SSSE3;
}
}
#endif
#if defined(HAS_SPLITRGBROW_SSE41)
if (TestCpuFlag(kCpuHasSSE41)) {
SplitRGBRow = SplitRGBRow_Any_SSE41;
if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_SSE41;
}
}
#endif
#if defined(HAS_SPLITRGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitRGBRow = SplitRGBRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
SplitRGBRow = SplitRGBRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITRGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitRGBRow = SplitRGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_NEON;
}
}
#endif
#if defined(HAS_SPLITRGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SplitRGBRow = SplitRGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
// Copy a row of RGB.
SplitRGBRow(src_rgb, dst_r, dst_g, dst_b, width);
dst_r += dst_stride_r;
dst_g += dst_stride_g;
dst_b += dst_stride_b;
src_rgb += src_stride_rgb;
}
}
LIBYUV_API
void MergeRGBPlane(const uint8_t* src_r,
int src_stride_r,
const uint8_t* src_g,
int src_stride_g,
const uint8_t* src_b,
int src_stride_b,
uint8_t* dst_rgb,
int dst_stride_rgb,
int width,
int height) {
int y;
void (*MergeRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
const uint8_t* src_b, uint8_t* dst_rgb, int width) =
MergeRGBRow_C;
if (width <= 0 || height == 0) {
return;
}
// Coalesce rows.
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_rgb = dst_rgb + (height - 1) * dst_stride_rgb;
dst_stride_rgb = -dst_stride_rgb;
}
// Coalesce rows.
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
dst_stride_rgb == width * 3) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = dst_stride_rgb = 0;
}
#if defined(HAS_MERGERGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
MergeRGBRow = MergeRGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
MergeRGBRow = MergeRGBRow_SSSE3;
}
}
#endif
#if defined(HAS_MERGERGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeRGBRow = MergeRGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
MergeRGBRow = MergeRGBRow_NEON;
}
}
#endif
#if defined(HAS_MERGERGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
MergeRGBRow = MergeRGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
// Merge a row of U and V into a row of RGB.
MergeRGBRow(src_r, src_g, src_b, dst_rgb, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
dst_rgb += dst_stride_rgb;
}
}
LIBYUV_NOINLINE
static void SplitARGBPlaneAlpha(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_r,
int dst_stride_r,
uint8_t* dst_g,
int dst_stride_g,
uint8_t* dst_b,
int dst_stride_b,
uint8_t* dst_a,
int dst_stride_a,
int width,
int height) {
int y;
void (*SplitARGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
uint8_t* dst_b, uint8_t* dst_a, int width) =
SplitARGBRow_C;
assert(height > 0);
if (width <= 0 || height == 0) {
return;
}
if (src_stride_argb == width * 4 && dst_stride_r == width &&
dst_stride_g == width && dst_stride_b == width && dst_stride_a == width) {
width *= height;
height = 1;
src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b =
dst_stride_a = 0;
}
#if defined(HAS_SPLITARGBROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
SplitARGBRow = SplitARGBRow_Any_SSE2;
if (IS_ALIGNED(width, 8)) {
SplitARGBRow = SplitARGBRow_SSE2;
}
}
#endif
#if defined(HAS_SPLITARGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
SplitARGBRow = SplitARGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 8)) {
SplitARGBRow = SplitARGBRow_SSSE3;
}
}
#endif
#if defined(HAS_SPLITARGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitARGBRow = SplitARGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
SplitARGBRow = SplitARGBRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITARGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitARGBRow = SplitARGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SplitARGBRow = SplitARGBRow_NEON;
}
}
#endif
#if defined(HAS_SPLITARGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SplitARGBRow = SplitARGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
SplitARGBRow(src_argb, dst_r, dst_g, dst_b, dst_a, width);
dst_r += dst_stride_r;
dst_g += dst_stride_g;
dst_b += dst_stride_b;
dst_a += dst_stride_a;
src_argb += src_stride_argb;
}
}
LIBYUV_NOINLINE
static void SplitARGBPlaneOpaque(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_r,
int dst_stride_r,
uint8_t* dst_g,
int dst_stride_g,
uint8_t* dst_b,
int dst_stride_b,
int width,
int height) {
int y;
void (*SplitXRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
uint8_t* dst_b, int width) = SplitXRGBRow_C;
assert(height > 0);
if (width <= 0 || height == 0) {
return;
}
if (src_stride_argb == width * 4 && dst_stride_r == width &&
dst_stride_g == width && dst_stride_b == width) {
width *= height;
height = 1;
src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
}
#if defined(HAS_SPLITXRGBROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
SplitXRGBRow = SplitXRGBRow_Any_SSE2;
if (IS_ALIGNED(width, 8)) {
SplitXRGBRow = SplitXRGBRow_SSE2;
}
}
#endif
#if defined(HAS_SPLITXRGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
SplitXRGBRow = SplitXRGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 8)) {
SplitXRGBRow = SplitXRGBRow_SSSE3;
}
}
#endif
#if defined(HAS_SPLITXRGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitXRGBRow = SplitXRGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
SplitXRGBRow = SplitXRGBRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITXRGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitXRGBRow = SplitXRGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SplitXRGBRow = SplitXRGBRow_NEON;
}
}
#endif
#if defined(HAS_SPLITXRGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SplitXRGBRow = SplitXRGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
SplitXRGBRow(src_argb, dst_r, dst_g, dst_b, width);
dst_r += dst_stride_r;
dst_g += dst_stride_g;
dst_b += dst_stride_b;
src_argb += src_stride_argb;
}
}
LIBYUV_API
void SplitARGBPlane(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_r,
int dst_stride_r,
uint8_t* dst_g,
int dst_stride_g,
uint8_t* dst_b,
int dst_stride_b,
uint8_t* dst_a,
int dst_stride_a,
int width,
int height) {
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_r = dst_r + (height - 1) * dst_stride_r;
dst_g = dst_g + (height - 1) * dst_stride_g;
dst_b = dst_b + (height - 1) * dst_stride_b;
dst_a = dst_a + (height - 1) * dst_stride_a;
dst_stride_r = -dst_stride_r;
dst_stride_g = -dst_stride_g;
dst_stride_b = -dst_stride_b;
dst_stride_a = -dst_stride_a;
}
if (dst_a == NULL) {
SplitARGBPlaneOpaque(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
dst_stride_g, dst_b, dst_stride_b, width, height);
} else {
SplitARGBPlaneAlpha(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
dst_stride_g, dst_b, dst_stride_b, dst_a, dst_stride_a,
width, height);
}
}
LIBYUV_NOINLINE
static void MergeARGBPlaneAlpha(const uint8_t* src_r,
int src_stride_r,
const uint8_t* src_g,
int src_stride_g,
const uint8_t* src_b,
int src_stride_b,
const uint8_t* src_a,
int src_stride_a,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*MergeARGBRow)(const uint8_t* src_r, const uint8_t* src_g,
const uint8_t* src_b, const uint8_t* src_a,
uint8_t* dst_argb, int width) = MergeARGBRow_C;
assert(height > 0);
if (width <= 0 || height == 0) {
return;
}
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
src_stride_a == width && dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = src_stride_a =
dst_stride_argb = 0;
}
#if defined(HAS_MERGEARGBROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
MergeARGBRow = MergeARGBRow_Any_SSE2;
if (IS_ALIGNED(width, 8)) {
MergeARGBRow = MergeARGBRow_SSE2;
}
}
#endif
#if defined(HAS_MERGEARGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeARGBRow = MergeARGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeARGBRow = MergeARGBRow_AVX2;
}
}
#endif
#if defined(HAS_MERGEARGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeARGBRow = MergeARGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
MergeARGBRow = MergeARGBRow_NEON;
}
}
#endif
#if defined(HAS_MERGEARGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
MergeARGBRow = MergeARGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
MergeARGBRow(src_r, src_g, src_b, src_a, dst_argb, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
src_a += src_stride_a;
dst_argb += dst_stride_argb;
}
}
LIBYUV_NOINLINE
static void MergeARGBPlaneOpaque(const uint8_t* src_r,
int src_stride_r,
const uint8_t* src_g,
int src_stride_g,
const uint8_t* src_b,
int src_stride_b,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*MergeXRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
const uint8_t* src_b, uint8_t* dst_argb, int width) =
MergeXRGBRow_C;
assert(height > 0);
if (width <= 0 || height == 0) {
return;
}
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
}
#if defined(HAS_MERGEXRGBROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
MergeXRGBRow = MergeXRGBRow_Any_SSE2;
if (IS_ALIGNED(width, 8)) {
MergeXRGBRow = MergeXRGBRow_SSE2;
}
}
#endif
#if defined(HAS_MERGEXRGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeXRGBRow = MergeXRGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeXRGBRow = MergeXRGBRow_AVX2;
}
}
#endif
#if defined(HAS_MERGEXRGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeXRGBRow = MergeXRGBRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
MergeXRGBRow = MergeXRGBRow_NEON;
}
}
#endif
#if defined(HAS_MERGEXRGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
MergeXRGBRow = MergeXRGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
MergeXRGBRow(src_r, src_g, src_b, dst_argb, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
dst_argb += dst_stride_argb;
}
}
LIBYUV_API
void MergeARGBPlane(const uint8_t* src_r,
int src_stride_r,
const uint8_t* src_g,
int src_stride_g,
const uint8_t* src_b,
int src_stride_b,
const uint8_t* src_a,
int src_stride_a,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_argb = dst_argb + (height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
if (src_a == NULL) {
MergeARGBPlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, dst_argb, dst_stride_argb, width,
height);
} else {
MergeARGBPlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, src_a, src_stride_a, dst_argb,
dst_stride_argb, width, height);
}
}
// TODO(yuan): Support 2 bit alpha channel.
LIBYUV_API
void MergeXR30Plane(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
uint8_t* dst_ar30,
int dst_stride_ar30,
int width,
int height,
int depth) {
int y;
void (*MergeXR30Row)(const uint16_t* src_r, const uint16_t* src_g,
const uint16_t* src_b, uint8_t* dst_ar30, int depth,
int width) = MergeXR30Row_C;
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_ar30 = dst_ar30 + (height - 1) * dst_stride_ar30;
dst_stride_ar30 = -dst_stride_ar30;
}
// Coalesce rows.
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
dst_stride_ar30 == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = dst_stride_ar30 = 0;
}
#if defined(HAS_MERGEXR30ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeXR30Row = MergeXR30Row_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeXR30Row = MergeXR30Row_AVX2;
}
}
#endif
#if defined(HAS_MERGEXR30ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
if (depth == 10) {
MergeXR30Row = MergeXR30Row_10_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeXR30Row = MergeXR30Row_10_NEON;
}
} else {
MergeXR30Row = MergeXR30Row_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeXR30Row = MergeXR30Row_NEON;
}
}
}
#endif
for (y = 0; y < height; ++y) {
MergeXR30Row(src_r, src_g, src_b, dst_ar30, depth, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
dst_ar30 += dst_stride_ar30;
}
}
LIBYUV_NOINLINE
static void MergeAR64PlaneAlpha(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
const uint16_t* src_a,
int src_stride_a,
uint16_t* dst_ar64,
int dst_stride_ar64,
int width,
int height,
int depth) {
int y;
void (*MergeAR64Row)(const uint16_t* src_r, const uint16_t* src_g,
const uint16_t* src_b, const uint16_t* src_a,
uint16_t* dst_argb, int depth, int width) =
MergeAR64Row_C;
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
src_stride_a == width && dst_stride_ar64 == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = src_stride_a =
dst_stride_ar64 = 0;
}
#if defined(HAS_MERGEAR64ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeAR64Row = MergeAR64Row_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeAR64Row = MergeAR64Row_AVX2;
}
}
#endif
#if defined(HAS_MERGEAR64ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeAR64Row = MergeAR64Row_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeAR64Row = MergeAR64Row_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
MergeAR64Row(src_r, src_g, src_b, src_a, dst_ar64, depth, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
src_a += src_stride_a;
dst_ar64 += dst_stride_ar64;
}
}
LIBYUV_NOINLINE
static void MergeAR64PlaneOpaque(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
uint16_t* dst_ar64,
int dst_stride_ar64,
int width,
int height,
int depth) {
int y;
void (*MergeXR64Row)(const uint16_t* src_r, const uint16_t* src_g,
const uint16_t* src_b, uint16_t* dst_argb, int depth,
int width) = MergeXR64Row_C;
// Coalesce rows.
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
dst_stride_ar64 == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = dst_stride_ar64 = 0;
}
#if defined(HAS_MERGEXR64ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeXR64Row = MergeXR64Row_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeXR64Row = MergeXR64Row_AVX2;
}
}
#endif
#if defined(HAS_MERGEXR64ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeXR64Row = MergeXR64Row_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeXR64Row = MergeXR64Row_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
MergeXR64Row(src_r, src_g, src_b, dst_ar64, depth, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
dst_ar64 += dst_stride_ar64;
}
}
LIBYUV_API
void MergeAR64Plane(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
const uint16_t* src_a,
int src_stride_a,
uint16_t* dst_ar64,
int dst_stride_ar64,
int width,
int height,
int depth) {
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_ar64 = dst_ar64 + (height - 1) * dst_stride_ar64;
dst_stride_ar64 = -dst_stride_ar64;
}
if (src_a == NULL) {
MergeAR64PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, dst_ar64, dst_stride_ar64, width, height,
depth);
} else {
MergeAR64PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, src_a, src_stride_a, dst_ar64,
dst_stride_ar64, width, height, depth);
}
}
LIBYUV_NOINLINE
static void MergeARGB16To8PlaneAlpha(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
const uint16_t* src_a,
int src_stride_a,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height,
int depth) {
int y;
void (*MergeARGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
const uint16_t* src_b, const uint16_t* src_a,
uint8_t* dst_argb, int depth, int width) =
MergeARGB16To8Row_C;
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
src_stride_a == width && dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = src_stride_a =
dst_stride_argb = 0;
}
#if defined(HAS_MERGEARGB16TO8ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeARGB16To8Row = MergeARGB16To8Row_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeARGB16To8Row = MergeARGB16To8Row_AVX2;
}
}
#endif
#if defined(HAS_MERGEARGB16TO8ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeARGB16To8Row = MergeARGB16To8Row_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeARGB16To8Row = MergeARGB16To8Row_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
MergeARGB16To8Row(src_r, src_g, src_b, src_a, dst_argb, depth, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
src_a += src_stride_a;
dst_argb += dst_stride_argb;
}
}
LIBYUV_NOINLINE
static void MergeARGB16To8PlaneOpaque(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height,
int depth) {
int y;
void (*MergeXRGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
const uint16_t* src_b, uint8_t* dst_argb, int depth,
int width) = MergeXRGB16To8Row_C;
// Coalesce rows.
if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
}
#if defined(HAS_MERGEXRGB16TO8ROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MergeXRGB16To8Row = MergeXRGB16To8Row_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MergeXRGB16To8Row = MergeXRGB16To8Row_AVX2;
}
}
#endif
#if defined(HAS_MERGEXRGB16TO8ROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MergeXRGB16To8Row = MergeXRGB16To8Row_Any_NEON;
if (IS_ALIGNED(width, 8)) {
MergeXRGB16To8Row = MergeXRGB16To8Row_NEON;
}
}
#endif
for (y = 0; y < height; ++y) {
MergeXRGB16To8Row(src_r, src_g, src_b, dst_argb, depth, width);
src_r += src_stride_r;
src_g += src_stride_g;
src_b += src_stride_b;
dst_argb += dst_stride_argb;
}
}
LIBYUV_API
void MergeARGB16To8Plane(const uint16_t* src_r,
int src_stride_r,
const uint16_t* src_g,
int src_stride_g,
const uint16_t* src_b,
int src_stride_b,
const uint16_t* src_a,
int src_stride_a,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height,
int depth) {
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_argb = dst_argb + (height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
if (src_a == NULL) {
MergeARGB16To8PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, dst_argb, dst_stride_argb, width,
height, depth);
} else {
MergeARGB16To8PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
src_stride_b, src_a, src_stride_a, dst_argb,
dst_stride_argb, width, height, depth);
}
}
// Convert YUY2 to I422.
LIBYUV_API
int YUY2ToI422(const uint8_t* src_yuy2,
int src_stride_yuy2,
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 y;
void (*YUY2ToUV422Row)(const uint8_t* src_yuy2, uint8_t* dst_u,
uint8_t* dst_v, int width) = YUY2ToUV422Row_C;
void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
YUY2ToYRow_C;
if (!src_yuy2 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
src_stride_yuy2 = -src_stride_yuy2;
}
// Coalesce rows.
if (src_stride_yuy2 == width * 2 && dst_stride_y == width &&
dst_stride_u * 2 == width && dst_stride_v * 2 == width &&
width * height <= 32768) {
width *= height;
height = 1;
src_stride_yuy2 = dst_stride_y = dst_stride_u = dst_stride_v = 0;
}
#if defined(HAS_YUY2TOYROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
YUY2ToYRow = YUY2ToYRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
YUY2ToYRow = YUY2ToYRow_SSE2;
}
}
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
YUY2ToYRow = YUY2ToYRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
YUY2ToYRow = YUY2ToYRow_AVX2;
}
}
#endif
#if defined(HAS_YUY2TOYROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
YUY2ToYRow = YUY2ToYRow_Any_NEON;
YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
if (IS_ALIGNED(width, 16)) {
YUY2ToYRow = YUY2ToYRow_NEON;
YUY2ToUV422Row = YUY2ToUV422Row_NEON;
}
}
#endif
#if defined(HAS_YUY2TOYROW_MSA) && defined(HAS_YUY2TOUV422ROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
YUY2ToYRow = YUY2ToYRow_Any_MSA;
YUY2ToUV422Row = YUY2ToUV422Row_Any_MSA;
if (IS_ALIGNED(width, 32)) {
YUY2ToYRow = YUY2ToYRow_MSA;
YUY2ToUV422Row = YUY2ToUV422Row_MSA;
}
}
#endif
#if defined(HAS_YUY2TOYROW_LSX) && defined(HAS_YUY2TOUV422ROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
YUY2ToYRow = YUY2ToYRow_Any_LSX;
YUY2ToUV422Row = YUY2ToUV422Row_Any_LSX;
if (IS_ALIGNED(width, 16)) {
YUY2ToYRow = YUY2ToYRow_LSX;
YUY2ToUV422Row = YUY2ToUV422Row_LSX;
}
}
#endif
#if defined(HAS_YUY2TOYROW_LASX) && defined(HAS_YUY2TOUV422ROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
YUY2ToYRow = YUY2ToYRow_Any_LASX;
YUY2ToUV422Row = YUY2ToUV422Row_Any_LASX;
if (IS_ALIGNED(width, 32)) {
YUY2ToYRow = YUY2ToYRow_LASX;
YUY2ToUV422Row = YUY2ToUV422Row_LASX;
}
}
#endif
for (y = 0; y < height; ++y) {
YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
YUY2ToYRow(src_yuy2, dst_y, width);
src_yuy2 += src_stride_yuy2;
dst_y += dst_stride_y;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
}
// Convert UYVY to I422.
LIBYUV_API
int UYVYToI422(const uint8_t* src_uyvy,
int src_stride_uyvy,
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 y;
void (*UYVYToUV422Row)(const uint8_t* src_uyvy, uint8_t* dst_u,
uint8_t* dst_v, int width) = UYVYToUV422Row_C;
void (*UYVYToYRow)(const uint8_t* src_uyvy, uint8_t* dst_y, int width) =
UYVYToYRow_C;
if (!src_uyvy || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
src_stride_uyvy = -src_stride_uyvy;
}
// Coalesce rows.
if (src_stride_uyvy == width * 2 && dst_stride_y == width &&
dst_stride_u * 2 == width && dst_stride_v * 2 == width &&
width * height <= 32768) {
width *= height;
height = 1;
src_stride_uyvy = dst_stride_y = dst_stride_u = dst_stride_v = 0;
}
#if defined(HAS_UYVYTOYROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
UYVYToUV422Row = UYVYToUV422Row_Any_SSE2;
UYVYToYRow = UYVYToYRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
UYVYToUV422Row = UYVYToUV422Row_SSE2;
UYVYToYRow = UYVYToYRow_SSE2;
}
}
#endif
#if defined(HAS_UYVYTOYROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
UYVYToUV422Row = UYVYToUV422Row_Any_AVX2;
UYVYToYRow = UYVYToYRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
UYVYToUV422Row = UYVYToUV422Row_AVX2;
UYVYToYRow = UYVYToYRow_AVX2;
}
}
#endif
#if defined(HAS_UYVYTOYROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
UYVYToYRow = UYVYToYRow_Any_NEON;
UYVYToUV422Row = UYVYToUV422Row_Any_NEON;
if (IS_ALIGNED(width, 16)) {
UYVYToYRow = UYVYToYRow_NEON;
UYVYToUV422Row = UYVYToUV422Row_NEON;
}
}
#endif
#if defined(HAS_UYVYTOYROW_MSA) && defined(HAS_UYVYTOUV422ROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
UYVYToYRow = UYVYToYRow_Any_MSA;
UYVYToUV422Row = UYVYToUV422Row_Any_MSA;
if (IS_ALIGNED(width, 32)) {
UYVYToYRow = UYVYToYRow_MSA;
UYVYToUV422Row = UYVYToUV422Row_MSA;
}
}
#endif
#if defined(HAS_UYVYTOYROW_LSX) && defined(HAS_UYVYTOUV422ROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
UYVYToYRow = UYVYToYRow_Any_LSX;
UYVYToUV422Row = UYVYToUV422Row_Any_LSX;
if (IS_ALIGNED(width, 16)) {
UYVYToYRow = UYVYToYRow_LSX;
UYVYToUV422Row = UYVYToUV422Row_LSX;
}
}
#endif
#if defined(HAS_UYVYTOYROW_LASX) && defined(HAS_UYVYTOUV422ROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
UYVYToYRow = UYVYToYRow_Any_LASX;
UYVYToUV422Row = UYVYToUV422Row_Any_LASX;
if (IS_ALIGNED(width, 32)) {
UYVYToYRow = UYVYToYRow_LASX;
UYVYToUV422Row = UYVYToUV422Row_LASX;
}
}
#endif
for (y = 0; y < height; ++y) {
UYVYToUV422Row(src_uyvy, dst_u, dst_v, width);
UYVYToYRow(src_uyvy, dst_y, width);
src_uyvy += src_stride_uyvy;
dst_y += dst_stride_y;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
}
// Convert YUY2 to Y.
LIBYUV_API
int YUY2ToY(const uint8_t* src_yuy2,
int src_stride_yuy2,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
int y;
void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
YUY2ToYRow_C;
if (!src_yuy2 || !dst_y || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
src_stride_yuy2 = -src_stride_yuy2;
}
// Coalesce rows.
if (src_stride_yuy2 == width * 2 && dst_stride_y == width) {
width *= height;
height = 1;
src_stride_yuy2 = dst_stride_y = 0;
}
#if defined(HAS_YUY2TOYROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
YUY2ToYRow = YUY2ToYRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
YUY2ToYRow = YUY2ToYRow_SSE2;
}
}
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
YUY2ToYRow = YUY2ToYRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
YUY2ToYRow = YUY2ToYRow_AVX2;
}
}
#endif
#if defined(HAS_YUY2TOYROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
YUY2ToYRow = YUY2ToYRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
YUY2ToYRow = YUY2ToYRow_NEON;
}
}
#endif
#if defined(HAS_YUY2TOYROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
YUY2ToYRow = YUY2ToYRow_Any_MSA;
if (IS_ALIGNED(width, 32)) {
YUY2ToYRow = YUY2ToYRow_MSA;
}
}
#endif
for (y = 0; y < height; ++y) {
YUY2ToYRow(src_yuy2, dst_y, width);
src_yuy2 += src_stride_yuy2;
dst_y += dst_stride_y;
}
return 0;
}
// Convert UYVY to Y.
LIBYUV_API
int UYVYToY(const uint8_t* src_uyvy,
int src_stride_uyvy,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
int y;
void (*UYVYToYRow)(const uint8_t* src_uyvy, uint8_t* dst_y, int width) =
UYVYToYRow_C;
if (!src_uyvy || !dst_y || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
src_stride_uyvy = -src_stride_uyvy;
}
// Coalesce rows.
if (src_stride_uyvy == width * 2 && dst_stride_y == width) {
width *= height;
height = 1;
src_stride_uyvy = dst_stride_y = 0;
}
#if defined(HAS_UYVYTOYROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
UYVYToYRow = UYVYToYRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
UYVYToYRow = UYVYToYRow_SSE2;
}
}
#endif
#if defined(HAS_UYVYTOYROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
UYVYToYRow = UYVYToYRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
UYVYToYRow = UYVYToYRow_AVX2;
}
}
#endif
#if defined(HAS_UYVYTOYROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
UYVYToYRow = UYVYToYRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
UYVYToYRow = UYVYToYRow_NEON;
}
}
#endif
#if defined(HAS_UYVYTOYROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
UYVYToYRow = UYVYToYRow_Any_MSA;
if (IS_ALIGNED(width, 32)) {
UYVYToYRow = UYVYToYRow_MSA;
}
}
#endif
#if defined(HAS_UYVYTOYROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
UYVYToYRow = UYVYToYRow_Any_LSX;
if (IS_ALIGNED(width, 16)) {
UYVYToYRow = UYVYToYRow_LSX;
}
}
#endif
for (y = 0; y < height; ++y) {
UYVYToYRow(src_uyvy, dst_y, width);
src_uyvy += src_stride_uyvy;
dst_y += dst_stride_y;
}
return 0;
}
// Mirror a plane of data.
// See Also I400Mirror
LIBYUV_API
void MirrorPlane(const uint8_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
int y;
void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C;
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_y = src_y + (height - 1) * src_stride_y;
src_stride_y = -src_stride_y;
}
#if defined(HAS_MIRRORROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MirrorRow = MirrorRow_Any_NEON;
if (IS_ALIGNED(width, 32)) {
MirrorRow = MirrorRow_NEON;
}
}
#endif
#if defined(HAS_MIRRORROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
MirrorRow = MirrorRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
MirrorRow = MirrorRow_SSSE3;
}
}
#endif
#if defined(HAS_MIRRORROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MirrorRow = MirrorRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
MirrorRow = MirrorRow_AVX2;
}
}
#endif
#if defined(HAS_MIRRORROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
MirrorRow = MirrorRow_Any_MSA;
if (IS_ALIGNED(width, 64)) {
MirrorRow = MirrorRow_MSA;
}
}
#endif
#if defined(HAS_MIRRORROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
MirrorRow = MirrorRow_Any_LSX;
if (IS_ALIGNED(width, 32)) {
MirrorRow = MirrorRow_LSX;
}
}
#endif
#if defined(HAS_MIRRORROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
MirrorRow = MirrorRow_Any_LASX;
if (IS_ALIGNED(width, 64)) {
MirrorRow = MirrorRow_LASX;
}
}
#endif
// Mirror plane
for (y = 0; y < height; ++y) {
MirrorRow(src_y, dst_y, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}
}
// Mirror a plane of UV data.
LIBYUV_API
void MirrorUVPlane(const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height) {
int y;
void (*MirrorUVRow)(const uint8_t* src, uint8_t* dst, int width) =
MirrorUVRow_C;
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_uv = src_uv + (height - 1) * src_stride_uv;
src_stride_uv = -src_stride_uv;
}
#if defined(HAS_MIRRORUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
MirrorUVRow = MirrorUVRow_Any_NEON;
if (IS_ALIGNED(width, 32)) {
MirrorUVRow = MirrorUVRow_NEON;
}
}
#endif
#if defined(HAS_MIRRORUVROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
MirrorUVRow = MirrorUVRow_Any_SSSE3;
if (IS_ALIGNED(width, 8)) {
MirrorUVRow = MirrorUVRow_SSSE3;
}
}
#endif
#if defined(HAS_MIRRORUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
MirrorUVRow = MirrorUVRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
MirrorUVRow = MirrorUVRow_AVX2;
}
}
#endif
#if defined(HAS_MIRRORUVROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
MirrorUVRow = MirrorUVRow_Any_MSA;
if (IS_ALIGNED(width, 8)) {
MirrorUVRow = MirrorUVRow_MSA;
}
}
#endif
#if defined(HAS_MIRRORUVROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
MirrorUVRow = MirrorUVRow_Any_LSX;
if (IS_ALIGNED(width, 8)) {
MirrorUVRow = MirrorUVRow_LSX;
}
}
#endif
#if defined(HAS_MIRRORUVROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
MirrorUVRow = MirrorUVRow_Any_LASX;
if (IS_ALIGNED(width, 16)) {
MirrorUVRow = MirrorUVRow_LASX;
}
}
#endif
// MirrorUV plane
for (y = 0; y < height; ++y) {
MirrorUVRow(src_uv, dst_uv, width);
src_uv += src_stride_uv;
dst_uv += dst_stride_uv;
}
}
// Mirror I400 with optional flipping
LIBYUV_API
int I400Mirror(const uint8_t* src_y,
int src_stride_y,
uint8_t* dst_y,
int dst_stride_y,
int width,
int height) {
if (!src_y || !dst_y || 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_stride_y = -src_stride_y;
}
MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
return 0;
}
// Mirror I420 with optional flipping
LIBYUV_API
int I420Mirror(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_u,
int src_stride_u,
const uint8_t* src_v,
int src_stride_v,
uint8_t* dst_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 halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if (!src_y || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_y = src_y + (height - 1) * src_stride_y;
src_u = src_u + (halfheight - 1) * src_stride_u;
src_v = src_v + (halfheight - 1) * src_stride_v;
src_stride_y = -src_stride_y;
src_stride_u = -src_stride_u;
src_stride_v = -src_stride_v;
}
if (dst_y) {
MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
MirrorPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
MirrorPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
return 0;
}
// NV12 mirror.
LIBYUV_API
int NV12Mirror(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height) {
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if (!src_y || !src_uv || !dst_uv || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_y = src_y + (height - 1) * src_stride_y;
src_uv = src_uv + (halfheight - 1) * src_stride_uv;
src_stride_y = -src_stride_y;
src_stride_uv = -src_stride_uv;
}
if (dst_y) {
MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
MirrorUVPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth,
halfheight);
return 0;
}
// ARGB mirror.
LIBYUV_API
int ARGBMirror(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*ARGBMirrorRow)(const uint8_t* src, uint8_t* dst, int width) =
ARGBMirrorRow_C;
if (!src_argb || !dst_argb || 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;
}
#if defined(HAS_ARGBMIRRORROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
ARGBMirrorRow = ARGBMirrorRow_Any_NEON;
if (IS_ALIGNED(width, 8)) {
ARGBMirrorRow = ARGBMirrorRow_NEON;
}
}
#endif
#if defined(HAS_ARGBMIRRORROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
ARGBMirrorRow = ARGBMirrorRow_Any_SSE2;
if (IS_ALIGNED(width, 4)) {
ARGBMirrorRow = ARGBMirrorRow_SSE2;
}
}
#endif
#if defined(HAS_ARGBMIRRORROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
ARGBMirrorRow = ARGBMirrorRow_Any_AVX2;
if (IS_ALIGNED(width, 8)) {
ARGBMirrorRow = ARGBMirrorRow_AVX2;
}
}
#endif
#if defined(HAS_ARGBMIRRORROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
ARGBMirrorRow = ARGBMirrorRow_Any_MSA;
if (IS_ALIGNED(width, 16)) {
ARGBMirrorRow = ARGBMirrorRow_MSA;
}
}
#endif
#if defined(HAS_ARGBMIRRORROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
ARGBMirrorRow = ARGBMirrorRow_Any_LSX;
if (IS_ALIGNED(width, 8)) {
ARGBMirrorRow = ARGBMirrorRow_LSX;
}
}
#endif
#if defined(HAS_ARGBMIRRORROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
ARGBMirrorRow = ARGBMirrorRow_Any_LASX;
if (IS_ALIGNED(width, 16)) {
ARGBMirrorRow = ARGBMirrorRow_LASX;
}
}
#endif
// Mirror plane
for (y = 0; y < height; ++y) {
ARGBMirrorRow(src_argb, dst_argb, width);
src_argb += src_stride_argb;
dst_argb += dst_stride_argb;
}
return 0;
}
// RGB24 mirror.
LIBYUV_API
int RGB24Mirror(const uint8_t* src_rgb24,
int src_stride_rgb24,
uint8_t* dst_rgb24,
int dst_stride_rgb24,
int width,
int height) {
int y;
void (*RGB24MirrorRow)(const uint8_t* src, uint8_t* dst, int width) =
RGB24MirrorRow_C;
if (!src_rgb24 || !dst_rgb24 || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
src_stride_rgb24 = -src_stride_rgb24;
}
#if defined(HAS_RGB24MIRRORROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
RGB24MirrorRow = RGB24MirrorRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
RGB24MirrorRow = RGB24MirrorRow_NEON;
}
}
#endif
#if defined(HAS_RGB24MIRRORROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
RGB24MirrorRow = RGB24MirrorRow_Any_SSSE3;
if (IS_ALIGNED(width, 16)) {
RGB24MirrorRow = RGB24MirrorRow_SSSE3;
}
}
#endif
// Mirror plane
for (y = 0; y < height; ++y) {
RGB24MirrorRow(src_rgb24, dst_rgb24, width);
src_rgb24 += src_stride_rgb24;
dst_rgb24 += dst_stride_rgb24;
}
return 0;
}
// Alpha Blend 2 ARGB images and store to destination.
LIBYUV_API
int ARGBBlend(const uint8_t* src_argb0,
int src_stride_argb0,
const uint8_t* src_argb1,
int src_stride_argb1,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*ARGBBlendRow)(const uint8_t* src_argb, const uint8_t* src_argb1,
uint8_t* dst_argb, int width) = ARGBBlendRow_C;
if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
return -1;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
dst_argb = dst_argb + (height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
// Coalesce rows.
if (src_stride_argb0 == width * 4 && src_stride_argb1 == width * 4 &&
dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
}
#if defined(HAS_ARGBBLENDROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
ARGBBlendRow = ARGBBlendRow_SSSE3;
}
#endif
#if defined(HAS_ARGBBLENDROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
ARGBBlendRow = ARGBBlendRow_NEON;
}
#endif
#if defined(HAS_ARGBBLENDROW_MSA)
if (TestCpuFlag(kCpuHasMSA)) {
ARGBBlendRow = ARGBBlendRow_MSA;
}
#endif
#if defined(HAS_ARGBBLENDROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
ARGBBlendRow = ARGBBlendRow_LSX;
}
#endif
#if defined(HAS_ARGBBLENDROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
ARGBBlendRow = ARGBBlendRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
src_argb0 += src_stride_argb0;
src_argb1 += src_stride_argb1;
dst_argb += dst_stride_argb;