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