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