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
ARGBTranspose(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate90(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate270(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate180(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height,enum RotationMode mode)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