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