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 19namespace libyuv { 20extern "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 28void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride, 29 int src_stepx, 30 uint8* dst_ptr, int dst_width); 31#endif 32void ScaleARGBRowDownEven_C(const uint8* src_ptr, int, 33 int src_stepx, 34 uint8* dst_ptr, int dst_width); 35 36static 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 57void 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 68void 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 79void 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 129LIBYUV_API 130int 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 176