1 /*
2 * Copyright 2011 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/rotate_row.h"
17 #include "libyuv/row.h"
18
19 #ifdef __cplusplus
20 namespace libyuv {
21 extern "C" {
22 #endif
23
24 LIBYUV_API
TransposePlane(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)25 void TransposePlane(const uint8* src, int src_stride,
26 uint8* dst, int dst_stride,
27 int width, int height) {
28 int i = height;
29 void (*TransposeWx8)(const uint8* src, int src_stride,
30 uint8* dst, int dst_stride, int width) = TransposeWx8_C;
31 #if defined(HAS_TRANSPOSEWX8_NEON)
32 if (TestCpuFlag(kCpuHasNEON)) {
33 TransposeWx8 = TransposeWx8_NEON;
34 }
35 #endif
36 #if defined(HAS_TRANSPOSEWX8_SSSE3)
37 if (TestCpuFlag(kCpuHasSSSE3)) {
38 TransposeWx8 = TransposeWx8_Any_SSSE3;
39 if (IS_ALIGNED(width, 8)) {
40 TransposeWx8 = TransposeWx8_SSSE3;
41 }
42 }
43 #endif
44 #if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
45 if (TestCpuFlag(kCpuHasSSSE3)) {
46 TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
47 if (IS_ALIGNED(width, 16)) {
48 TransposeWx8 = TransposeWx8_Fast_SSSE3;
49 }
50 }
51 #endif
52 #if defined(HAS_TRANSPOSEWX8_DSPR2)
53 if (TestCpuFlag(kCpuHasDSPR2)) {
54 if (IS_ALIGNED(width, 4) &&
55 IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) {
56 TransposeWx8 = TransposeWx8_Fast_DSPR2;
57 } else {
58 TransposeWx8 = TransposeWx8_DSPR2;
59 }
60 }
61 #endif
62
63 // Work across the source in 8x8 tiles
64 while (i >= 8) {
65 TransposeWx8(src, src_stride, dst, dst_stride, width);
66 src += 8 * src_stride; // Go down 8 rows.
67 dst += 8; // Move over 8 columns.
68 i -= 8;
69 }
70
71 if (i > 0) {
72 TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
73 }
74 }
75
76 LIBYUV_API
RotatePlane90(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)77 void RotatePlane90(const uint8* src, int src_stride,
78 uint8* dst, int dst_stride,
79 int width, int height) {
80 // Rotate by 90 is a transpose with the source read
81 // from bottom to top. So set the source pointer to the end
82 // of the buffer and flip the sign of the source stride.
83 src += src_stride * (height - 1);
84 src_stride = -src_stride;
85 TransposePlane(src, src_stride, dst, dst_stride, width, height);
86 }
87
88 LIBYUV_API
RotatePlane270(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)89 void RotatePlane270(const uint8* src, int src_stride,
90 uint8* dst, int dst_stride,
91 int width, int height) {
92 // Rotate by 270 is a transpose with the destination written
93 // from bottom to top. So set the destination pointer to the end
94 // of the buffer and flip the sign of the destination stride.
95 dst += dst_stride * (width - 1);
96 dst_stride = -dst_stride;
97 TransposePlane(src, src_stride, dst, dst_stride, width, height);
98 }
99
100 LIBYUV_API
RotatePlane180(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)101 void RotatePlane180(const uint8* src, int src_stride,
102 uint8* dst, int dst_stride,
103 int width, int height) {
104 // Swap first and last row and mirror the content. Uses a temporary row.
105 align_buffer_64(row, width);
106 const uint8* src_bot = src + src_stride * (height - 1);
107 uint8* dst_bot = dst + dst_stride * (height - 1);
108 int half_height = (height + 1) >> 1;
109 int y;
110 void (*MirrorRow)(const uint8* src, uint8* dst, int width) = MirrorRow_C;
111 void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
112 #if defined(HAS_MIRRORROW_NEON)
113 if (TestCpuFlag(kCpuHasNEON)) {
114 MirrorRow = MirrorRow_Any_NEON;
115 if (IS_ALIGNED(width, 16)) {
116 MirrorRow = MirrorRow_NEON;
117 }
118 }
119 #endif
120 #if defined(HAS_MIRRORROW_SSSE3)
121 if (TestCpuFlag(kCpuHasSSSE3)) {
122 MirrorRow = MirrorRow_Any_SSSE3;
123 if (IS_ALIGNED(width, 16)) {
124 MirrorRow = MirrorRow_SSSE3;
125 }
126 }
127 #endif
128 #if defined(HAS_MIRRORROW_AVX2)
129 if (TestCpuFlag(kCpuHasAVX2)) {
130 MirrorRow = MirrorRow_Any_AVX2;
131 if (IS_ALIGNED(width, 32)) {
132 MirrorRow = MirrorRow_AVX2;
133 }
134 }
135 #endif
136 // TODO(fbarchard): Mirror on mips handle unaligned memory.
137 #if defined(HAS_MIRRORROW_DSPR2)
138 if (TestCpuFlag(kCpuHasDSPR2) &&
139 IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4) &&
140 IS_ALIGNED(dst, 4) && IS_ALIGNED(dst_stride, 4)) {
141 MirrorRow = MirrorRow_DSPR2;
142 }
143 #endif
144 #if defined(HAS_COPYROW_SSE2)
145 if (TestCpuFlag(kCpuHasSSE2)) {
146 CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
147 }
148 #endif
149 #if defined(HAS_COPYROW_AVX)
150 if (TestCpuFlag(kCpuHasAVX)) {
151 CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
152 }
153 #endif
154 #if defined(HAS_COPYROW_ERMS)
155 if (TestCpuFlag(kCpuHasERMS)) {
156 CopyRow = CopyRow_ERMS;
157 }
158 #endif
159 #if defined(HAS_COPYROW_NEON)
160 if (TestCpuFlag(kCpuHasNEON)) {
161 CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
162 }
163 #endif
164 #if defined(HAS_COPYROW_MIPS)
165 if (TestCpuFlag(kCpuHasMIPS)) {
166 CopyRow = CopyRow_MIPS;
167 }
168 #endif
169
170 // Odd height will harmlessly mirror the middle row twice.
171 for (y = 0; y < half_height; ++y) {
172 MirrorRow(src, row, width); // Mirror first row into a buffer
173 src += src_stride;
174 MirrorRow(src_bot, dst, width); // Mirror last row into first row
175 dst += dst_stride;
176 CopyRow(row, dst_bot, width); // Copy first mirrored row into last
177 src_bot -= src_stride;
178 dst_bot -= dst_stride;
179 }
180 free_aligned_buffer_64(row);
181 }
182
183 LIBYUV_API
TransposeUV(const uint8 * src,int src_stride,uint8 * dst_a,int dst_stride_a,uint8 * dst_b,int dst_stride_b,int width,int height)184 void TransposeUV(const uint8* src, int src_stride,
185 uint8* dst_a, int dst_stride_a,
186 uint8* dst_b, int dst_stride_b,
187 int width, int height) {
188 int i = height;
189 void (*TransposeUVWx8)(const uint8* src, int src_stride,
190 uint8* dst_a, int dst_stride_a,
191 uint8* dst_b, int dst_stride_b,
192 int width) = TransposeUVWx8_C;
193 #if defined(HAS_TRANSPOSEUVWX8_NEON)
194 if (TestCpuFlag(kCpuHasNEON)) {
195 TransposeUVWx8 = TransposeUVWx8_NEON;
196 }
197 #endif
198 #if defined(HAS_TRANSPOSEUVWX8_SSE2)
199 if (TestCpuFlag(kCpuHasSSE2)) {
200 TransposeUVWx8 = TransposeUVWx8_Any_SSE2;
201 if (IS_ALIGNED(width, 8)) {
202 TransposeUVWx8 = TransposeUVWx8_SSE2;
203 }
204 }
205 #endif
206 #if defined(HAS_TRANSPOSEUVWX8_DSPR2)
207 if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 2) &&
208 IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) {
209 TransposeUVWx8 = TransposeUVWx8_DSPR2;
210 }
211 #endif
212
213 // Work through the source in 8x8 tiles.
214 while (i >= 8) {
215 TransposeUVWx8(src, src_stride,
216 dst_a, dst_stride_a,
217 dst_b, dst_stride_b,
218 width);
219 src += 8 * src_stride; // Go down 8 rows.
220 dst_a += 8; // Move over 8 columns.
221 dst_b += 8; // Move over 8 columns.
222 i -= 8;
223 }
224
225 if (i > 0) {
226 TransposeUVWxH_C(src, src_stride,
227 dst_a, dst_stride_a,
228 dst_b, dst_stride_b,
229 width, i);
230 }
231 }
232
233 LIBYUV_API
RotateUV90(const uint8 * src,int src_stride,uint8 * dst_a,int dst_stride_a,uint8 * dst_b,int dst_stride_b,int width,int height)234 void RotateUV90(const uint8* src, int src_stride,
235 uint8* dst_a, int dst_stride_a,
236 uint8* dst_b, int dst_stride_b,
237 int width, int height) {
238 src += src_stride * (height - 1);
239 src_stride = -src_stride;
240
241 TransposeUV(src, src_stride,
242 dst_a, dst_stride_a,
243 dst_b, dst_stride_b,
244 width, height);
245 }
246
247 LIBYUV_API
RotateUV270(const uint8 * src,int src_stride,uint8 * dst_a,int dst_stride_a,uint8 * dst_b,int dst_stride_b,int width,int height)248 void RotateUV270(const uint8* src, int src_stride,
249 uint8* dst_a, int dst_stride_a,
250 uint8* dst_b, int dst_stride_b,
251 int width, int height) {
252 dst_a += dst_stride_a * (width - 1);
253 dst_b += dst_stride_b * (width - 1);
254 dst_stride_a = -dst_stride_a;
255 dst_stride_b = -dst_stride_b;
256
257 TransposeUV(src, src_stride,
258 dst_a, dst_stride_a,
259 dst_b, dst_stride_b,
260 width, height);
261 }
262
263 // Rotate 180 is a horizontal and vertical flip.
264 LIBYUV_API
RotateUV180(const uint8 * src,int src_stride,uint8 * dst_a,int dst_stride_a,uint8 * dst_b,int dst_stride_b,int width,int height)265 void RotateUV180(const uint8* src, int src_stride,
266 uint8* dst_a, int dst_stride_a,
267 uint8* dst_b, int dst_stride_b,
268 int width, int height) {
269 int i;
270 void (*MirrorUVRow)(const uint8* src, uint8* dst_u, uint8* dst_v, int width) =
271 MirrorUVRow_C;
272 #if defined(HAS_MIRRORUVROW_NEON)
273 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
274 MirrorUVRow = MirrorUVRow_NEON;
275 }
276 #endif
277 #if defined(HAS_MIRRORUVROW_SSSE3)
278 if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
279 MirrorUVRow = MirrorUVRow_SSSE3;
280 }
281 #endif
282 #if defined(HAS_MIRRORUVROW_DSPR2)
283 if (TestCpuFlag(kCpuHasDSPR2) &&
284 IS_ALIGNED(src, 4) && IS_ALIGNED(src_stride, 4)) {
285 MirrorUVRow = MirrorUVRow_DSPR2;
286 }
287 #endif
288
289 dst_a += dst_stride_a * (height - 1);
290 dst_b += dst_stride_b * (height - 1);
291
292 for (i = 0; i < height; ++i) {
293 MirrorUVRow(src, dst_a, dst_b, width);
294 src += src_stride;
295 dst_a -= dst_stride_a;
296 dst_b -= dst_stride_b;
297 }
298 }
299
300 LIBYUV_API
RotatePlane(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height,enum RotationMode mode)301 int RotatePlane(const uint8* src, int src_stride,
302 uint8* dst, int dst_stride,
303 int width, int height,
304 enum RotationMode mode) {
305 if (!src || width <= 0 || height == 0 || !dst) {
306 return -1;
307 }
308
309 // Negative height means invert the image.
310 if (height < 0) {
311 height = -height;
312 src = src + (height - 1) * src_stride;
313 src_stride = -src_stride;
314 }
315
316 switch (mode) {
317 case kRotate0:
318 // copy frame
319 CopyPlane(src, src_stride,
320 dst, dst_stride,
321 width, height);
322 return 0;
323 case kRotate90:
324 RotatePlane90(src, src_stride,
325 dst, dst_stride,
326 width, height);
327 return 0;
328 case kRotate270:
329 RotatePlane270(src, src_stride,
330 dst, dst_stride,
331 width, height);
332 return 0;
333 case kRotate180:
334 RotatePlane180(src, src_stride,
335 dst, dst_stride,
336 width, height);
337 return 0;
338 default:
339 break;
340 }
341 return -1;
342 }
343
344 LIBYUV_API
I420Rotate(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)345 int I420Rotate(const uint8* src_y, int src_stride_y,
346 const uint8* src_u, int src_stride_u,
347 const uint8* src_v, int src_stride_v,
348 uint8* dst_y, int dst_stride_y,
349 uint8* dst_u, int dst_stride_u,
350 uint8* dst_v, int dst_stride_v,
351 int width, int height,
352 enum RotationMode mode) {
353 int halfwidth = (width + 1) >> 1;
354 int halfheight = (height + 1) >> 1;
355 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 ||
356 !dst_y || !dst_u || !dst_v) {
357 return -1;
358 }
359
360 // Negative height means invert the image.
361 if (height < 0) {
362 height = -height;
363 halfheight = (height + 1) >> 1;
364 src_y = src_y + (height - 1) * src_stride_y;
365 src_u = src_u + (halfheight - 1) * src_stride_u;
366 src_v = src_v + (halfheight - 1) * src_stride_v;
367 src_stride_y = -src_stride_y;
368 src_stride_u = -src_stride_u;
369 src_stride_v = -src_stride_v;
370 }
371
372 switch (mode) {
373 case kRotate0:
374 // copy frame
375 return I420Copy(src_y, src_stride_y,
376 src_u, src_stride_u,
377 src_v, src_stride_v,
378 dst_y, dst_stride_y,
379 dst_u, dst_stride_u,
380 dst_v, dst_stride_v,
381 width, height);
382 case kRotate90:
383 RotatePlane90(src_y, src_stride_y,
384 dst_y, dst_stride_y,
385 width, height);
386 RotatePlane90(src_u, src_stride_u,
387 dst_u, dst_stride_u,
388 halfwidth, halfheight);
389 RotatePlane90(src_v, src_stride_v,
390 dst_v, dst_stride_v,
391 halfwidth, halfheight);
392 return 0;
393 case kRotate270:
394 RotatePlane270(src_y, src_stride_y,
395 dst_y, dst_stride_y,
396 width, height);
397 RotatePlane270(src_u, src_stride_u,
398 dst_u, dst_stride_u,
399 halfwidth, halfheight);
400 RotatePlane270(src_v, src_stride_v,
401 dst_v, dst_stride_v,
402 halfwidth, halfheight);
403 return 0;
404 case kRotate180:
405 RotatePlane180(src_y, src_stride_y,
406 dst_y, dst_stride_y,
407 width, height);
408 RotatePlane180(src_u, src_stride_u,
409 dst_u, dst_stride_u,
410 halfwidth, halfheight);
411 RotatePlane180(src_v, src_stride_v,
412 dst_v, dst_stride_v,
413 halfwidth, halfheight);
414 return 0;
415 default:
416 break;
417 }
418 return -1;
419 }
420
421 LIBYUV_API
NV12ToI420Rotate(const uint8 * src_y,int src_stride_y,const uint8 * src_uv,int src_stride_uv,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)422 int NV12ToI420Rotate(const uint8* src_y, int src_stride_y,
423 const uint8* src_uv, int src_stride_uv,
424 uint8* dst_y, int dst_stride_y,
425 uint8* dst_u, int dst_stride_u,
426 uint8* dst_v, int dst_stride_v,
427 int width, int height,
428 enum RotationMode mode) {
429 int halfwidth = (width + 1) >> 1;
430 int halfheight = (height + 1) >> 1;
431 if (!src_y || !src_uv || width <= 0 || height == 0 ||
432 !dst_y || !dst_u || !dst_v) {
433 return -1;
434 }
435
436 // Negative height means invert the image.
437 if (height < 0) {
438 height = -height;
439 halfheight = (height + 1) >> 1;
440 src_y = src_y + (height - 1) * src_stride_y;
441 src_uv = src_uv + (halfheight - 1) * src_stride_uv;
442 src_stride_y = -src_stride_y;
443 src_stride_uv = -src_stride_uv;
444 }
445
446 switch (mode) {
447 case kRotate0:
448 // copy frame
449 return NV12ToI420(src_y, src_stride_y,
450 src_uv, src_stride_uv,
451 dst_y, dst_stride_y,
452 dst_u, dst_stride_u,
453 dst_v, dst_stride_v,
454 width, height);
455 case kRotate90:
456 RotatePlane90(src_y, src_stride_y,
457 dst_y, dst_stride_y,
458 width, height);
459 RotateUV90(src_uv, src_stride_uv,
460 dst_u, dst_stride_u,
461 dst_v, dst_stride_v,
462 halfwidth, halfheight);
463 return 0;
464 case kRotate270:
465 RotatePlane270(src_y, src_stride_y,
466 dst_y, dst_stride_y,
467 width, height);
468 RotateUV270(src_uv, src_stride_uv,
469 dst_u, dst_stride_u,
470 dst_v, dst_stride_v,
471 halfwidth, halfheight);
472 return 0;
473 case kRotate180:
474 RotatePlane180(src_y, src_stride_y,
475 dst_y, dst_stride_y,
476 width, height);
477 RotateUV180(src_uv, src_stride_uv,
478 dst_u, dst_stride_u,
479 dst_v, dst_stride_v,
480 halfwidth, halfheight);
481 return 0;
482 default:
483 break;
484 }
485 return -1;
486 }
487
488 #ifdef __cplusplus
489 } // extern "C"
490 } // namespace libyuv
491 #endif
492