fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 1 | /* |
fbarchard@google.com | b0c9797 | 2012-08-08 19:04:24 +0000 | [diff] [blame] | 2 | * Copyright 2012 The LibYuv Project Authors. All rights reserved. |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "libyuv/rotate.h" |
| 12 | |
| 13 | #include "libyuv/cpu_id.h" |
| 14 | #include "libyuv/convert.h" |
| 15 | #include "libyuv/planar_functions.h" |
fbarchard@google.com | 142f6c4 | 2012-09-18 20:56:51 +0000 | [diff] [blame] | 16 | #include "libyuv/row.h" |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 17 | |
| 18 | #ifdef __cplusplus |
| 19 | namespace libyuv { |
| 20 | extern "C" { |
| 21 | #endif |
| 22 | |
| 23 | // ARGBScale has a function to copy pixels to a row, striding each source |
| 24 | // pixel by a constant. |
| 25 | #if !defined(YUV_DISABLE_ASM) && (defined(_M_IX86) || \ |
| 26 | defined(__x86_64__) || defined(__i386__)) |
| 27 | #define HAS_SCALEARGBROWDOWNEVEN_SSE2 |
| 28 | void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride, |
| 29 | int src_stepx, |
| 30 | uint8* dst_ptr, int dst_width); |
| 31 | #endif |
| 32 | void ScaleARGBRowDownEven_C(const uint8* src_ptr, int, |
| 33 | int src_stepx, |
| 34 | uint8* dst_ptr, int dst_width); |
| 35 | |
| 36 | static void ARGBTranspose(const uint8* src, int src_stride, |
| 37 | uint8* dst, int dst_stride, |
| 38 | int width, int height) { |
| 39 | void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride, |
| 40 | int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C; |
| 41 | #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2) |
| 42 | if (TestCpuFlag(kCpuHasSSE2) && |
| 43 | IS_ALIGNED(height, 4) && // width of dest. |
| 44 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 45 | ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2; |
| 46 | } |
| 47 | #endif |
| 48 | |
| 49 | int src_pixel_step = src_stride / 4; |
| 50 | for (int i = 0; i < width; ++i) { // column of source to row of dest. |
| 51 | ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height); |
| 52 | dst += dst_stride; |
| 53 | src += 4; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | void ARGBRotate90(const uint8* src, int src_stride, |
| 58 | uint8* dst, int dst_stride, |
| 59 | int width, int height) { |
| 60 | // Rotate by 90 is a ARGBTranspose with the source read |
| 61 | // from bottom to top. So set the source pointer to the end |
| 62 | // of the buffer and flip the sign of the source stride. |
| 63 | src += src_stride * (height - 1); |
| 64 | src_stride = -src_stride; |
| 65 | ARGBTranspose(src, src_stride, dst, dst_stride, width, height); |
| 66 | } |
| 67 | |
| 68 | void ARGBRotate270(const uint8* src, int src_stride, |
| 69 | uint8* dst, int dst_stride, |
| 70 | int width, int height) { |
| 71 | // Rotate by 270 is a ARGBTranspose with the destination written |
| 72 | // from bottom to top. So set the destination pointer to the end |
| 73 | // of the buffer and flip the sign of the destination stride. |
| 74 | dst += dst_stride * (width - 1); |
| 75 | dst_stride = -dst_stride; |
| 76 | ARGBTranspose(src, src_stride, dst, dst_stride, width, height); |
| 77 | } |
| 78 | |
| 79 | void ARGBRotate180(const uint8* src, int src_stride, |
| 80 | uint8* dst, int dst_stride, |
| 81 | int width, int height) { |
| 82 | void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) = |
| 83 | ARGBMirrorRow_C; |
| 84 | #if defined(HAS_ARGBMIRRORROW_SSSE3) |
| 85 | if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) && |
| 86 | IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && |
| 87 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 88 | ARGBMirrorRow = ARGBMirrorRow_SSSE3; |
| 89 | } |
| 90 | #endif |
| 91 | void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; |
| 92 | #if defined(HAS_COPYROW_NEON) |
| 93 | if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width * 4, 64)) { |
| 94 | CopyRow = CopyRow_NEON; |
| 95 | } |
| 96 | #endif |
| 97 | #if defined(HAS_COPYROW_X86) |
| 98 | if (TestCpuFlag(kCpuHasX86)) { |
| 99 | CopyRow = CopyRow_X86; |
| 100 | } |
| 101 | #endif |
| 102 | #if defined(HAS_COPYROW_SSE2) |
| 103 | if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width * 4, 32) && |
| 104 | IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && |
| 105 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 106 | CopyRow = CopyRow_SSE2; |
| 107 | } |
| 108 | #endif |
| 109 | if (width * 4 > kMaxStride) { |
| 110 | return; |
| 111 | } |
| 112 | // Swap first and last row and mirror the content. Uses a temporary row. |
| 113 | SIMD_ALIGNED(uint8 row[kMaxStride]); |
| 114 | const uint8* src_bot = src + src_stride * (height - 1); |
| 115 | uint8* dst_bot = dst + dst_stride * (height - 1); |
| 116 | int half_height = (height + 1) >> 1; |
| 117 | // Odd height will harmlessly mirror the middle row twice. |
| 118 | for (int y = 0; y < half_height; ++y) { |
| 119 | ARGBMirrorRow(src, row, width); // Mirror first row into a buffer |
| 120 | src += src_stride; |
| 121 | ARGBMirrorRow(src_bot, dst, width); // Mirror last row into first row |
| 122 | dst += dst_stride; |
| 123 | CopyRow(row, dst_bot, width * 4); // Copy first mirrored row into last |
| 124 | src_bot -= src_stride; |
| 125 | dst_bot -= dst_stride; |
| 126 | } |
| 127 | } |
| 128 | |
fbarchard@google.com | fc7314e | 2012-09-27 02:17:51 +0000 | [diff] [blame^] | 129 | LIBYUV_API |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 130 | int ARGBRotate(const uint8* src_argb, int src_stride_argb, |
| 131 | uint8* dst_argb, int dst_stride_argb, |
| 132 | int width, int height, |
| 133 | RotationMode mode) { |
| 134 | if (!src_argb || width <= 0 || height == 0 || !dst_argb) { |
| 135 | return -1; |
| 136 | } |
| 137 | |
| 138 | // Negative height means invert the image. |
| 139 | if (height < 0) { |
| 140 | height = -height; |
| 141 | src_argb = src_argb + (height - 1) * src_stride_argb; |
| 142 | src_stride_argb = -src_stride_argb; |
| 143 | } |
| 144 | |
| 145 | switch (mode) { |
| 146 | case kRotate0: |
| 147 | // copy frame |
| 148 | return ARGBCopy(src_argb, src_stride_argb, |
| 149 | dst_argb, dst_stride_argb, |
| 150 | width, height); |
| 151 | case kRotate90: |
| 152 | ARGBRotate90(src_argb, src_stride_argb, |
| 153 | dst_argb, dst_stride_argb, |
| 154 | width, height); |
| 155 | return 0; |
| 156 | case kRotate270: |
| 157 | ARGBRotate270(src_argb, src_stride_argb, |
| 158 | dst_argb, dst_stride_argb, |
| 159 | width, height); |
| 160 | return 0; |
| 161 | case kRotate180: |
| 162 | ARGBRotate180(src_argb, src_stride_argb, |
| 163 | dst_argb, dst_stride_argb, |
| 164 | width, height); |
| 165 | return 0; |
| 166 | default: |
| 167 | break; |
| 168 | } |
| 169 | return -1; |
| 170 | } |
| 171 | |
| 172 | #ifdef __cplusplus |
| 173 | } // extern "C" |
| 174 | } // namespace libyuv |
| 175 | #endif |