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