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