1 /* 2 * Copyright 2012 The LibYuv Project Authors. All rights reserved. 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/convert.h" 14 #include "libyuv/cpu_id.h" 15 #include "libyuv/planar_functions.h" 16 #include "libyuv/row.h" 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(LIBYUV_DISABLE_X86) && \ 26 (defined(_M_IX86) || \ 27 (defined(__x86_64__) && !defined(__native_client__)) || \ 28 defined(__i386__)) 29 #define HAS_SCALEARGBROWDOWNEVEN_SSE2 30 void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, 31 int src_stride, 32 int src_stepx, 33 uint8* dst_ptr, 34 int dst_width); 35 #endif 36 #if !defined(LIBYUV_DISABLE_NEON) && !defined(__native_client__) && \ 37 (defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__)) 38 #define HAS_SCALEARGBROWDOWNEVEN_NEON 39 void ScaleARGBRowDownEven_NEON(const uint8* src_ptr, 40 int src_stride, 41 int src_stepx, 42 uint8* dst_ptr, 43 int dst_width); 44 #endif 45 46 void ScaleARGBRowDownEven_C(const uint8* src_ptr, 47 int, 48 int src_stepx, 49 uint8* dst_ptr, 50 int dst_width); 51 52 static void ARGBTranspose(const uint8* src, 53 int src_stride, 54 uint8* dst, 55 int dst_stride, 56 int width, 57 int height) { 58 int i; 59 int src_pixel_step = src_stride >> 2; 60 void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride, 61 int src_step, uint8* dst_ptr, int dst_width) = 62 ScaleARGBRowDownEven_C; 63 #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2) 64 if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(height, 4)) { // Width of dest. 65 ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2; 66 } 67 #endif 68 #if defined(HAS_SCALEARGBROWDOWNEVEN_NEON) 69 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(height, 4)) { // Width of dest. 70 ScaleARGBRowDownEven = ScaleARGBRowDownEven_NEON; 71 } 72 #endif 73 74 for (i = 0; i < width; ++i) { // column of source to row of dest. 75 ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height); 76 dst += dst_stride; 77 src += 4; 78 } 79 } 80 81 void ARGBRotate90(const uint8* src, 82 int src_stride, 83 uint8* dst, 84 int dst_stride, 85 int width, 86 int height) { 87 // Rotate by 90 is a ARGBTranspose with the source read 88 // from bottom to top. So set the source pointer to the end 89 // of the buffer and flip the sign of the source stride. 90 src += src_stride * (height - 1); 91 src_stride = -src_stride; 92 ARGBTranspose(src, src_stride, dst, dst_stride, width, height); 93 } 94 95 void ARGBRotate270(const uint8* src, 96 int src_stride, 97 uint8* dst, 98 int dst_stride, 99 int width, 100 int height) { 101 // Rotate by 270 is a ARGBTranspose with the destination written 102 // from bottom to top. So set the destination pointer to the end 103 // of the buffer and flip the sign of the destination stride. 104 dst += dst_stride * (width - 1); 105 dst_stride = -dst_stride; 106 ARGBTranspose(src, src_stride, dst, dst_stride, width, height); 107 } 108 109 void ARGBRotate180(const uint8* src, 110 int src_stride, 111 uint8* dst, 112 int dst_stride, 113 int width, 114 int height) { 115 // Swap first and last row and mirror the content. Uses a temporary row. 116 align_buffer_64(row, width * 4); 117 const uint8* src_bot = src + src_stride * (height - 1); 118 uint8* dst_bot = dst + dst_stride * (height - 1); 119 int half_height = (height + 1) >> 1; 120 int y; 121 void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) = 122 ARGBMirrorRow_C; 123 void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; 124 #if defined(HAS_ARGBMIRRORROW_NEON) 125 if (TestCpuFlag(kCpuHasNEON)) { 126 ARGBMirrorRow = ARGBMirrorRow_Any_NEON; 127 if (IS_ALIGNED(width, 4)) { 128 ARGBMirrorRow = ARGBMirrorRow_NEON; 129 } 130 } 131 #endif 132 #if defined(HAS_ARGBMIRRORROW_SSE2) 133 if (TestCpuFlag(kCpuHasSSE2)) { 134 ARGBMirrorRow = ARGBMirrorRow_Any_SSE2; 135 if (IS_ALIGNED(width, 4)) { 136 ARGBMirrorRow = ARGBMirrorRow_SSE2; 137 } 138 } 139 #endif 140 #if defined(HAS_ARGBMIRRORROW_AVX2) 141 if (TestCpuFlag(kCpuHasAVX2)) { 142 ARGBMirrorRow = ARGBMirrorRow_Any_AVX2; 143 if (IS_ALIGNED(width, 8)) { 144 ARGBMirrorRow = ARGBMirrorRow_AVX2; 145 } 146 } 147 #endif 148 #if defined(HAS_ARGBMIRRORROW_MSA) 149 if (TestCpuFlag(kCpuHasMSA)) { 150 ARGBMirrorRow = ARGBMirrorRow_Any_MSA; 151 if (IS_ALIGNED(width, 16)) { 152 ARGBMirrorRow = ARGBMirrorRow_MSA; 153 } 154 } 155 #endif 156 #if defined(HAS_COPYROW_SSE2) 157 if (TestCpuFlag(kCpuHasSSE2)) { 158 CopyRow = IS_ALIGNED(width * 4, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2; 159 } 160 #endif 161 #if defined(HAS_COPYROW_AVX) 162 if (TestCpuFlag(kCpuHasAVX)) { 163 CopyRow = IS_ALIGNED(width * 4, 64) ? CopyRow_AVX : CopyRow_Any_AVX; 164 } 165 #endif 166 #if defined(HAS_COPYROW_ERMS) 167 if (TestCpuFlag(kCpuHasERMS)) { 168 CopyRow = CopyRow_ERMS; 169 } 170 #endif 171 #if defined(HAS_COPYROW_NEON) 172 if (TestCpuFlag(kCpuHasNEON)) { 173 CopyRow = IS_ALIGNED(width * 4, 32) ? CopyRow_NEON : CopyRow_Any_NEON; 174 } 175 #endif 176 #if defined(HAS_COPYROW_MIPS) 177 if (TestCpuFlag(kCpuHasMIPS)) { 178 CopyRow = CopyRow_MIPS; 179 } 180 #endif 181 182 // Odd height will harmlessly mirror the middle row twice. 183 for (y = 0; y < half_height; ++y) { 184 ARGBMirrorRow(src, row, width); // Mirror first row into a buffer 185 ARGBMirrorRow(src_bot, dst, width); // Mirror last row into first row 186 CopyRow(row, dst_bot, width * 4); // Copy first mirrored row into last 187 src += src_stride; 188 dst += dst_stride; 189 src_bot -= src_stride; 190 dst_bot -= dst_stride; 191 } 192 free_aligned_buffer_64(row); 193 } 194 195 LIBYUV_API 196 int ARGBRotate(const uint8* src_argb, 197 int src_stride_argb, 198 uint8* dst_argb, 199 int dst_stride_argb, 200 int width, 201 int height, 202 enum RotationMode mode) { 203 if (!src_argb || width <= 0 || height == 0 || !dst_argb) { 204 return -1; 205 } 206 207 // Negative height means invert the image. 208 if (height < 0) { 209 height = -height; 210 src_argb = src_argb + (height - 1) * src_stride_argb; 211 src_stride_argb = -src_stride_argb; 212 } 213 214 switch (mode) { 215 case kRotate0: 216 // copy frame 217 return ARGBCopy(src_argb, src_stride_argb, dst_argb, dst_stride_argb, 218 width, height); 219 case kRotate90: 220 ARGBRotate90(src_argb, src_stride_argb, dst_argb, dst_stride_argb, width, 221 height); 222 return 0; 223 case kRotate270: 224 ARGBRotate270(src_argb, src_stride_argb, dst_argb, dst_stride_argb, width, 225 height); 226 return 0; 227 case kRotate180: 228 ARGBRotate180(src_argb, src_stride_argb, dst_argb, dst_stride_argb, width, 229 height); 230 return 0; 231 default: 232 break; 233 } 234 return -1; 235 } 236 237 #ifdef __cplusplus 238 } // extern "C" 239 } // namespace libyuv 240 #endif 241