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 |
fbarchard@google.com | cb5262d | 2012-11-16 01:41:35 +0000 | [diff] [blame^] | 32 | #if !defined(YUV_DISABLE_ASM) && (defined(__ARM_NEON__) || defined(LIBYUV_NEON)) |
| 33 | #define HAS_SCALEARGBROWDOWNEVEN_NEON |
| 34 | void ScaleARGBRowDownEven_NEON(const uint8* src_ptr, int src_stride, |
| 35 | int src_stepx, |
| 36 | uint8* dst_ptr, int dst_width); |
| 37 | #endif |
| 38 | |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 39 | void ScaleARGBRowDownEven_C(const uint8* src_ptr, int, |
| 40 | int src_stepx, |
| 41 | uint8* dst_ptr, int dst_width); |
| 42 | |
| 43 | static void ARGBTranspose(const uint8* src, int src_stride, |
| 44 | uint8* dst, int dst_stride, |
| 45 | int width, int height) { |
fbarchard@google.com | cb5262d | 2012-11-16 01:41:35 +0000 | [diff] [blame^] | 46 | int src_pixel_step = src_stride >> 2; |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 47 | void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride, |
| 48 | int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C; |
| 49 | #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2) |
fbarchard@google.com | 3e46444 | 2012-11-14 02:03:49 +0000 | [diff] [blame] | 50 | if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(height, 4) && // Width of dest. |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 51 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 52 | ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2; |
| 53 | } |
fbarchard@google.com | cb5262d | 2012-11-16 01:41:35 +0000 | [diff] [blame^] | 54 | #elif defined(HAS_SCALEARGBROWDOWNEVEN_NEON) |
| 55 | if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(height, 4) && // Width of dest. |
| 56 | IS_ALIGNED(src, 4)) { |
| 57 | ScaleARGBRowDownEven = ScaleARGBRowDownEven_NEON; |
| 58 | } |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 59 | #endif |
| 60 | |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 61 | for (int i = 0; i < width; ++i) { // column of source to row of dest. |
| 62 | ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height); |
| 63 | dst += dst_stride; |
| 64 | src += 4; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | void ARGBRotate90(const uint8* src, int src_stride, |
| 69 | uint8* dst, int dst_stride, |
| 70 | int width, int height) { |
| 71 | // Rotate by 90 is a ARGBTranspose with the source read |
fbarchard@google.com | 64ce0ab | 2012-10-09 00:05:29 +0000 | [diff] [blame] | 72 | // from bottom to top. So set the source pointer to the end |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 73 | // of the buffer and flip the sign of the source stride. |
| 74 | src += src_stride * (height - 1); |
| 75 | src_stride = -src_stride; |
| 76 | ARGBTranspose(src, src_stride, dst, dst_stride, width, height); |
| 77 | } |
| 78 | |
| 79 | void ARGBRotate270(const uint8* src, int src_stride, |
| 80 | uint8* dst, int dst_stride, |
| 81 | int width, int height) { |
| 82 | // Rotate by 270 is a ARGBTranspose with the destination written |
fbarchard@google.com | 64ce0ab | 2012-10-09 00:05:29 +0000 | [diff] [blame] | 83 | // from bottom to top. So set the destination pointer to the end |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 84 | // of the buffer and flip the sign of the destination stride. |
| 85 | dst += dst_stride * (width - 1); |
| 86 | dst_stride = -dst_stride; |
| 87 | ARGBTranspose(src, src_stride, dst, dst_stride, width, height); |
| 88 | } |
| 89 | |
| 90 | void ARGBRotate180(const uint8* src, int src_stride, |
| 91 | uint8* dst, int dst_stride, |
| 92 | int width, int height) { |
| 93 | void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) = |
| 94 | ARGBMirrorRow_C; |
| 95 | #if defined(HAS_ARGBMIRRORROW_SSSE3) |
| 96 | if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) && |
| 97 | IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && |
| 98 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 99 | ARGBMirrorRow = ARGBMirrorRow_SSSE3; |
| 100 | } |
fbarchard@google.com | 3e46444 | 2012-11-14 02:03:49 +0000 | [diff] [blame] | 101 | #elif defined(HAS_ARGBMIRRORROW_NEON) |
| 102 | if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 4)) { |
| 103 | ARGBMirrorRow = ARGBMirrorRow_NEON; |
| 104 | } |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 105 | #endif |
| 106 | void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; |
| 107 | #if defined(HAS_COPYROW_NEON) |
fbarchard@google.com | 62a961b | 2012-10-22 17:24:50 +0000 | [diff] [blame] | 108 | if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width * 4, 32)) { |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 109 | CopyRow = CopyRow_NEON; |
| 110 | } |
| 111 | #endif |
| 112 | #if defined(HAS_COPYROW_X86) |
| 113 | if (TestCpuFlag(kCpuHasX86)) { |
| 114 | CopyRow = CopyRow_X86; |
| 115 | } |
| 116 | #endif |
| 117 | #if defined(HAS_COPYROW_SSE2) |
| 118 | if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width * 4, 32) && |
| 119 | IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && |
| 120 | IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { |
| 121 | CopyRow = CopyRow_SSE2; |
| 122 | } |
| 123 | #endif |
| 124 | if (width * 4 > kMaxStride) { |
| 125 | return; |
| 126 | } |
fbarchard@google.com | 64ce0ab | 2012-10-09 00:05:29 +0000 | [diff] [blame] | 127 | // Swap first and last row and mirror the content. Uses a temporary row. |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 128 | SIMD_ALIGNED(uint8 row[kMaxStride]); |
| 129 | const uint8* src_bot = src + src_stride * (height - 1); |
| 130 | uint8* dst_bot = dst + dst_stride * (height - 1); |
| 131 | int half_height = (height + 1) >> 1; |
| 132 | // Odd height will harmlessly mirror the middle row twice. |
| 133 | for (int y = 0; y < half_height; ++y) { |
| 134 | ARGBMirrorRow(src, row, width); // Mirror first row into a buffer |
| 135 | src += src_stride; |
| 136 | ARGBMirrorRow(src_bot, dst, width); // Mirror last row into first row |
| 137 | dst += dst_stride; |
| 138 | CopyRow(row, dst_bot, width * 4); // Copy first mirrored row into last |
| 139 | src_bot -= src_stride; |
| 140 | dst_bot -= dst_stride; |
| 141 | } |
| 142 | } |
| 143 | |
fbarchard@google.com | fc7314e | 2012-09-27 02:17:51 +0000 | [diff] [blame] | 144 | LIBYUV_API |
fbarchard@google.com | d51c342 | 2012-06-26 23:46:25 +0000 | [diff] [blame] | 145 | int ARGBRotate(const uint8* src_argb, int src_stride_argb, |
| 146 | uint8* dst_argb, int dst_stride_argb, |
| 147 | int width, int height, |
| 148 | RotationMode mode) { |
| 149 | if (!src_argb || width <= 0 || height == 0 || !dst_argb) { |
| 150 | return -1; |
| 151 | } |
| 152 | |
| 153 | // Negative height means invert the image. |
| 154 | if (height < 0) { |
| 155 | height = -height; |
| 156 | src_argb = src_argb + (height - 1) * src_stride_argb; |
| 157 | src_stride_argb = -src_stride_argb; |
| 158 | } |
| 159 | |
| 160 | switch (mode) { |
| 161 | case kRotate0: |
| 162 | // copy frame |
| 163 | return ARGBCopy(src_argb, src_stride_argb, |
| 164 | dst_argb, dst_stride_argb, |
| 165 | width, height); |
| 166 | case kRotate90: |
| 167 | ARGBRotate90(src_argb, src_stride_argb, |
| 168 | dst_argb, dst_stride_argb, |
| 169 | width, height); |
| 170 | return 0; |
| 171 | case kRotate270: |
| 172 | ARGBRotate270(src_argb, src_stride_argb, |
| 173 | dst_argb, dst_stride_argb, |
| 174 | width, height); |
| 175 | return 0; |
| 176 | case kRotate180: |
| 177 | ARGBRotate180(src_argb, src_stride_argb, |
| 178 | dst_argb, dst_stride_argb, |
| 179 | width, height); |
| 180 | return 0; |
| 181 | default: |
| 182 | break; |
| 183 | } |
| 184 | return -1; |
| 185 | } |
| 186 | |
| 187 | #ifdef __cplusplus |
| 188 | } // extern "C" |
| 189 | } // namespace libyuv |
| 190 | #endif |