| /* |
| * 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/cpu_id.h" |
| #include "libyuv/convert.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* src, int src_stride, |
| uint8* dst, int dst_stride, |
| int width, int height) { |
| int i = height; |
| void (*TransposeWx8)(const uint8* src, int src_stride, |
| uint8* dst, int dst_stride, int width) = TransposeWx8_C; |
| #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_FAST_SSSE3) |
| if (TestCpuFlag(kCpuHasSSSE3)) { |
| TransposeWx8 = TransposeWx8_Fast_Any_SSSE3; |
| if (IS_ALIGNED(width, 16)) { |
| TransposeWx8 = TransposeWx8_Fast_SSSE3; |
| } |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEWX8_MIPS_DSPR2) |
| if (TestCpuFlag(kCpuHasMIPS_DSPR2)) { |
| if (IS_ALIGNED(width, 4) && |
| IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) { |
| TransposeWx8 = TransposeWx8_Fast_MIPS_DSPR2; |
| } else { |
| TransposeWx8 = TransposeWx8_MIPS_DSPR2; |
| } |
| } |
| #endif |
| |
| // 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; |
| } |
| |
| if (i > 0) { |
| TransposeWxH_C(src, src_stride, dst, dst_stride, width, i); |
| } |
| } |
| |
| LIBYUV_API |
| void RotatePlane90(const uint8* src, int src_stride, |
| uint8* 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* src, int src_stride, |
| uint8* 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* src, int src_stride, |
| uint8* 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* src_bot = src + src_stride * (height - 1); |
| uint8* dst_bot = dst + dst_stride * (height - 1); |
| int half_height = (height + 1) >> 1; |
| int y; |
| void (*MirrorRow)(const uint8* src, uint8* dst, int width) = MirrorRow_C; |
| void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; |
| #if defined(HAS_MIRRORROW_NEON) |
| if (TestCpuFlag(kCpuHasNEON)) { |
| MirrorRow = MirrorRow_Any_NEON; |
| if (IS_ALIGNED(width, 16)) { |
| MirrorRow = MirrorRow_NEON; |
| } |
| } |
| #endif |
| #if defined(HAS_MIRRORROW_SSE2) |
| if (TestCpuFlag(kCpuHasSSE2)) { |
| MirrorRow = MirrorRow_Any_SSE2; |
| if (IS_ALIGNED(width, 16)) { |
| MirrorRow = MirrorRow_SSE2; |
| } |
| } |
| #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 |
| // TODO(fbarchard): Mirror on mips handle unaligned memory. |
| #if defined(HAS_MIRRORROW_MIPS_DSPR2) |
| if (TestCpuFlag(kCpuHasMIPS_DSPR2) && |
| IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4) && |
| IS_ALIGNED(dst, 4) && IS_ALIGNED(dst_stride, 4)) { |
| MirrorRow = MirrorRow_MIPS_DSPR2; |
| } |
| #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_MIPS) |
| if (TestCpuFlag(kCpuHasMIPS)) { |
| CopyRow = CopyRow_MIPS; |
| } |
| #endif |
| |
| // Odd height will harmlessly mirror the middle row twice. |
| for (y = 0; y < half_height; ++y) { |
| MirrorRow(src, row, width); // Mirror first row into a buffer |
| src += src_stride; |
| MirrorRow(src_bot, dst, width); // Mirror last row into first row |
| dst += dst_stride; |
| CopyRow(row, dst_bot, width); // Copy first mirrored row into last |
| src_bot -= src_stride; |
| dst_bot -= dst_stride; |
| } |
| free_aligned_buffer_64(row); |
| } |
| |
| LIBYUV_API |
| void TransposeUV(const uint8* src, int src_stride, |
| uint8* dst_a, int dst_stride_a, |
| uint8* dst_b, int dst_stride_b, |
| int width, int height) { |
| int i = height; |
| void (*TransposeUVWx8)(const uint8* src, int src_stride, |
| uint8* dst_a, int dst_stride_a, |
| uint8* dst_b, int dst_stride_b, |
| int width) = TransposeUVWx8_C; |
| #if defined(HAS_TRANSPOSEUVWX8_NEON) |
| if (TestCpuFlag(kCpuHasNEON)) { |
| TransposeUVWx8 = TransposeUVWx8_NEON; |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEUVWX8_SSE2) |
| if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 8)) { |
| TransposeUVWx8 = TransposeUVWx8_SSE2; |
| } |
| #endif |
| #if defined(HAS_TRANSPOSEUVWx8_MIPS_DSPR2) |
| if (TestCpuFlag(kCpuHasMIPS_DSPR2) && IS_ALIGNED(width, 2) && |
| IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) { |
| TransposeUVWx8 = TransposeUVWx8_MIPS_DSPR2; |
| } |
| #endif |
| |
| // 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; |
| } |
| |
| 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* src, int src_stride, |
| uint8* dst_a, int dst_stride_a, |
| uint8* 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* src, int src_stride, |
| uint8* dst_a, int dst_stride_a, |
| uint8* 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* src, int src_stride, |
| uint8* dst_a, int dst_stride_a, |
| uint8* dst_b, int dst_stride_b, |
| int width, int height) { |
| int i; |
| void (*MirrorRowUV)(const uint8* src, uint8* dst_u, uint8* dst_v, int width) = |
| MirrorUVRow_C; |
| #if defined(HAS_MIRRORUVROW_NEON) |
| if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) { |
| MirrorRowUV = MirrorUVRow_NEON; |
| } |
| #endif |
| #if defined(HAS_MIRRORROW_UV_SSSE3) |
| if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) { |
| MirrorRowUV = MirrorUVRow_SSSE3; |
| } |
| #endif |
| #if defined(HAS_MIRRORUVROW_MIPS_DSPR2) |
| if (TestCpuFlag(kCpuHasMIPS_DSPR2) && |
| IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) { |
| MirrorRowUV = MirrorUVRow_MIPS_DSPR2; |
| } |
| #endif |
| |
| dst_a += dst_stride_a * (height - 1); |
| dst_b += dst_stride_b * (height - 1); |
| |
| for (i = 0; i < height; ++i) { |
| MirrorRowUV(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* src, int src_stride, |
| uint8* 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* src_y, int src_stride_y, |
| const uint8* src_u, int src_stride_u, |
| const uint8* src_v, int src_stride_v, |
| uint8* dst_y, int dst_stride_y, |
| uint8* dst_u, int dst_stride_u, |
| uint8* 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 NV12ToI420Rotate(const uint8* src_y, int src_stride_y, |
| const uint8* src_uv, int src_stride_uv, |
| uint8* dst_y, int dst_stride_y, |
| uint8* dst_u, int dst_stride_u, |
| uint8* 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 |