Android420ToI420Rotate function to convert with rotation

- adapted from Android420ToI420, adding a rotation parameter
- SplitRotateUV added to rotate and split the UV channel of NV12 or NV21
- rename RotateUV functions to SplitRotateUV

Bug: b/203549508
Change-Id: I6774da5fb5908fdf1fc12393f0001f41bbda9851
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3251282
Reviewed-by: richard winterton <rrwinterton@gmail.com>
Commit-Queue: Frank Barchard <fbarchard@chromium.org>
This commit is contained in:
Frank Barchard 2021-10-28 13:30:23 -07:00 committed by libyuv LUCI CQ
parent b179f1847a
commit fa043c7a64
10 changed files with 430 additions and 203 deletions

View File

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

View File

@ -83,6 +83,26 @@ int NV12ToI420Rotate(const uint8_t* src_y,
int height, int height,
enum RotationMode mode); enum RotationMode mode);
// Convert Android420 to I420 with rotation.
// "rotation" can be 0, 90, 180 or 270.
LIBYUV_API
int Android420ToI420Rotate(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,
int src_pixel_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height,
enum RotationMode rotation);
// Rotate a plane by 0, 90, 180, or 270. // Rotate a plane by 0, 90, 180, or 270.
LIBYUV_API LIBYUV_API
int RotatePlane(const uint8_t* src, int RotatePlane(const uint8_t* src,
@ -119,38 +139,50 @@ void RotatePlane270(const uint8_t* src,
int height); int height);
// Rotations for when U and V are interleaved. // Rotations for when U and V are interleaved.
// These functions take one input pointer and // These functions take one UV input pointer and
// split the data into two buffers while // split the data into two buffers while
// rotating them. Deprecated. // rotating them.
// width and height expected to be half size for NV12.
LIBYUV_API LIBYUV_API
void RotateUV90(const uint8_t* src, int SplitRotateUV(const uint8_t* src_uv,
int src_stride, int src_stride_uv,
uint8_t* dst_a, uint8_t* dst_u,
int dst_stride_a, int dst_stride_u,
uint8_t* dst_b, uint8_t* dst_v,
int dst_stride_b, int dst_stride_v,
int width, int width,
int height); int height,
enum RotationMode mode);
LIBYUV_API LIBYUV_API
void RotateUV180(const uint8_t* src, void SplitRotateUV90(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height); int height);
LIBYUV_API LIBYUV_API
void RotateUV270(const uint8_t* src, void SplitRotateUV180(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height); int height);
LIBYUV_API
void SplitRotateUV270(const uint8_t* src,
int src_stride,
uint8_t* dst_a,
int dst_stride_a,
uint8_t* dst_b,
int dst_stride_b,
int width,
int height);
// The 90 and 270 functions are based on transposes. // The 90 and 270 functions are based on transposes.
// Doing a transpose with reversing the read/write // Doing a transpose with reversing the read/write
@ -165,14 +197,14 @@ void TransposePlane(const uint8_t* src,
int height); int height);
LIBYUV_API LIBYUV_API
void TransposeUV(const uint8_t* src, void SplitTransposeUV(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height); int height);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@ -1099,15 +1099,15 @@ void RAWToUVRow_NEON(const uint8_t* src_raw,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24, void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24,
int src_stride_rgb24, int src_stride_rgb24,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RAWToUVJRow_NEON(const uint8_t* src_raw, void RAWToUVJRow_NEON(const uint8_t* src_raw,
int src_stride_raw, int src_stride_raw,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB565ToUVRow_NEON(const uint8_t* src_rgb565, void RGB565ToUVRow_NEON(const uint8_t* src_rgb565,
int src_stride_rgb565, int src_stride_rgb565,
uint8_t* dst_u, uint8_t* dst_u,
@ -1446,15 +1446,15 @@ void RAWToUVRow_Any_NEON(const uint8_t* src_ptr,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB24ToUVJRow_Any_NEON(const uint8_t* src_ptr, void RGB24ToUVJRow_Any_NEON(const uint8_t* src_ptr,
int src_stride, int src_stride,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RAWToUVJRow_Any_NEON(const uint8_t* src_ptr, void RAWToUVJRow_Any_NEON(const uint8_t* src_ptr,
int src_stride, int src_stride,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB565ToUVRow_Any_NEON(const uint8_t* src_ptr, void RGB565ToUVRow_Any_NEON(const uint8_t* src_ptr,
int src_stride, int src_stride,
uint8_t* dst_u, uint8_t* dst_u,
@ -1601,15 +1601,15 @@ void RAWToUVRow_C(const uint8_t* src_rgb,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB24ToUVJRow_C(const uint8_t* src_rgb, void RGB24ToUVJRow_C(const uint8_t* src_rgb,
int src_stride_rgb, int src_stride_rgb,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RAWToUVJRow_C(const uint8_t* src_rgb, void RAWToUVJRow_C(const uint8_t* src_rgb,
int src_stride_rgb, int src_stride_rgb,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width); int width);
void RGB565ToUVRow_C(const uint8_t* src_rgb565, void RGB565ToUVRow_C(const uint8_t* src_rgb565,
int src_stride_rgb565, int src_stride_rgb565,
uint8_t* dst_u, uint8_t* dst_u,

View File

@ -11,6 +11,6 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_ #ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_ #define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 1801 #define LIBYUV_VERSION 1802
#endif // INCLUDE_LIBYUV_VERSION_H_ #endif // INCLUDE_LIBYUV_VERSION_H_

View File

@ -1951,7 +1951,7 @@ int RGB24ToJ420(const uint8_t* src_rgb24,
int y; int y;
#if defined(HAS_RGB24TOYJROW) #if defined(HAS_RGB24TOYJROW)
void (*RGB24ToUVJRow)(const uint8_t* src_rgb24, int src_stride_rgb24, void (*RGB24ToUVJRow)(const uint8_t* src_rgb24, int src_stride_rgb24,
uint8_t* dst_u, uint8_t* dst_v, int width) = uint8_t* dst_u, uint8_t* dst_v, int width) =
RGB24ToUVJRow_C; RGB24ToUVJRow_C;
void (*RGB24ToYJRow)(const uint8_t* src_rgb24, uint8_t* dst_y, int width) = void (*RGB24ToYJRow)(const uint8_t* src_rgb24, uint8_t* dst_y, int width) =
RGB24ToYJRow_C; RGB24ToYJRow_C;
@ -1959,7 +1959,7 @@ int RGB24ToJ420(const uint8_t* src_rgb24,
void (*RGB24ToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) = void (*RGB24ToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) =
RGB24ToARGBRow_C; RGB24ToARGBRow_C;
void (*ARGBToUVJRow)(const uint8_t* src_argb0, int src_stride_argb, void (*ARGBToUVJRow)(const uint8_t* src_argb0, int src_stride_argb,
uint8_t* dst_u, uint8_t* dst_v, int width) = uint8_t* dst_u, uint8_t* dst_v, int width) =
ARGBToUVJRow_C; ARGBToUVJRow_C;
void (*ARGBToYJRow)(const uint8_t* src_argb, uint8_t* dst_y, int width) = void (*ARGBToYJRow)(const uint8_t* src_argb, uint8_t* dst_y, int width) =
ARGBToYJRow_C; ARGBToYJRow_C;
@ -2108,20 +2108,19 @@ int RGB24ToJ420(const uint8_t* src_rgb24,
// Convert RAW to I420. // Convert RAW to I420.
LIBYUV_API LIBYUV_API
int RAWToI420(const uint8_t* src_raw, int RAWToI420(const uint8_t* src_raw,
int src_stride_raw, int src_stride_raw,
uint8_t* dst_y, uint8_t* dst_y,
int dst_stride_y, int dst_stride_y,
uint8_t* dst_u, uint8_t* dst_u,
int dst_stride_u, int dst_stride_u,
uint8_t* dst_v, uint8_t* dst_v,
int dst_stride_v, int dst_stride_v,
int width, int width,
int height) { int height) {
int y; int y;
#if defined(HAS_RAWTOYROW) #if defined(HAS_RAWTOYROW)
void (*RAWToUVRow)(const uint8_t* src_raw, int src_stride_raw, void (*RAWToUVRow)(const uint8_t* src_raw, int src_stride_raw, uint8_t* dst_u,
uint8_t* dst_u, uint8_t* dst_v, int width) = uint8_t* dst_v, int width) = RAWToUVRow_C;
RAWToUVRow_C;
void (*RAWToYRow)(const uint8_t* src_raw, uint8_t* dst_y, int width) = void (*RAWToYRow)(const uint8_t* src_raw, uint8_t* dst_y, int width) =
RAWToYRow_C; RAWToYRow_C;
#else #else
@ -2277,19 +2276,19 @@ int RAWToI420(const uint8_t* src_raw,
// Convert RAW to J420. // Convert RAW to J420.
LIBYUV_API LIBYUV_API
int RAWToJ420(const uint8_t* src_raw, int RAWToJ420(const uint8_t* src_raw,
int src_stride_raw, int src_stride_raw,
uint8_t* dst_y, uint8_t* dst_y,
int dst_stride_y, int dst_stride_y,
uint8_t* dst_u, uint8_t* dst_u,
int dst_stride_u, int dst_stride_u,
uint8_t* dst_v, uint8_t* dst_v,
int dst_stride_v, int dst_stride_v,
int width, int width,
int height) { int height) {
int y; int y;
#if defined(HAS_RAWTOYJROW) #if defined(HAS_RAWTOYJROW)
void (*RAWToUVJRow)(const uint8_t* src_raw, int src_stride_raw, void (*RAWToUVJRow)(const uint8_t* src_raw, int src_stride_raw,
uint8_t* dst_u, uint8_t* dst_v, int width) = uint8_t* dst_u, uint8_t* dst_v, int width) =
RAWToUVJRow_C; RAWToUVJRow_C;
void (*RAWToYJRow)(const uint8_t* src_raw, uint8_t* dst_y, int width) = void (*RAWToYJRow)(const uint8_t* src_raw, uint8_t* dst_y, int width) =
RAWToYJRow_C; RAWToYJRow_C;
@ -2297,7 +2296,7 @@ int RAWToJ420(const uint8_t* src_raw,
void (*RAWToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) = void (*RAWToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) =
RAWToARGBRow_C; RAWToARGBRow_C;
void (*ARGBToUVJRow)(const uint8_t* src_argb0, int src_stride_argb, void (*ARGBToUVJRow)(const uint8_t* src_argb0, int src_stride_argb,
uint8_t* dst_u, uint8_t* dst_v, int width) = uint8_t* dst_u, uint8_t* dst_v, int width) =
ARGBToUVJRow_C; ARGBToUVJRow_C;
void (*ARGBToYJRow)(const uint8_t* src_argb, uint8_t* dst_y, int width) = void (*ARGBToYJRow)(const uint8_t* src_argb, uint8_t* dst_y, int width) =
ARGBToYJRow_C; ARGBToYJRow_C;
@ -3118,18 +3117,6 @@ int RAWToJ400(const uint8_t* src_raw,
return 0; return 0;
} }
static void SplitPixels(const uint8_t* src_u,
int src_pixel_stride_uv,
uint8_t* dst_u,
int width) {
int i;
for (i = 0; i < width; ++i) {
*dst_u = *src_u;
++dst_u;
src_u += src_pixel_stride_uv;
}
}
// Convert Android420 to I420. // Convert Android420 to I420.
LIBYUV_API LIBYUV_API
int Android420ToI420(const uint8_t* src_y, int Android420ToI420(const uint8_t* src_y,
@ -3147,58 +3134,10 @@ int Android420ToI420(const uint8_t* src_y,
int dst_stride_v, int dst_stride_v,
int width, int width,
int height) { int height) {
int y; return Android420ToI420Rotate(src_y, src_stride_y, src_u, src_stride_u, src_v,
const ptrdiff_t vu_off = src_v - src_u; src_stride_v, src_pixel_stride_uv, dst_y,
int halfwidth = (width + 1) >> 1; dst_stride_y, dst_u, dst_stride_u, dst_v,
int halfheight = (height + 1) >> 1; dst_stride_v, width, height, kRotate0);
if (!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) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
// Copy UV planes as is - I420
if (src_pixel_stride_uv == 1) {
CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
return 0;
// Split UV planes - NV21
}
if (src_pixel_stride_uv == 2 && vu_off == -1 &&
src_stride_u == src_stride_v) {
SplitUVPlane(src_v, src_stride_v, dst_v, dst_stride_v, dst_u, dst_stride_u,
halfwidth, halfheight);
return 0;
// Split UV planes - NV12
}
if (src_pixel_stride_uv == 2 && vu_off == 1 && src_stride_u == src_stride_v) {
SplitUVPlane(src_u, src_stride_u, dst_u, dst_stride_u, dst_v, dst_stride_v,
halfwidth, halfheight);
return 0;
}
for (y = 0; y < halfheight; ++y) {
SplitPixels(src_u, src_pixel_stride_uv, dst_u, halfwidth);
SplitPixels(src_v, src_pixel_stride_uv, dst_v, halfwidth);
src_u += src_stride_u;
src_v += src_stride_v;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -221,14 +221,14 @@ void RotatePlane180(const uint8_t* src,
} }
LIBYUV_API LIBYUV_API
void TransposeUV(const uint8_t* src, void SplitTransposeUV(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height) { int height) {
int i = height; int i = height;
#if defined(HAS_TRANSPOSEUVWX16_MSA) #if defined(HAS_TRANSPOSEUVWX16_MSA)
void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a, void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
@ -300,49 +300,49 @@ void TransposeUV(const uint8_t* src,
} }
LIBYUV_API LIBYUV_API
void RotateUV90(const uint8_t* src, void SplitRotateUV90(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height) { int height) {
src += src_stride * (height - 1); src += src_stride * (height - 1);
src_stride = -src_stride; src_stride = -src_stride;
TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width, SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
height); width, height);
} }
LIBYUV_API LIBYUV_API
void RotateUV270(const uint8_t* src, void SplitRotateUV270(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height) { int height) {
dst_a += dst_stride_a * (width - 1); dst_a += dst_stride_a * (width - 1);
dst_b += dst_stride_b * (width - 1); dst_b += dst_stride_b * (width - 1);
dst_stride_a = -dst_stride_a; dst_stride_a = -dst_stride_a;
dst_stride_b = -dst_stride_b; dst_stride_b = -dst_stride_b;
TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width, SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
height); width, height);
} }
// Rotate 180 is a horizontal and vertical flip. // Rotate 180 is a horizontal and vertical flip.
LIBYUV_API LIBYUV_API
void RotateUV180(const uint8_t* src, void SplitRotateUV180(const uint8_t* src,
int src_stride, int src_stride,
uint8_t* dst_a, uint8_t* dst_a,
int dst_stride_a, int dst_stride_a,
uint8_t* dst_b, uint8_t* dst_b,
int dst_stride_b, int dst_stride_b,
int width, int width,
int height) { int height) {
int i; int i;
void (*MirrorSplitUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v, void (*MirrorSplitUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v,
int width) = MirrorSplitUVRow_C; int width) = MirrorSplitUVRow_C;
@ -378,6 +378,52 @@ void RotateUV180(const uint8_t* src,
} }
} }
// Rotate UV and split into planar.
// width and height expected to be half size for NV12
LIBYUV_API
int SplitRotateUV(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,
enum RotationMode mode) {
if (!src_uv || width <= 0 || height == 0 || !dst_u || !dst_v) {
return -1;
}
// 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;
}
switch (mode) {
case kRotate0:
SplitUVPlane(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, width, height);
return 0;
case kRotate90:
SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, width, height);
return 0;
case kRotate270:
SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, width, height);
return 0;
case kRotate180:
SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, width, height);
return 0;
default:
break;
}
return -1;
}
LIBYUV_API LIBYUV_API
int RotatePlane(const uint8_t* src, int RotatePlane(const uint8_t* src,
int src_stride, int src_stride,
@ -584,18 +630,18 @@ int NV12ToI420Rotate(const uint8_t* src_y,
width, height); width, height);
case kRotate90: case kRotate90:
RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height); RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
RotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v, SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, halfwidth, halfheight); dst_stride_v, halfwidth, halfheight);
return 0; return 0;
case kRotate270: case kRotate270:
RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height); RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
RotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v, SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, halfwidth, halfheight); dst_stride_v, halfwidth, halfheight);
return 0; return 0;
case kRotate180: case kRotate180:
RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height); RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
RotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v, SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
dst_stride_v, halfwidth, halfheight); dst_stride_v, halfwidth, halfheight);
return 0; return 0;
default: default:
break; break;
@ -603,6 +649,97 @@ int NV12ToI420Rotate(const uint8_t* src_y,
return -1; return -1;
} }
static void SplitPixels(const uint8_t* src_u,
int src_pixel_stride_uv,
uint8_t* dst_u,
int width) {
int i;
for (i = 0; i < width; ++i) {
*dst_u = *src_u;
++dst_u;
src_u += src_pixel_stride_uv;
}
}
// Convert Android420 to I420 with Rotate
LIBYUV_API
int Android420ToI420Rotate(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,
int src_pixel_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height,
enum RotationMode rotation) {
int y;
const ptrdiff_t vu_off = src_v - src_u;
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if (!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) {
RotatePlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height,
rotation);
}
// Copy UV planes - I420
if (src_pixel_stride_uv == 1) {
RotatePlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight,
rotation);
RotatePlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight,
rotation);
return 0;
}
// Split UV planes - NV21
if (src_pixel_stride_uv == 2 && vu_off == -1 &&
src_stride_u == src_stride_v) {
SplitRotateUV(src_v, src_stride_v, dst_v, dst_stride_v, dst_u, dst_stride_u,
halfwidth, halfheight, rotation);
return 0;
}
// Split UV planes - NV12
if (src_pixel_stride_uv == 2 && vu_off == 1 && src_stride_u == src_stride_v) {
SplitRotateUV(src_u, src_stride_u, dst_u, dst_stride_u, dst_v, dst_stride_v,
halfwidth, halfheight, rotation);
return 0;
}
if (rotation == 0) {
for (y = 0; y < halfheight; ++y) {
SplitPixels(src_u, src_pixel_stride_uv, dst_u, halfwidth);
SplitPixels(src_v, src_pixel_stride_uv, dst_v, halfwidth);
src_u += src_stride_u;
src_v += src_stride_v;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
return 0;
}
// unsupported type and/or rotation.
return -1;
}
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
} // namespace libyuv } // namespace libyuv

View File

@ -1832,10 +1832,10 @@ void ARGBToUVJRow_NEON(const uint8_t* src_argb,
// TODO(fbarchard): Subsample match C code. // TODO(fbarchard): Subsample match C code.
void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24, void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24,
int src_stride_rgb24, int src_stride_rgb24,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width) { int width) {
asm volatile ( asm volatile (
"add %1, %0, %1 \n" // src_stride + src_rgb24 "add %1, %0, %1 \n" // src_stride + src_rgb24
"vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient "vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient
@ -1878,10 +1878,10 @@ void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24,
// TODO(fbarchard): Subsample match C code. // TODO(fbarchard): Subsample match C code.
void RAWToUVJRow_NEON(const uint8_t* src_raw, void RAWToUVJRow_NEON(const uint8_t* src_raw,
int src_stride_raw, int src_stride_raw,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width) { int width) {
asm volatile ( asm volatile (
"add %1, %0, %1 \n" // src_stride + src_raw "add %1, %0, %1 \n" // src_stride + src_raw
"vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient "vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient

View File

@ -2063,10 +2063,10 @@ void RGB24ToUVJRow_NEON(const uint8_t* src_rgb24,
} }
void RAWToUVJRow_NEON(const uint8_t* src_raw, void RAWToUVJRow_NEON(const uint8_t* src_raw,
int src_stride_raw, int src_stride_raw,
uint8_t* dst_u, uint8_t* dst_u,
uint8_t* dst_v, uint8_t* dst_v,
int width) { int width) {
const uint8_t* src_raw_1 = src_raw + src_stride_raw; const uint8_t* src_raw_1 = src_raw + src_stride_raw;
asm volatile ( asm volatile (
"movi v20.8h, #63, lsl #0 \n" // UB/VR coeff (0.500) / 2 "movi v20.8h, #63, lsl #0 \n" // UB/VR coeff (0.500) / 2

View File

@ -290,6 +290,8 @@ TESTPLANARTOP(I412, uint16_t, 2, 1, 1, I444, uint8_t, 1, 1, 1, 12)
TESTAPLANARTOP(Android420, I420, 1, 0, 0, 2, 2, I420, 2, 2) TESTAPLANARTOP(Android420, I420, 1, 0, 0, 2, 2, I420, 2, 2)
TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2) TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2)
TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2) TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2)
#undef TESTAPLANARTOP
#undef TESTAPLANARTOPI
// wrapper to keep API the same // wrapper to keep API the same
int I400ToNV21(const uint8_t* src_y, int I400ToNV21(const uint8_t* src_y,

View File

@ -16,6 +16,8 @@
namespace libyuv { namespace libyuv {
#define SUBSAMPLE(v, a) ((((v) + (a)-1)) / (a))
static void I420TestRotate(int src_width, static void I420TestRotate(int src_width,
int src_height, int src_height,
int dst_width, int dst_width,
@ -391,4 +393,119 @@ TEST_F(LibYUVRotateTest, NV12Rotate270_Invert) {
disable_cpu_flags_, benchmark_cpu_info_); disable_cpu_flags_, benchmark_cpu_info_);
} }
// Test Android 420 to I420 Rotate
#define TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, \
SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \
W1280, N, NEG, OFF, PN, OFF_U, OFF_V, ROT) \
TEST_F(LibYUVRotateTest, \
SRC_FMT_PLANAR##To##FMT_PLANAR##Rotate##ROT##To##PN##N) { \
const int kWidth = W1280; \
const int kHeight = benchmark_height_; \
const int kSizeUV = \
SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); \
align_buffer_page_end(src_y, kWidth* kHeight + OFF); \
align_buffer_page_end(src_uv, \
kSizeUV*((PIXEL_STRIDE == 3) ? 3 : 2) + OFF); \
align_buffer_page_end(dst_y_c, kWidth* kHeight); \
align_buffer_page_end(dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_v_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_y_opt, kWidth* kHeight); \
align_buffer_page_end(dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
uint8_t* src_u = src_uv + OFF_U; \
uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUV : OFF_V); \
int src_stride_uv = SUBSAMPLE(kWidth, SUBSAMP_X) * PIXEL_STRIDE; \
for (int i = 0; i < kHeight; ++i) \
for (int j = 0; j < kWidth; ++j) \
src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \
for (int i = 0; i < SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); ++i) { \
for (int j = 0; j < SUBSAMPLE(kWidth, SRC_SUBSAMP_X); ++j) { \
src_u[(i * src_stride_uv) + j * PIXEL_STRIDE + OFF] = \
(fastrand() & 0xff); \
src_v[(i * src_stride_uv) + j * PIXEL_STRIDE + OFF] = \
(fastrand() & 0xff); \
} \
} \
memset(dst_y_c, 1, kWidth* kHeight); \
memset(dst_u_c, 2, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_v_c, 3, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_y_opt, 101, kWidth* kHeight); \
memset(dst_u_opt, 102, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_v_opt, 103, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
MaskCpuFlags(disable_cpu_flags_); \
SRC_FMT_PLANAR##To##FMT_PLANAR##Rotate( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, dst_y_c, \
kWidth, dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X), dst_v_c, \
SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight, \
(libyuv::RotationMode)ROT); \
MaskCpuFlags(benchmark_cpu_info_); \
for (int i = 0; i < benchmark_iterations_; ++i) { \
SRC_FMT_PLANAR##To##FMT_PLANAR##Rotate( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, \
dst_y_opt, kWidth, dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \
dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight, \
(libyuv::RotationMode)ROT); \
} \
for (int i = 0; i < kHeight; ++i) { \
for (int j = 0; j < kWidth; ++j) { \
EXPECT_EQ(dst_y_c[i * kWidth + j], dst_y_opt[i * kWidth + j]); \
} \
} \
for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \
for (int j = 0; j < SUBSAMPLE(kWidth, SUBSAMP_X); ++j) { \
EXPECT_EQ(dst_u_c[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j], \
dst_u_opt[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j]); \
} \
} \
for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \
for (int j = 0; j < SUBSAMPLE(kWidth, SUBSAMP_X); ++j) { \
EXPECT_EQ(dst_v_c[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j], \
dst_v_opt[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j]); \
} \
} \
free_aligned_buffer_page_end(dst_y_c); \
free_aligned_buffer_page_end(dst_u_c); \
free_aligned_buffer_page_end(dst_v_c); \
free_aligned_buffer_page_end(dst_y_opt); \
free_aligned_buffer_page_end(dst_u_opt); \
free_aligned_buffer_page_end(dst_v_opt); \
free_aligned_buffer_page_end(src_y); \
free_aligned_buffer_page_end(src_uv); \
}
#define TESTAPLANARTOP(SRC_FMT_PLANAR, PN, PIXEL_STRIDE, OFF_U, OFF_V, \
SRC_SUBSAMP_X, SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, \
SUBSAMP_Y) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_ + 1, \
_Any, +, 0, PN, OFF_U, OFF_V, 0) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, \
_Unaligned, +, 2, PN, OFF_U, OFF_V, 0) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Invert, \
-, 0, PN, OFF_U, OFF_V, 0) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Opt, +, \
0, PN, OFF_U, OFF_V, 0) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Opt, +, \
0, PN, OFF_U, OFF_V, 180)
TESTAPLANARTOP(Android420, I420, 1, 0, 0, 2, 2, I420, 2, 2)
TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2)
TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2)
#undef TESTAPLANARTOP
#undef TESTAPLANARTOPI
} // namespace libyuv } // namespace libyuv