| /* |
| * 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/rotate.h" |
| |
| #include "libyuv/convert.h" |
| #include "libyuv/cpu_id.h" |
| #include "libyuv/planar_functions.h" |
| #include "libyuv/rotate_row.h" |
| #include "libyuv/row.h" |
| |
| #ifdef __cplusplus |
| namespace libyuv { |
| extern "C" { |
| #endif |
| |
| LIBYUV_API |
| void TransposePlane(const uint8_t* src, |
| int src_stride, |
| uint8_t* dst, |
| int dst_stride, |
| int width, |
| int height) { |
| int i = height; |
| #if defined(HAS_TRANSPOSEWX16_MSA) |
| void (*TransposeWx16)(const uint8_t* src, int src_stride, uint8_t* dst, |
| int dst_stride, int width) = TransposeWx16_C; |
| #else |
| void (*TransposeWx8)(const uint8_t* src, int src_stride, uint8_t* dst, |
| int dst_stride, int width) = TransposeWx8_C; |
| #endif |
| |
| #if defined(HAS_TRANSPOSEWX16_MSA) |
| if (TestCpuFlag(kCpuHasMSA)) { |
| TransposeWx16 = TransposeWx16_Any_MSA; |
| if (IS_ALIGNED(width, 16)) { |
| TransposeWx16 = TransposeWx16_MSA; |
| } |
| } |
| #else |
| #if defined(HAS_TRANSPOSEWX8_NEON) |
| if (TestCpuFlag(kCpuHasNEON)) { |
| TransposeWx8 = TransposeWx8_NEON; |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEWX8_SSSE3) |
| if (TestCpuFlag(kCpuHasSSSE3)) { |
| TransposeWx8 = TransposeWx8_Any_SSSE3; |
| if (IS_ALIGNED(width, 8)) { |
| TransposeWx8 = TransposeWx8_SSSE3; |
| } |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEWX8_MMI) |
| if (TestCpuFlag(kCpuHasMMI)) { |
| TransposeWx8 = TransposeWx8_MMI; |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEWX8_FAST_SSSE3) |
| if (TestCpuFlag(kCpuHasSSSE3)) { |
| TransposeWx8 = TransposeWx8_Fast_Any_SSSE3; |
| if (IS_ALIGNED(width, 16)) { |
| TransposeWx8 = TransposeWx8_Fast_SSSE3; |
| } |
| } |
| #endif |
| #endif /* defined(HAS_TRANSPOSEWX16_MSA) */ |
| |
| #if defined(HAS_TRANSPOSEWX16_MSA) |
| // Work across the source in 16x16 tiles |
| while (i >= 16) { |
| TransposeWx16(src, src_stride, dst, dst_stride, width); |
| src += 16 * src_stride; // Go down 16 rows. |
| dst += 16; // Move over 16 columns. |
| i -= 16; |
| } |
| #else |
| // Work across the source in 8x8 tiles |
| while (i >= 8) { |
| TransposeWx8(src, src_stride, dst, dst_stride, width); |
| src += 8 * src_stride; // Go down 8 rows. |
| dst += 8; // Move over 8 columns. |
| i -= 8; |
| } |
| #endif |
| |
| if (i > 0) { |
| TransposeWxH_C(src, src_stride, dst, dst_stride, width, i); |
| } |
| } |
| |
| LIBYUV_API |
| void RotatePlane90(const uint8_t* src, |
| int src_stride, |
| uint8_t* dst, |
| int dst_stride, |
| int width, |
| int height) { |
| // Rotate by 90 is a transpose with the source read |
| // from bottom to top. So set the source pointer to the end |
| // of the buffer and flip the sign of the source stride. |
| src += src_stride * (height - 1); |
| src_stride = -src_stride; |
| TransposePlane(src, src_stride, dst, dst_stride, width, height); |
| } |
| |
| LIBYUV_API |
| void RotatePlane270(const uint8_t* src, |
| int src_stride, |
| uint8_t* dst, |
| int dst_stride, |
| int width, |
| int height) { |
| // Rotate by 270 is a transpose with the destination written |
| // from bottom to top. So set the destination pointer to the end |
| // of the buffer and flip the sign of the destination stride. |
| dst += dst_stride * (width - 1); |
| dst_stride = -dst_stride; |
| TransposePlane(src, src_stride, dst, dst_stride, width, height); |
| } |
| |
| LIBYUV_API |
| void RotatePlane180(const uint8_t* src, |
| int src_stride, |
| uint8_t* dst, |
| int dst_stride, |
| int width, |
| int height) { |
| // Swap first and last row and mirror the content. Uses a temporary row. |
| align_buffer_64(row, width); |
| const uint8_t* src_bot = src + src_stride * (height - 1); |
| uint8_t* dst_bot = dst + dst_stride * (height - 1); |
| int half_height = (height + 1) >> 1; |
| int y; |
| void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C; |
| void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C; |
| #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_MMI) |
| if (TestCpuFlag(kCpuHasMMI)) { |
| MirrorRow = MirrorRow_Any_MMI; |
| if (IS_ALIGNED(width, 8)) { |
| MirrorRow = MirrorRow_MMI; |
| } |
| } |
| #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_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_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_MMI) |
| if (TestCpuFlag(kCpuHasMMI)) { |
| CopyRow = IS_ALIGNED(width, 8) ? CopyRow_MMI : CopyRow_Any_MMI; |
| } |
| #endif |
| |
| // Odd height will harmlessly mirror the middle row twice. |
| for (y = 0; y < half_height; ++y) { |
| CopyRow(src, row, width); // Copy first row into buffer |
| MirrorRow(src_bot, dst, width); // Mirror last row into first row |
| MirrorRow(row, dst_bot, width); // Mirror buffer into last row |
| src += src_stride; |
| dst += dst_stride; |
| src_bot -= src_stride; |
| dst_bot -= dst_stride; |
| } |
| free_aligned_buffer_64(row); |
| } |
| |
| LIBYUV_API |
| void TransposeUV(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) { |
| int i = height; |
| #if defined(HAS_TRANSPOSEUVWX16_MSA) |
| void (*TransposeUVWx16)(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) = TransposeUVWx16_C; |
| #else |
| void (*TransposeUVWx8)(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) = TransposeUVWx8_C; |
| #endif |
| |
| #if defined(HAS_TRANSPOSEUVWX16_MSA) |
| if (TestCpuFlag(kCpuHasMSA)) { |
| TransposeUVWx16 = TransposeUVWx16_Any_MSA; |
| if (IS_ALIGNED(width, 8)) { |
| TransposeUVWx16 = TransposeUVWx16_MSA; |
| } |
| } |
| #else |
| #if defined(HAS_TRANSPOSEUVWX8_NEON) |
| if (TestCpuFlag(kCpuHasNEON)) { |
| TransposeUVWx8 = TransposeUVWx8_NEON; |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEUVWX8_SSE2) |
| if (TestCpuFlag(kCpuHasSSE2)) { |
| TransposeUVWx8 = TransposeUVWx8_Any_SSE2; |
| if (IS_ALIGNED(width, 8)) { |
| TransposeUVWx8 = TransposeUVWx8_SSE2; |
| } |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEUVWX8_MMI) |
| if (TestCpuFlag(kCpuHasMMI)) { |
| TransposeUVWx8 = TransposeUVWx8_Any_MMI; |
| if (IS_ALIGNED(width, 4)) { |
| TransposeUVWx8 = TransposeUVWx8_MMI; |
| } |
| } |
| #endif |
| #endif /* defined(HAS_TRANSPOSEUVWX16_MSA) */ |
| |
| #if defined(HAS_TRANSPOSEUVWX16_MSA) |
| // Work through the source in 8x8 tiles. |
| while (i >= 16) { |
| TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, |
| width); |
| src += 16 * src_stride; // Go down 16 rows. |
| dst_a += 16; // Move over 8 columns. |
| dst_b += 16; // Move over 8 columns. |
| i -= 16; |
| } |
| #else |
| // Work through the source in 8x8 tiles. |
| while (i >= 8) { |
| TransposeUVWx8(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, |
| width); |
| src += 8 * src_stride; // Go down 8 rows. |
| dst_a += 8; // Move over 8 columns. |
| dst_b += 8; // Move over 8 columns. |
| i -= 8; |
| } |
| #endif |
| |
| if (i > 0) { |
| TransposeUVWxH_C(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, |
| width, i); |
| } |
| } |
| |
| LIBYUV_API |
| void RotateUV90(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) { |
| src += src_stride * (height - 1); |
| src_stride = -src_stride; |
| |
| TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width, |
| height); |
| } |
| |
| LIBYUV_API |
| void RotateUV270(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) { |
| dst_a += dst_stride_a * (width - 1); |
| dst_b += dst_stride_b * (width - 1); |
| dst_stride_a = -dst_stride_a; |
| dst_stride_b = -dst_stride_b; |
| |
| TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width, |
| height); |
| } |
| |
| // Rotate 180 is a horizontal and vertical flip. |
| LIBYUV_API |
| void RotateUV180(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) { |
| int i; |
| void (*MirrorSplitUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v, |
| int width) = MirrorSplitUVRow_C; |
| #if defined(HAS_MIRRORSPLITUVROW_NEON) |
| if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16)) { |
| MirrorSplitUVRow = MirrorSplitUVRow_NEON; |
| } |
| #endif |
| #if defined(HAS_MIRRORSPLITUVROW_SSSE3) |
| if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) { |
| MirrorSplitUVRow = MirrorSplitUVRow_SSSE3; |
| } |
| #endif |
| #if defined(HAS_MIRRORSPLITUVROW_MMI) |
| if (TestCpuFlag(kCpuHasMMI) && IS_ALIGNED(width, 8)) { |
| MirrorSplitUVRow = MirrorSplitUVRow_MMI; |
| } |
| #endif |
| #if defined(HAS_MIRRORSPLITUVROW_MSA) |
| if (TestCpuFlag(kCpuHasMSA) && IS_ALIGNED(width, 32)) { |
| MirrorSplitUVRow = MirrorSplitUVRow_MSA; |
| } |
| #endif |
| |
| dst_a += dst_stride_a * (height - 1); |
| dst_b += dst_stride_b * (height - 1); |
| |
| for (i = 0; i < height; ++i) { |
| MirrorSplitUVRow(src, dst_a, dst_b, width); |
| src += src_stride; |
| dst_a -= dst_stride_a; |
| dst_b -= dst_stride_b; |
| } |
| } |
| |
| LIBYUV_API |
| int RotatePlane(const uint8_t* src, |
| int src_stride, |
| uint8_t* dst, |
| int dst_stride, |
| int width, |
| int height, |
| enum RotationMode mode) { |
| if (!src || width <= 0 || height == 0 || !dst) { |
| return -1; |
| } |
| |
| // Negative height means invert the image. |
| if (height < 0) { |
| height = -height; |
| src = src + (height - 1) * src_stride; |
| src_stride = -src_stride; |
| } |
| |
| switch (mode) { |
| case kRotate0: |
| // copy frame |
| CopyPlane(src, src_stride, dst, dst_stride, width, height); |
| return 0; |
| case kRotate90: |
| RotatePlane90(src, src_stride, dst, dst_stride, width, height); |
| return 0; |
| case kRotate270: |
| RotatePlane270(src, src_stride, dst, dst_stride, width, height); |
| return 0; |
| case kRotate180: |
| RotatePlane180(src, src_stride, dst, dst_stride, width, height); |
| return 0; |
| default: |
| break; |
| } |
| return -1; |
| } |
| |
| LIBYUV_API |
| int I420Rotate(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, |
| enum RotationMode mode) { |
| int halfwidth = (width + 1) >> 1; |
| int halfheight = (height + 1) >> 1; |
| if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y || |
| !dst_u || !dst_v) { |
| 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; |
| } |
| |
| switch (mode) { |
| case kRotate0: |
| // copy frame |
| return I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v, |
| src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u, |
| dst_v, dst_stride_v, width, height); |
| case kRotate90: |
| RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, |
| halfheight); |
| RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, |
| halfheight); |
| return 0; |
| case kRotate270: |
| RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, |
| halfheight); |
| RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, |
| halfheight); |
| return 0; |
| case kRotate180: |
| RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, |
| halfheight); |
| RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, |
| halfheight); |
| return 0; |
| default: |
| break; |
| } |
| return -1; |
| } |
| |
| LIBYUV_API |
| int I444Rotate(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, |
| enum libyuv::RotationMode mode) { |
| if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y || |
| !dst_u || !dst_v) { |
| 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; |
| } |
| |
| switch (mode) { |
| case libyuv::kRotate0: |
| // copy frame |
| 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; |
| case libyuv::kRotate90: |
| RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, width, height); |
| RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, width, height); |
| return 0; |
| case libyuv::kRotate270: |
| RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, width, height); |
| RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, width, height); |
| return 0; |
| case libyuv::kRotate180: |
| RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height); |
| RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, width, height); |
| RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, width, height); |
| return 0; |
| default: |
| break; |
| } |
| return -1; |
| } |
| |
| LIBYUV_API |
| int NV12ToI420Rotate(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_u, |
| int dst_stride_u, |
| uint8_t* dst_v, |
| int dst_stride_v, |
| int width, |
| int height, |
| enum RotationMode mode) { |
| int halfwidth = (width + 1) >> 1; |
| int halfheight = (height + 1) >> 1; |
| if (!src_y || !src_uv || width <= 0 || height == 0 || !dst_y || !dst_u || |
| !dst_v) { |
| 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; |
| } |
| |
| switch (mode) { |
| case kRotate0: |
| // copy frame |
| return NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y, |
| dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, |
| width, height); |
| case kRotate90: |
| 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, |
| dst_stride_v, halfwidth, halfheight); |
| return 0; |
| case kRotate270: |
| 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, |
| dst_stride_v, halfwidth, halfheight); |
| return 0; |
| case kRotate180: |
| 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, |
| dst_stride_v, halfwidth, halfheight); |
| return 0; |
| default: |
| break; |
| } |
| return -1; |
| } |
| |
| #ifdef __cplusplus |
| } // extern "C" |
| } // namespace libyuv |
| #endif |