Line data Source code
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
25 0 : void TransposePlane(const uint8* src,
26 : int src_stride,
27 : uint8* dst,
28 : int dst_stride,
29 : int width,
30 : int height) {
31 0 : 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 0 : 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 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
46 0 : TransposeWx8 = TransposeWx8_Any_SSSE3;
47 0 : if (IS_ALIGNED(width, 8)) {
48 0 : TransposeWx8 = TransposeWx8_SSSE3;
49 : }
50 : }
51 : #endif
52 : #if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
53 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
54 0 : TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
55 0 : if (IS_ALIGNED(width, 16)) {
56 0 : 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 0 : while (i >= 8) {
90 0 : TransposeWx8(src, src_stride, dst, dst_stride, width);
91 0 : src += 8 * src_stride; // Go down 8 rows.
92 0 : dst += 8; // Move over 8 columns.
93 0 : i -= 8;
94 : }
95 : #endif
96 :
97 0 : if (i > 0) {
98 0 : TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
99 : }
100 0 : }
101 :
102 : LIBYUV_API
103 0 : 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 0 : src += src_stride * (height - 1);
113 0 : src_stride = -src_stride;
114 0 : TransposePlane(src, src_stride, dst, dst_stride, width, height);
115 0 : }
116 :
117 : LIBYUV_API
118 0 : 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 0 : dst += dst_stride * (width - 1);
128 0 : dst_stride = -dst_stride;
129 0 : TransposePlane(src, src_stride, dst, dst_stride, width, height);
130 0 : }
131 :
132 : LIBYUV_API
133 0 : 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 0 : align_buffer_64(row, width);
141 0 : const uint8* src_bot = src + src_stride * (height - 1);
142 0 : uint8* dst_bot = dst + dst_stride * (height - 1);
143 0 : int half_height = (height + 1) >> 1;
144 : int y;
145 0 : void (*MirrorRow)(const uint8* src, uint8* dst, int width) = MirrorRow_C;
146 0 : 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 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
157 0 : MirrorRow = MirrorRow_Any_SSSE3;
158 0 : if (IS_ALIGNED(width, 16)) {
159 0 : MirrorRow = MirrorRow_SSSE3;
160 : }
161 : }
162 : #endif
163 : #if defined(HAS_MIRRORROW_AVX2)
164 0 : if (TestCpuFlag(kCpuHasAVX2)) {
165 0 : MirrorRow = MirrorRow_Any_AVX2;
166 0 : if (IS_ALIGNED(width, 32)) {
167 0 : 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 0 : if (TestCpuFlag(kCpuHasSSE2)) {
189 0 : CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
190 : }
191 : #endif
192 : #if defined(HAS_COPYROW_AVX)
193 0 : if (TestCpuFlag(kCpuHasAVX)) {
194 0 : CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
195 : }
196 : #endif
197 : #if defined(HAS_COPYROW_ERMS)
198 0 : if (TestCpuFlag(kCpuHasERMS)) {
199 0 : 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 0 : for (y = 0; y < half_height; ++y) {
215 0 : MirrorRow(src, row, width); // Mirror first row into a buffer
216 0 : src += src_stride;
217 0 : MirrorRow(src_bot, dst, width); // Mirror last row into first row
218 0 : dst += dst_stride;
219 0 : CopyRow(row, dst_bot, width); // Copy first mirrored row into last
220 0 : src_bot -= src_stride;
221 0 : dst_bot -= dst_stride;
222 : }
223 0 : free_aligned_buffer_64(row);
224 0 : }
225 :
226 : LIBYUV_API
227 0 : 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 0 : 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 0 : 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 0 : if (TestCpuFlag(kCpuHasSSE2)) {
252 0 : TransposeUVWx8 = TransposeUVWx8_Any_SSE2;
253 0 : if (IS_ALIGNED(width, 8)) {
254 0 : 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 0 : while (i >= 8) {
286 : TransposeUVWx8(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
287 0 : width);
288 0 : src += 8 * src_stride; // Go down 8 rows.
289 0 : dst_a += 8; // Move over 8 columns.
290 0 : dst_b += 8; // Move over 8 columns.
291 0 : i -= 8;
292 : }
293 : #endif
294 :
295 0 : if (i > 0) {
296 : TransposeUVWxH_C(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
297 0 : width, i);
298 : }
299 0 : }
300 :
301 : LIBYUV_API
302 0 : 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 0 : src += src_stride * (height - 1);
311 0 : src_stride = -src_stride;
312 :
313 : TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width,
314 0 : height);
315 0 : }
316 :
317 : LIBYUV_API
318 0 : 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 0 : dst_a += dst_stride_a * (width - 1);
327 0 : dst_b += dst_stride_b * (width - 1);
328 0 : dst_stride_a = -dst_stride_a;
329 0 : dst_stride_b = -dst_stride_b;
330 :
331 : TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width,
332 0 : height);
333 0 : }
334 :
335 : // Rotate 180 is a horizontal and vertical flip.
336 : LIBYUV_API
337 0 : 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 0 : 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 0 : if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
355 0 : 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 0 : dst_a += dst_stride_a * (height - 1);
366 0 : dst_b += dst_stride_b * (height - 1);
367 :
368 0 : for (i = 0; i < height; ++i) {
369 0 : MirrorUVRow(src, dst_a, dst_b, width);
370 0 : src += src_stride;
371 0 : dst_a -= dst_stride_a;
372 0 : dst_b -= dst_stride_b;
373 : }
374 0 : }
375 :
376 : LIBYUV_API
377 0 : 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 0 : if (!src || width <= 0 || height == 0 || !dst) {
385 0 : return -1;
386 : }
387 :
388 : // Negative height means invert the image.
389 0 : if (height < 0) {
390 0 : height = -height;
391 0 : src = src + (height - 1) * src_stride;
392 0 : src_stride = -src_stride;
393 : }
394 :
395 0 : switch (mode) {
396 : case kRotate0:
397 : // copy frame
398 0 : CopyPlane(src, src_stride, dst, dst_stride, width, height);
399 0 : return 0;
400 : case kRotate90:
401 0 : RotatePlane90(src, src_stride, dst, dst_stride, width, height);
402 0 : return 0;
403 : case kRotate270:
404 0 : RotatePlane270(src, src_stride, dst, dst_stride, width, height);
405 0 : return 0;
406 : case kRotate180:
407 0 : RotatePlane180(src, src_stride, dst, dst_stride, width, height);
408 0 : return 0;
409 : default:
410 0 : break;
411 : }
412 0 : return -1;
413 : }
414 :
415 : LIBYUV_API
416 0 : 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 0 : int halfwidth = (width + 1) >> 1;
432 0 : int halfheight = (height + 1) >> 1;
433 0 : if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
434 0 : !dst_u || !dst_v) {
435 0 : return -1;
436 : }
437 :
438 : // Negative height means invert the image.
439 0 : if (height < 0) {
440 0 : height = -height;
441 0 : halfheight = (height + 1) >> 1;
442 0 : src_y = src_y + (height - 1) * src_stride_y;
443 0 : src_u = src_u + (halfheight - 1) * src_stride_u;
444 0 : src_v = src_v + (halfheight - 1) * src_stride_v;
445 0 : src_stride_y = -src_stride_y;
446 0 : src_stride_u = -src_stride_u;
447 0 : src_stride_v = -src_stride_v;
448 : }
449 :
450 0 : 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 0 : dst_v, dst_stride_v, width, height);
456 : case kRotate90:
457 0 : 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 0 : halfheight);
460 : RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
461 0 : halfheight);
462 0 : return 0;
463 : case kRotate270:
464 0 : 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 0 : halfheight);
467 : RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
468 0 : halfheight);
469 0 : return 0;
470 : case kRotate180:
471 0 : 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 0 : halfheight);
474 : RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
475 0 : halfheight);
476 0 : return 0;
477 : default:
478 0 : break;
479 : }
480 0 : return -1;
481 : }
482 :
483 : LIBYUV_API
484 0 : 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 0 : int halfwidth = (width + 1) >> 1;
498 0 : int halfheight = (height + 1) >> 1;
499 0 : if (!src_y || !src_uv || width <= 0 || height == 0 || !dst_y || !dst_u ||
500 : !dst_v) {
501 0 : return -1;
502 : }
503 :
504 : // Negative height means invert the image.
505 0 : if (height < 0) {
506 0 : height = -height;
507 0 : halfheight = (height + 1) >> 1;
508 0 : src_y = src_y + (height - 1) * src_stride_y;
509 0 : src_uv = src_uv + (halfheight - 1) * src_stride_uv;
510 0 : src_stride_y = -src_stride_y;
511 0 : src_stride_uv = -src_stride_uv;
512 : }
513 :
514 0 : 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 0 : width, height);
520 : case kRotate90:
521 0 : 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 0 : dst_stride_v, halfwidth, halfheight);
524 0 : return 0;
525 : case kRotate270:
526 0 : 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 0 : dst_stride_v, halfwidth, halfheight);
529 0 : return 0;
530 : case kRotate180:
531 0 : 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 0 : dst_stride_v, halfwidth, halfheight);
534 0 : return 0;
535 : default:
536 0 : break;
537 : }
538 0 : return -1;
539 : }
540 :
541 : #ifdef __cplusplus
542 : } // extern "C"
543 : } // namespace libyuv
544 : #endif
|