/* * 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
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;
} #ifdefined(HAS_CONVERT16TO8ROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
Convert16To8Row = Convert16To8Row_Any_NEON; if (IS_ALIGNED(width, 16)) {
Convert16To8Row = Convert16To8Row_NEON;
}
} #endif #ifdefined(HAS_CONVERT16TO8ROW_SME) if (TestCpuFlag(kCpuHasSME)) {
Convert16To8Row = Convert16To8Row_SME;
} #endif #ifdefined(HAS_CONVERT16TO8ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) {
Convert16To8Row = Convert16To8Row_Any_SSSE3; if (IS_ALIGNED(width, 16)) {
Convert16To8Row = Convert16To8Row_SSSE3;
}
} #endif #ifdefined(HAS_CONVERT16TO8ROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
Convert16To8Row = Convert16To8Row_Any_AVX2; if (IS_ALIGNED(width, 32)) {
Convert16To8Row = Convert16To8Row_AVX2;
}
} #endif #ifdefined(HAS_CONVERT16TO8ROW_AVX512BW) if (TestCpuFlag(kCpuHasAVX512BW)) {
Convert16To8Row = Convert16To8Row_Any_AVX512BW; if (IS_ALIGNED(width, 64)) {
Convert16To8Row = Convert16To8Row_AVX512BW;
}
} #endif
// 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;
// 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;
// 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;
// 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;
} #ifdefined(HAS_SPLITUVROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
SplitUVRow = SplitUVRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_SSE2;
}
} #endif #ifdefined(HAS_SPLITUVROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow = SplitUVRow_Any_AVX2; if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_AVX2;
}
} #endif #ifdefined(HAS_SPLITUVROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
SplitUVRow = SplitUVRow_Any_NEON; if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_NEON;
}
} #endif #ifdefined(HAS_SPLITUVROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
SplitUVRow = SplitUVRow_Any_MSA; if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_MSA;
}
} #endif #ifdefined(HAS_SPLITUVROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
SplitUVRow = SplitUVRow_Any_LSX; if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_LSX;
}
} #endif #ifdefined(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;
} #ifdefined(HAS_MERGEUVROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
MergeUVRow = MergeUVRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_SSE2;
}
} #endif #ifdefined(HAS_MERGEUVROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
MergeUVRow = MergeUVRow_Any_AVX2; if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_AVX2;
}
} #endif #ifdefined(HAS_MERGEUVROW_AVX512BW) if (TestCpuFlag(kCpuHasAVX512BW)) {
MergeUVRow = MergeUVRow_Any_AVX512BW; if (IS_ALIGNED(width, 32)) {
MergeUVRow = MergeUVRow_AVX512BW;
}
} #endif #ifdefined(HAS_MERGEUVROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
MergeUVRow = MergeUVRow_Any_NEON; if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_NEON;
}
} #endif #ifdefined(HAS_MERGEUVROW_SME) if (TestCpuFlag(kCpuHasSME)) {
MergeUVRow = MergeUVRow_SME;
} #endif #ifdefined(HAS_MERGEUVROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
MergeUVRow = MergeUVRow_Any_MSA; if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_MSA;
}
} #endif #ifdefined(HAS_MERGEUVROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
MergeUVRow = MergeUVRow_Any_LSX; if (IS_ALIGNED(width, 16)) {
MergeUVRow = MergeUVRow_LSX;
}
} #endif #ifdefined(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;
} #ifdefined(HAS_SPLITUVROW_16_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow_16 = SplitUVRow_16_Any_AVX2; if (IS_ALIGNED(width, 16)) {
SplitUVRow_16 = SplitUVRow_16_AVX2;
}
} #endif #ifdefined(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;
} #ifdefined(HAS_MERGEUVROW_16_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
MergeUVRow_16 = MergeUVRow_16_Any_AVX2; if (IS_ALIGNED(width, 8)) {
MergeUVRow_16 = MergeUVRow_16_AVX2;
}
} #endif #ifdefined(HAS_MERGEUVROW_16_NEON) if (TestCpuFlag(kCpuHasNEON)) {
MergeUVRow_16 = MergeUVRow_16_Any_NEON; if (IS_ALIGNED(width, 8)) {
MergeUVRow_16 = MergeUVRow_16_NEON;
}
} #endif #ifdefined(HAS_MERGEUVROW_16_SME) if (TestCpuFlag(kCpuHasSME)) {
MergeUVRow_16 = MergeUVRow_16_SME;
} #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;
}
#ifdefined(HAS_MULTIPLYROW_16_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
MultiplyRow_16 = MultiplyRow_16_Any_AVX2; if (IS_ALIGNED(width, 32)) {
MultiplyRow_16 = MultiplyRow_16_AVX2;
}
} #endif #ifdefined(HAS_MULTIPLYROW_16_NEON) if (TestCpuFlag(kCpuHasNEON)) {
MultiplyRow_16 = MultiplyRow_16_Any_NEON; if (IS_ALIGNED(width, 16)) {
MultiplyRow_16 = MultiplyRow_16_NEON;
}
} #endif #ifdefined(HAS_MULTIPLYROW_16_SME) if (TestCpuFlag(kCpuHasSME)) {
MultiplyRow_16 = MultiplyRow_16_SME;
} #endif
// 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;
}
#ifdefined(HAS_SWAPUVROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) {
SwapUVRow = SwapUVRow_Any_SSSE3; if (IS_ALIGNED(width, 16)) {
SwapUVRow = SwapUVRow_SSSE3;
}
} #endif #ifdefined(HAS_SWAPUVROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
SwapUVRow = SwapUVRow_Any_AVX2; if (IS_ALIGNED(width, 32)) {
SwapUVRow = SwapUVRow_AVX2;
}
} #endif #ifdefined(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;
// 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;
}
#ifdefined(HAS_DETILEROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
DetileRow = DetileRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
DetileRow = DetileRow_SSE2;
}
} #endif #ifdefined(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;
}
#ifdefined(HAS_DETILEROW_16_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
DetileRow_16 = DetileRow_16_Any_SSE2; if (IS_ALIGNED(width, 16)) {
DetileRow_16 = DetileRow_16_SSE2;
}
} #endif #ifdefined(HAS_DETILEROW_16_AVX) if (TestCpuFlag(kCpuHasAVX)) {
DetileRow_16 = DetileRow_16_Any_AVX; if (IS_ALIGNED(width, 16)) {
DetileRow_16 = DetileRow_16_AVX;
}
} #endif #ifdefined(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);
// 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;
} #ifdefined(HAS_SPLITRGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) {
SplitRGBRow = SplitRGBRow_Any_SSSE3; if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_SSSE3;
}
} #endif #ifdefined(HAS_SPLITRGBROW_SSE41) if (TestCpuFlag(kCpuHasSSE41)) {
SplitRGBRow = SplitRGBRow_Any_SSE41; if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_SSE41;
}
} #endif #ifdefined(HAS_SPLITRGBROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
SplitRGBRow = SplitRGBRow_Any_AVX2; if (IS_ALIGNED(width, 32)) {
SplitRGBRow = SplitRGBRow_AVX2;
}
} #endif #ifdefined(HAS_SPLITRGBROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
SplitRGBRow = SplitRGBRow_Any_NEON; if (IS_ALIGNED(width, 16)) {
SplitRGBRow = SplitRGBRow_NEON;
}
} #endif #ifdefined(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;
} #ifdefined(HAS_MERGERGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) {
MergeRGBRow = MergeRGBRow_Any_SSSE3; if (IS_ALIGNED(width, 16)) {
MergeRGBRow = MergeRGBRow_SSSE3;
}
} #endif #ifdefined(HAS_MERGERGBROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
MergeRGBRow = MergeRGBRow_Any_NEON; if (IS_ALIGNED(width, 16)) {
MergeRGBRow = MergeRGBRow_NEON;
}
} #endif #ifdefined(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 staticvoid 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;
// 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;
LIBYUV_NOINLINE staticvoid 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;
LIBYUV_NOINLINE staticvoid 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;
// 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;
// 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;
// TODO(fbarchard): Consider uint8_t value
LIBYUV_API void SetPlane(uint8_t* dst_y, int dst_stride_y, int width, int height,
uint32_t value) { int y; void (*SetRow)(uint8_t* dst, uint8_t value, int width) = SetRow_C;
// Draw a rectangle into ARGB
LIBYUV_API int ARGBRect(uint8_t* dst_argb, int dst_stride_argb, int dst_x, int dst_y, int width, int height,
uint32_t value) { int y; void (*ARGBSetRow)(uint8_t* dst_argb, uint32_t value, int width) =
ARGBSetRow_C; if (!dst_argb || width <= 0 || height == 0 || dst_x < 0 || dst_y < 0) { return -1;
} if (height < 0) {
height = -height;
dst_argb = dst_argb + (height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
dst_argb += dst_y * dst_stride_argb + dst_x * 4; // Coalesce rows. if (dst_stride_argb == width * 4) {
width *= height;
height = 1;
dst_stride_argb = 0;
}
#ifdefined(HAS_ARGBSETROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
ARGBSetRow = ARGBSetRow_Any_NEON; if (IS_ALIGNED(width, 4)) {
ARGBSetRow = ARGBSetRow_NEON;
}
} #endif #ifdefined(HAS_ARGBSETROW_X86) if (TestCpuFlag(kCpuHasX86)) {
ARGBSetRow = ARGBSetRow_X86;
} #endif #ifdefined(HAS_ARGBSETROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
ARGBSetRow = ARGBSetRow_Any_MSA; if (IS_ALIGNED(width, 4)) {
ARGBSetRow = ARGBSetRow_MSA;
}
} #endif #ifdefined(HAS_ARGBSETROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
ARGBSetRow = ARGBSetRow_Any_LSX; if (IS_ALIGNED(width, 4)) {
ARGBSetRow = ARGBSetRow_LSX;
}
} #endif
// Set plane for (y = 0; y < height; ++y) {
ARGBSetRow(dst_argb, value, width);
dst_argb += dst_stride_argb;
} return 0;
}
// Convert unattentuated ARGB to preattenuated ARGB. // An unattenutated ARGB alpha blend uses the formula // p = a * f + (1 - a) * b // where // p is output pixel // f is foreground pixel // b is background pixel // a is alpha value from foreground pixel // An preattenutated ARGB alpha blend uses the formula // p = f + (1 - a) * b // where // f is foreground pixel premultiplied by alpha
LIBYUV_API int ARGBAttenuate(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_argb, int dst_stride_argb, int width, int height) { int y; void (*ARGBAttenuateRow)(const uint8_t* src_argb, uint8_t* dst_argb, int width) = ARGBAttenuateRow_C; if (!src_argb || !dst_argb || width <= 0 || height == 0) { return -1;
} if (height < 0) {
height = -height;
src_argb = src_argb + (height - 1) * src_stride_argb;
src_stride_argb = -src_stride_argb;
} // Coalesce rows. if (src_stride_argb == width * 4 && dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_argb = dst_stride_argb = 0;
} #ifdefined(HAS_ARGBATTENUATEROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_SSSE3; if (IS_ALIGNED(width, 4)) {
ARGBAttenuateRow = ARGBAttenuateRow_SSSE3;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_AVX2; if (IS_ALIGNED(width, 8)) {
ARGBAttenuateRow = ARGBAttenuateRow_AVX2;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_NEON; if (IS_ALIGNED(width, 8)) {
ARGBAttenuateRow = ARGBAttenuateRow_NEON;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_MSA; if (IS_ALIGNED(width, 8)) {
ARGBAttenuateRow = ARGBAttenuateRow_MSA;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; if (IS_ALIGNED(width, 8)) {
ARGBAttenuateRow = ARGBAttenuateRow_LSX;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_LASX) if (TestCpuFlag(kCpuHasLASX)) {
ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; if (IS_ALIGNED(width, 16)) {
ARGBAttenuateRow = ARGBAttenuateRow_LASX;
}
} #endif #ifdefined(HAS_ARGBATTENUATEROW_RVV) if (TestCpuFlag(kCpuHasRVV)) {
ARGBAttenuateRow = ARGBAttenuateRow_RVV;
} #endif
// Apply a color table each ARGB pixel. // Table contains 256 ARGB values.
LIBYUV_API int ARGBColorTable(uint8_t* dst_argb, int dst_stride_argb, const uint8_t* table_argb, int dst_x, int dst_y, int width, int height) { int y; void (*ARGBColorTableRow)(uint8_t* dst_argb, const uint8_t* table_argb, int width) = ARGBColorTableRow_C;
uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4; if (!dst_argb || !table_argb || width <= 0 || height <= 0 || dst_x < 0 ||
dst_y < 0) { return -1;
} // Coalesce rows. if (dst_stride_argb == width * 4) {
width *= height;
height = 1;
dst_stride_argb = 0;
} #ifdefined(HAS_ARGBCOLORTABLEROW_X86) if (TestCpuFlag(kCpuHasX86)) {
ARGBColorTableRow = ARGBColorTableRow_X86;
} #endif for (y = 0; y < height; ++y) {
ARGBColorTableRow(dst, table_argb, width);
dst += dst_stride_argb;
} return 0;
}
// Apply a color table each ARGB pixel but preserve destination alpha. // Table contains 256 ARGB values.
LIBYUV_API int RGBColorTable(uint8_t* dst_argb, int dst_stride_argb, const uint8_t* table_argb, int dst_x, int dst_y, int width, int height) { int y; void (*RGBColorTableRow)(uint8_t* dst_argb, const uint8_t* table_argb, int width) = RGBColorTableRow_C;
uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4; if (!dst_argb || !table_argb || width <= 0 || height <= 0 || dst_x < 0 ||
dst_y < 0) { return -1;
} // Coalesce rows. if (dst_stride_argb == width * 4) {
width *= height;
height = 1;
dst_stride_argb = 0;
} #ifdefined(HAS_RGBCOLORTABLEROW_X86) if (TestCpuFlag(kCpuHasX86)) {
RGBColorTableRow = RGBColorTableRow_X86;
} #endif for (y = 0; y < height; ++y) {
RGBColorTableRow(dst, table_argb, width);
dst += dst_stride_argb;
} return 0;
}
// ARGBQuantize is used to posterize art. // e.g. rgb / qvalue * qvalue + qvalue / 2 // But the low levels implement efficiently with 3 parameters, and could be // used for other high level operations. // dst_argb[0] = (b * scale >> 16) * interval_size + interval_offset; // where scale is 1 / interval_size as a fixed point value. // The divide is replaces with a multiply by reciprocal fixed point multiply. // Caveat - although SSE2 saturates, the C function does not and should be used // with care if doing anything but quantization.
LIBYUV_API int ARGBQuantize(uint8_t* dst_argb, int dst_stride_argb, int scale, int interval_size, int interval_offset, int dst_x, int dst_y, int width, int height) { int y; void (*ARGBQuantizeRow)(uint8_t* dst_argb, int scale, int interval_size, int interval_offset, int width) = ARGBQuantizeRow_C;
uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4; if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0 ||
interval_size < 1 || interval_size > 255) { return -1;
} // Coalesce rows. if (dst_stride_argb == width * 4) {
width *= height;
height = 1;
dst_stride_argb = 0;
} #ifdefined(HAS_ARGBQUANTIZEROW_SSE2) if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
ARGBQuantizeRow = ARGBQuantizeRow_SSE2;
} #endif #ifdefined(HAS_ARGBQUANTIZEROW_NEON) if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
ARGBQuantizeRow = ARGBQuantizeRow_NEON;
} #endif #ifdefined(HAS_ARGBQUANTIZEROW_MSA) if (TestCpuFlag(kCpuHasMSA) && IS_ALIGNED(width, 8)) {
ARGBQuantizeRow = ARGBQuantizeRow_MSA;
} #endif #ifdefined(HAS_ARGBQUANTIZEROW_LSX) if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
ARGBQuantizeRow = ARGBQuantizeRow_LSX;
} #endif for (y = 0; y < height; ++y) {
ARGBQuantizeRow(dst, scale, interval_size, interval_offset, width);
dst += dst_stride_argb;
} return 0;
}
// Computes table of cumulative sum for image where the value is the sum // of all values above and to the left of the entry. Used by ARGBBlur.
LIBYUV_API int ARGBComputeCumulativeSum(const uint8_t* src_argb, int src_stride_argb,
int32_t* dst_cumsum, int dst_stride32_cumsum, int width, int height) { int y; void (*ComputeCumulativeSumRow)(const uint8_t* row, int32_t* cumsum, const int32_t* previous_cumsum, int width) =
ComputeCumulativeSumRow_C;
int32_t* previous_cumsum = dst_cumsum; if (!dst_cumsum || !src_argb || width <= 0 || height <= 0) { return -1;
} #ifdefined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
} #endif
memset(dst_cumsum, 0, width * sizeof(dst_cumsum[0]) * 4); // 4 int per pixel. for (y = 0; y < height; ++y) {
ComputeCumulativeSumRow(src_argb, dst_cumsum, previous_cumsum, width);
previous_cumsum = dst_cumsum;
dst_cumsum += dst_stride32_cumsum;
src_argb += src_stride_argb;
} return 0;
}
// Blur ARGB image. // Caller should allocate CumulativeSum table of width * height * 16 bytes // aligned to 16 byte boundary. height can be radius * 2 + 2 to save memory // as the buffer is treated as circular.
LIBYUV_API int ARGBBlur(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_argb, int dst_stride_argb,
int32_t* dst_cumsum, int dst_stride32_cumsum, int width, int height, int radius) { int y; void (*ComputeCumulativeSumRow)(const uint8_t* row, int32_t* cumsum, const int32_t* previous_cumsum, int width) =
ComputeCumulativeSumRow_C; void (*CumulativeSumToAverageRow)( const int32_t* topleft, const int32_t* botleft, int width, int area,
uint8_t* dst, int count) = CumulativeSumToAverageRow_C;
int32_t* cumsum_bot_row;
int32_t* max_cumsum_bot_row;
int32_t* cumsum_top_row;
if (!src_argb || !dst_argb || width <= 0 || height == 0) { return -1;
} if (height < 0) {
height = -height;
src_argb = src_argb + (height - 1) * src_stride_argb;
src_stride_argb = -src_stride_argb;
} if (radius > height) {
radius = height;
} if (radius > (width / 2 - 1)) {
radius = width / 2 - 1;
} if (radius <= 0 || height <= 1) { return -1;
} #ifdefined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
CumulativeSumToAverageRow = CumulativeSumToAverageRow_SSE2;
} #endif // Compute enough CumulativeSum for first row to be blurred. After this // one row of CumulativeSum is updated at a time.
ARGBComputeCumulativeSum(src_argb, src_stride_argb, dst_cumsum,
dst_stride32_cumsum, width, radius);
// Interpolate 2 ARGB images by specified amount (0 to 255).
LIBYUV_API int ARGBInterpolate(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 interpolation) { return InterpolatePlane(src_argb0, src_stride_argb0, src_argb1,
src_stride_argb1, dst_argb, dst_stride_argb,
width * 4, height, interpolation);
}
// Interpolate 2 YUV images by specified amount (0 to 255).
LIBYUV_API int I420Interpolate(const uint8_t* src0_y, int src0_stride_y, const uint8_t* src0_u, int src0_stride_u, const uint8_t* src0_v, int src0_stride_v, const uint8_t* src1_y, int src1_stride_y, const uint8_t* src1_u, int src1_stride_u, const uint8_t* src1_v, int src1_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 interpolation) { int halfwidth = (width + 1) >> 1; int halfheight = (height + 1) >> 1;
// Gauss blur a float plane using Gaussian 5x5 filter with // coefficients of 1, 4, 6, 4, 1. // Each destination pixel is a blur of the 5x5 // pixels from the source. // Source edges are clamped. // Edge is 2 pixels on each side, and interior is multiple of 4.
LIBYUV_API int GaussPlane_F32(constfloat* src, int src_stride, float* dst, int dst_stride, int width, int height) { int y; void (*GaussCol_F32)(constfloat* src0, constfloat* src1, constfloat* src2, constfloat* src3, constfloat* src4, float* dst, int width) = GaussCol_F32_C; void (*GaussRow_F32)(constfloat* src, float* dst, int width) =
GaussRow_F32_C; if (!src || !dst || width <= 0 || height == 0) { return -1;
} // Negative height means invert the image. if (height < 0) {
height = -height;
src = src + (height - 1) * src_stride;
src_stride = -src_stride;
}
// Sobel ARGB effect.
LIBYUV_API int ARGBSobel(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_argb, int dst_stride_argb, int width, int height) { void (*SobelRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
uint8_t* dst_argb, int width) = SobelRow_C; #ifdefined(HAS_SOBELROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
SobelRow = SobelRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
SobelRow = SobelRow_SSE2;
}
} #endif #ifdefined(HAS_SOBELROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
SobelRow = SobelRow_Any_NEON; if (IS_ALIGNED(width, 8)) {
SobelRow = SobelRow_NEON;
}
} #endif #ifdefined(HAS_SOBELROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
SobelRow = SobelRow_Any_MSA; if (IS_ALIGNED(width, 16)) {
SobelRow = SobelRow_MSA;
}
} #endif #ifdefined(HAS_SOBELROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
SobelRow = SobelRow_Any_LSX; if (IS_ALIGNED(width, 16)) {
SobelRow = SobelRow_LSX;
}
} #endif return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
width, height, SobelRow);
}
// Sobel ARGB effect with planar output.
LIBYUV_API int ARGBSobelToPlane(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_y, int dst_stride_y, int width, int height) { void (*SobelToPlaneRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
uint8_t* dst_, int width) = SobelToPlaneRow_C; #ifdefined(HAS_SOBELTOPLANEROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
SobelToPlaneRow = SobelToPlaneRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
SobelToPlaneRow = SobelToPlaneRow_SSE2;
}
} #endif #ifdefined(HAS_SOBELTOPLANEROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
SobelToPlaneRow = SobelToPlaneRow_Any_NEON; if (IS_ALIGNED(width, 16)) {
SobelToPlaneRow = SobelToPlaneRow_NEON;
}
} #endif #ifdefined(HAS_SOBELTOPLANEROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
SobelToPlaneRow = SobelToPlaneRow_Any_MSA; if (IS_ALIGNED(width, 32)) {
SobelToPlaneRow = SobelToPlaneRow_MSA;
}
} #endif #ifdefined(HAS_SOBELTOPLANEROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
SobelToPlaneRow = SobelToPlaneRow_Any_LSX; if (IS_ALIGNED(width, 32)) {
SobelToPlaneRow = SobelToPlaneRow_LSX;
}
} #endif return ARGBSobelize(src_argb, src_stride_argb, dst_y, dst_stride_y, width,
height, SobelToPlaneRow);
}
// SobelXY ARGB effect. // Similar to Sobel, but also stores Sobel X in R and Sobel Y in B. G = Sobel.
LIBYUV_API int ARGBSobelXY(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_argb, int dst_stride_argb, int width, int height) { void (*SobelXYRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
uint8_t* dst_argb, int width) = SobelXYRow_C; #ifdefined(HAS_SOBELXYROW_SSE2) if (TestCpuFlag(kCpuHasSSE2)) {
SobelXYRow = SobelXYRow_Any_SSE2; if (IS_ALIGNED(width, 16)) {
SobelXYRow = SobelXYRow_SSE2;
}
} #endif #ifdefined(HAS_SOBELXYROW_NEON) if (TestCpuFlag(kCpuHasNEON)) {
SobelXYRow = SobelXYRow_Any_NEON; if (IS_ALIGNED(width, 8)) {
SobelXYRow = SobelXYRow_NEON;
}
} #endif #ifdefined(HAS_SOBELXYROW_MSA) if (TestCpuFlag(kCpuHasMSA)) {
SobelXYRow = SobelXYRow_Any_MSA; if (IS_ALIGNED(width, 16)) {
SobelXYRow = SobelXYRow_MSA;
}
} #endif #ifdefined(HAS_SOBELXYROW_LSX) if (TestCpuFlag(kCpuHasLSX)) {
SobelXYRow = SobelXYRow_Any_LSX; if (IS_ALIGNED(width, 16)) {
SobelXYRow = SobelXYRow_LSX;
}
} #endif return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
width, height, SobelXYRow);
}
// Apply a 4x4 polynomial to each ARGB pixel.
LIBYUV_API int ARGBPolynomial(const uint8_t* src_argb, int src_stride_argb,
uint8_t* dst_argb, int dst_stride_argb, constfloat* poly, int width, int height) { int y; void (*ARGBPolynomialRow)(const uint8_t* src_argb, uint8_t* dst_argb, constfloat* poly, int width) = ARGBPolynomialRow_C; if (!src_argb || !dst_argb || !poly || 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;
} // Coalesce rows. if (src_stride_argb == width * 4 && dst_stride_argb == width * 4) {
width *= height;
height = 1;
src_stride_argb = dst_stride_argb = 0;
} #ifdefined(HAS_ARGBPOLYNOMIALROW_SSE2) if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 2)) {
ARGBPolynomialRow = ARGBPolynomialRow_SSE2;
} #endif #ifdefined(HAS_ARGBPOLYNOMIALROW_AVX2) if (TestCpuFlag(kCpuHasAVX2) && TestCpuFlag(kCpuHasFMA3) &&
IS_ALIGNED(width, 2)) {
ARGBPolynomialRow = ARGBPolynomialRow_AVX2;
} #endif
// width and height are src size allowing odd size handling.
LIBYUV_API void HalfMergeUVPlane(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 (*HalfMergeUVRow)(const uint8_t* src_u, int src_stride_u, const uint8_t* src_v, int src_stride_v,
uint8_t* dst_uv, int width) = HalfMergeUVRow_C;
for (y = 0; y < height - 1; y += 2) { // Merge a row of U and V into a row of UV.
HalfMergeUVRow(src_u, src_stride_u, src_v, src_stride_v, dst_uv, width);
src_u += src_stride_u * 2;
src_v += src_stride_v * 2;
dst_uv += dst_stride_uv;
} if (height & 1) {
HalfMergeUVRow(src_u, 0, src_v, 0, dst_uv, width);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.112Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.