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 <assert.h>
12
13 #include "libyuv/rotate.h"
14
15 #include "libyuv/convert.h"
16 #include "libyuv/cpu_id.h"
17 #include "libyuv/planar_functions.h"
18 #include "libyuv/rotate_row.h"
19 #include "libyuv/row.h"
20
21 #ifdef __cplusplus
22 namespace libyuv {
23 extern "C" {
24 #endif
25
26 LIBYUV_API
TransposePlane(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)27 void TransposePlane(const uint8_t* src,
28 int src_stride,
29 uint8_t* dst,
30 int dst_stride,
31 int width,
32 int height) {
33 int i = height;
34 #if defined(HAS_TRANSPOSEWX16_MSA) || defined(HAS_TRANSPOSEWX16_LSX)
35 void (*TransposeWx16)(const uint8_t* src, int src_stride, uint8_t* dst,
36 int dst_stride, int width) = TransposeWx16_C;
37 #else
38 void (*TransposeWx8)(const uint8_t* src, int src_stride, uint8_t* dst,
39 int dst_stride, int width) = TransposeWx8_C;
40 #endif
41
42 #if defined(HAS_TRANSPOSEWX8_NEON)
43 if (TestCpuFlag(kCpuHasNEON)) {
44 TransposeWx8 = TransposeWx8_Any_NEON;
45 if (IS_ALIGNED(width, 8)) {
46 TransposeWx8 = TransposeWx8_NEON;
47 }
48 }
49 #endif
50 #if defined(HAS_TRANSPOSEWX8_SSSE3)
51 if (TestCpuFlag(kCpuHasSSSE3)) {
52 TransposeWx8 = TransposeWx8_Any_SSSE3;
53 if (IS_ALIGNED(width, 8)) {
54 TransposeWx8 = TransposeWx8_SSSE3;
55 }
56 }
57 #endif
58 #if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
59 if (TestCpuFlag(kCpuHasSSSE3)) {
60 TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
61 if (IS_ALIGNED(width, 16)) {
62 TransposeWx8 = TransposeWx8_Fast_SSSE3;
63 }
64 }
65 #endif
66 #if defined(HAS_TRANSPOSEWX16_MSA)
67 if (TestCpuFlag(kCpuHasMSA)) {
68 TransposeWx16 = TransposeWx16_Any_MSA;
69 if (IS_ALIGNED(width, 16)) {
70 TransposeWx16 = TransposeWx16_MSA;
71 }
72 }
73 #endif
74 #if defined(HAS_TRANSPOSEWX16_LSX)
75 if (TestCpuFlag(kCpuHasLSX)) {
76 TransposeWx16 = TransposeWx16_Any_LSX;
77 if (IS_ALIGNED(width, 16)) {
78 TransposeWx16 = TransposeWx16_LSX;
79 }
80 }
81 #endif
82
83 #if defined(HAS_TRANSPOSEWX16_MSA) || defined(HAS_TRANSPOSEWX16_LSX)
84 // Work across the source in 16x16 tiles
85 while (i >= 16) {
86 TransposeWx16(src, src_stride, dst, dst_stride, width);
87 src += 16 * src_stride; // Go down 16 rows.
88 dst += 16; // Move over 16 columns.
89 i -= 16;
90 }
91 #else
92 // Work across the source in 8x8 tiles
93 while (i >= 8) {
94 TransposeWx8(src, src_stride, dst, dst_stride, width);
95 src += 8 * src_stride; // Go down 8 rows.
96 dst += 8; // Move over 8 columns.
97 i -= 8;
98 }
99 #endif
100
101 if (i > 0) {
102 TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
103 }
104 }
105
106 LIBYUV_API
RotatePlane90(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)107 void RotatePlane90(const uint8_t* src,
108 int src_stride,
109 uint8_t* dst,
110 int dst_stride,
111 int width,
112 int height) {
113 // Rotate by 90 is a transpose with the source read
114 // from bottom to top. So set the source pointer to the end
115 // of the buffer and flip the sign of the source stride.
116 src += src_stride * (height - 1);
117 src_stride = -src_stride;
118 TransposePlane(src, src_stride, dst, dst_stride, width, height);
119 }
120
121 LIBYUV_API
RotatePlane270(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)122 void RotatePlane270(const uint8_t* src,
123 int src_stride,
124 uint8_t* dst,
125 int dst_stride,
126 int width,
127 int height) {
128 // Rotate by 270 is a transpose with the destination written
129 // from bottom to top. So set the destination pointer to the end
130 // of the buffer and flip the sign of the destination stride.
131 dst += dst_stride * (width - 1);
132 dst_stride = -dst_stride;
133 TransposePlane(src, src_stride, dst, dst_stride, width, height);
134 }
135
136 LIBYUV_API
RotatePlane180(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)137 void RotatePlane180(const uint8_t* src,
138 int src_stride,
139 uint8_t* dst,
140 int dst_stride,
141 int width,
142 int height) {
143 // Swap top and bottom row and mirror the content. Uses a temporary row.
144 align_buffer_64(row, width);
145 assert(row);
146 if (!row)
147 return;
148 const uint8_t* src_bot = src + src_stride * (height - 1);
149 uint8_t* dst_bot = dst + dst_stride * (height - 1);
150 int half_height = (height + 1) >> 1;
151 int y;
152 void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C;
153 void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
154 #if defined(HAS_MIRRORROW_NEON)
155 if (TestCpuFlag(kCpuHasNEON)) {
156 MirrorRow = MirrorRow_Any_NEON;
157 if (IS_ALIGNED(width, 32)) {
158 MirrorRow = MirrorRow_NEON;
159 }
160 }
161 #endif
162 #if defined(HAS_MIRRORROW_SSSE3)
163 if (TestCpuFlag(kCpuHasSSSE3)) {
164 MirrorRow = MirrorRow_Any_SSSE3;
165 if (IS_ALIGNED(width, 16)) {
166 MirrorRow = MirrorRow_SSSE3;
167 }
168 }
169 #endif
170 #if defined(HAS_MIRRORROW_AVX2)
171 if (TestCpuFlag(kCpuHasAVX2)) {
172 MirrorRow = MirrorRow_Any_AVX2;
173 if (IS_ALIGNED(width, 32)) {
174 MirrorRow = MirrorRow_AVX2;
175 }
176 }
177 #endif
178 #if defined(HAS_MIRRORROW_MSA)
179 if (TestCpuFlag(kCpuHasMSA)) {
180 MirrorRow = MirrorRow_Any_MSA;
181 if (IS_ALIGNED(width, 64)) {
182 MirrorRow = MirrorRow_MSA;
183 }
184 }
185 #endif
186 #if defined(HAS_MIRRORROW_LSX)
187 if (TestCpuFlag(kCpuHasLSX)) {
188 MirrorRow = MirrorRow_Any_LSX;
189 if (IS_ALIGNED(width, 32)) {
190 MirrorRow = MirrorRow_LSX;
191 }
192 }
193 #endif
194 #if defined(HAS_MIRRORROW_LASX)
195 if (TestCpuFlag(kCpuHasLASX)) {
196 MirrorRow = MirrorRow_Any_LASX;
197 if (IS_ALIGNED(width, 64)) {
198 MirrorRow = MirrorRow_LASX;
199 }
200 }
201 #endif
202 #if defined(HAS_COPYROW_SSE2)
203 if (TestCpuFlag(kCpuHasSSE2)) {
204 CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
205 }
206 #endif
207 #if defined(HAS_COPYROW_AVX)
208 if (TestCpuFlag(kCpuHasAVX)) {
209 CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
210 }
211 #endif
212 #if defined(HAS_COPYROW_ERMS)
213 if (TestCpuFlag(kCpuHasERMS)) {
214 CopyRow = CopyRow_ERMS;
215 }
216 #endif
217 #if defined(HAS_COPYROW_NEON)
218 if (TestCpuFlag(kCpuHasNEON)) {
219 CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
220 }
221 #endif
222 #if defined(HAS_COPYROW_RVV)
223 if (TestCpuFlag(kCpuHasRVV)) {
224 CopyRow = CopyRow_RVV;
225 }
226 #endif
227
228 // Odd height will harmlessly mirror the middle row twice.
229 for (y = 0; y < half_height; ++y) {
230 CopyRow(src, row, width); // Copy top row into buffer
231 MirrorRow(src_bot, dst, width); // Mirror bottom row into top row
232 MirrorRow(row, dst_bot, width); // Mirror buffer into bottom row
233 src += src_stride;
234 dst += dst_stride;
235 src_bot -= src_stride;
236 dst_bot -= dst_stride;
237 }
238 free_aligned_buffer_64(row);
239 }
240
241 LIBYUV_API
SplitTransposeUV(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)242 void SplitTransposeUV(const uint8_t* src,
243 int src_stride,
244 uint8_t* dst_a,
245 int dst_stride_a,
246 uint8_t* dst_b,
247 int dst_stride_b,
248 int width,
249 int height) {
250 int i = height;
251 #if defined(HAS_TRANSPOSEUVWX16_MSA)
252 void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
253 int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
254 int width) = TransposeUVWx16_C;
255 #elif defined(HAS_TRANSPOSEUVWX16_LSX)
256 void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
257 int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
258 int width) = TransposeUVWx16_C;
259 #else
260 void (*TransposeUVWx8)(const uint8_t* src, int src_stride, uint8_t* dst_a,
261 int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
262 int width) = TransposeUVWx8_C;
263 #endif
264
265 #if defined(HAS_TRANSPOSEUVWX16_MSA)
266 if (TestCpuFlag(kCpuHasMSA)) {
267 TransposeUVWx16 = TransposeUVWx16_Any_MSA;
268 if (IS_ALIGNED(width, 8)) {
269 TransposeUVWx16 = TransposeUVWx16_MSA;
270 }
271 }
272 #elif defined(HAS_TRANSPOSEUVWX16_LSX)
273 if (TestCpuFlag(kCpuHasLSX)) {
274 TransposeUVWx16 = TransposeUVWx16_Any_LSX;
275 if (IS_ALIGNED(width, 8)) {
276 TransposeUVWx16 = TransposeUVWx16_LSX;
277 }
278 }
279 #else
280 #if defined(HAS_TRANSPOSEUVWX8_NEON)
281 if (TestCpuFlag(kCpuHasNEON)) {
282 TransposeUVWx8 = TransposeUVWx8_NEON;
283 }
284 #endif
285 #if defined(HAS_TRANSPOSEUVWX8_SSE2)
286 if (TestCpuFlag(kCpuHasSSE2)) {
287 TransposeUVWx8 = TransposeUVWx8_Any_SSE2;
288 if (IS_ALIGNED(width, 8)) {
289 TransposeUVWx8 = TransposeUVWx8_SSE2;
290 }
291 }
292 #endif
293 #endif /* defined(HAS_TRANSPOSEUVWX16_MSA) */
294
295 #if defined(HAS_TRANSPOSEUVWX16_MSA)
296 // Work through the source in 8x8 tiles.
297 while (i >= 16) {
298 TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
299 width);
300 src += 16 * src_stride; // Go down 16 rows.
301 dst_a += 16; // Move over 8 columns.
302 dst_b += 16; // Move over 8 columns.
303 i -= 16;
304 }
305 #elif defined(HAS_TRANSPOSEUVWX16_LSX)
306 // Work through the source in 8x8 tiles.
307 while (i >= 16) {
308 TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
309 width);
310 src += 16 * src_stride; // Go down 16 rows.
311 dst_a += 16; // Move over 8 columns.
312 dst_b += 16; // Move over 8 columns.
313 i -= 16;
314 }
315 #else
316 // Work through the source in 8x8 tiles.
317 while (i >= 8) {
318 TransposeUVWx8(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
319 width);
320 src += 8 * src_stride; // Go down 8 rows.
321 dst_a += 8; // Move over 8 columns.
322 dst_b += 8; // Move over 8 columns.
323 i -= 8;
324 }
325 #endif
326
327 if (i > 0) {
328 TransposeUVWxH_C(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
329 width, i);
330 }
331 }
332
333 LIBYUV_API
SplitRotateUV90(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)334 void SplitRotateUV90(const uint8_t* src,
335 int src_stride,
336 uint8_t* dst_a,
337 int dst_stride_a,
338 uint8_t* dst_b,
339 int dst_stride_b,
340 int width,
341 int height) {
342 src += src_stride * (height - 1);
343 src_stride = -src_stride;
344
345 SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
346 width, height);
347 }
348
349 LIBYUV_API
SplitRotateUV270(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)350 void SplitRotateUV270(const uint8_t* src,
351 int src_stride,
352 uint8_t* dst_a,
353 int dst_stride_a,
354 uint8_t* dst_b,
355 int dst_stride_b,
356 int width,
357 int height) {
358 dst_a += dst_stride_a * (width - 1);
359 dst_b += dst_stride_b * (width - 1);
360 dst_stride_a = -dst_stride_a;
361 dst_stride_b = -dst_stride_b;
362
363 SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
364 width, height);
365 }
366
367 // Rotate 180 is a horizontal and vertical flip.
368 LIBYUV_API
SplitRotateUV180(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)369 void SplitRotateUV180(const uint8_t* src,
370 int src_stride,
371 uint8_t* dst_a,
372 int dst_stride_a,
373 uint8_t* dst_b,
374 int dst_stride_b,
375 int width,
376 int height) {
377 int i;
378 void (*MirrorSplitUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v,
379 int width) = MirrorSplitUVRow_C;
380 #if defined(HAS_MIRRORSPLITUVROW_NEON)
381 if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16)) {
382 MirrorSplitUVRow = MirrorSplitUVRow_NEON;
383 }
384 #endif
385 #if defined(HAS_MIRRORSPLITUVROW_SSSE3)
386 if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
387 MirrorSplitUVRow = MirrorSplitUVRow_SSSE3;
388 }
389 #endif
390 #if defined(HAS_MIRRORSPLITUVROW_MSA)
391 if (TestCpuFlag(kCpuHasMSA) && IS_ALIGNED(width, 32)) {
392 MirrorSplitUVRow = MirrorSplitUVRow_MSA;
393 }
394 #endif
395 #if defined(HAS_MIRRORSPLITUVROW_LSX)
396 if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 32)) {
397 MirrorSplitUVRow = MirrorSplitUVRow_LSX;
398 }
399 #endif
400
401 dst_a += dst_stride_a * (height - 1);
402 dst_b += dst_stride_b * (height - 1);
403
404 for (i = 0; i < height; ++i) {
405 MirrorSplitUVRow(src, dst_a, dst_b, width);
406 src += src_stride;
407 dst_a -= dst_stride_a;
408 dst_b -= dst_stride_b;
409 }
410 }
411
412 // Rotate UV and split into planar.
413 // width and height expected to be half size for NV12
414 LIBYUV_API
SplitRotateUV(const uint8_t * src_uv,int src_stride_uv,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)415 int SplitRotateUV(const uint8_t* src_uv,
416 int src_stride_uv,
417 uint8_t* dst_u,
418 int dst_stride_u,
419 uint8_t* dst_v,
420 int dst_stride_v,
421 int width,
422 int height,
423 enum RotationMode mode) {
424 if (!src_uv || width <= 0 || height == 0 || !dst_u || !dst_v) {
425 return -1;
426 }
427
428 // Negative height means invert the image.
429 if (height < 0) {
430 height = -height;
431 src_uv = src_uv + (height - 1) * src_stride_uv;
432 src_stride_uv = -src_stride_uv;
433 }
434
435 switch (mode) {
436 case kRotate0:
437 SplitUVPlane(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
438 dst_stride_v, width, height);
439 return 0;
440 case kRotate90:
441 SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
442 dst_stride_v, width, height);
443 return 0;
444 case kRotate270:
445 SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
446 dst_stride_v, width, height);
447 return 0;
448 case kRotate180:
449 SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
450 dst_stride_v, width, height);
451 return 0;
452 default:
453 break;
454 }
455 return -1;
456 }
457
458 LIBYUV_API
RotatePlane(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height,enum RotationMode mode)459 int RotatePlane(const uint8_t* src,
460 int src_stride,
461 uint8_t* dst,
462 int dst_stride,
463 int width,
464 int height,
465 enum RotationMode mode) {
466 if (!src || width <= 0 || height == 0 || !dst) {
467 return -1;
468 }
469
470 // Negative height means invert the image.
471 if (height < 0) {
472 height = -height;
473 src = src + (height - 1) * src_stride;
474 src_stride = -src_stride;
475 }
476
477 switch (mode) {
478 case kRotate0:
479 // copy frame
480 CopyPlane(src, src_stride, dst, dst_stride, width, height);
481 return 0;
482 case kRotate90:
483 RotatePlane90(src, src_stride, dst, dst_stride, width, height);
484 return 0;
485 case kRotate270:
486 RotatePlane270(src, src_stride, dst, dst_stride, width, height);
487 return 0;
488 case kRotate180:
489 RotatePlane180(src, src_stride, dst, dst_stride, width, height);
490 return 0;
491 default:
492 break;
493 }
494 return -1;
495 }
496
TransposePlane_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)497 static void TransposePlane_16(const uint16_t* src,
498 int src_stride,
499 uint16_t* dst,
500 int dst_stride,
501 int width,
502 int height) {
503 int i = height;
504 // Work across the source in 8x8 tiles
505 while (i >= 8) {
506 TransposeWx8_16_C(src, src_stride, dst, dst_stride, width);
507 src += 8 * src_stride; // Go down 8 rows.
508 dst += 8; // Move over 8 columns.
509 i -= 8;
510 }
511
512 if (i > 0) {
513 TransposeWxH_16_C(src, src_stride, dst, dst_stride, width, i);
514 }
515 }
516
RotatePlane90_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)517 static void RotatePlane90_16(const uint16_t* src,
518 int src_stride,
519 uint16_t* dst,
520 int dst_stride,
521 int width,
522 int height) {
523 // Rotate by 90 is a transpose with the source read
524 // from bottom to top. So set the source pointer to the end
525 // of the buffer and flip the sign of the source stride.
526 src += src_stride * (height - 1);
527 src_stride = -src_stride;
528 TransposePlane_16(src, src_stride, dst, dst_stride, width, height);
529 }
530
RotatePlane270_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)531 static void RotatePlane270_16(const uint16_t* src,
532 int src_stride,
533 uint16_t* dst,
534 int dst_stride,
535 int width,
536 int height) {
537 // Rotate by 270 is a transpose with the destination written
538 // from bottom to top. So set the destination pointer to the end
539 // of the buffer and flip the sign of the destination stride.
540 dst += dst_stride * (width - 1);
541 dst_stride = -dst_stride;
542 TransposePlane_16(src, src_stride, dst, dst_stride, width, height);
543 }
544
RotatePlane180_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)545 static void RotatePlane180_16(const uint16_t* src,
546 int src_stride,
547 uint16_t* dst,
548 int dst_stride,
549 int width,
550 int height) {
551 const uint16_t* src_bot = src + src_stride * (height - 1);
552 uint16_t* dst_bot = dst + dst_stride * (height - 1);
553 int half_height = (height + 1) >> 1;
554 int y;
555
556 // Swap top and bottom row and mirror the content. Uses a temporary row.
557 align_buffer_64(row, width * 2);
558 uint16_t* row_tmp = (uint16_t*)row;
559 assert(row);
560 if (!row)
561 return;
562
563 // Odd height will harmlessly mirror the middle row twice.
564 for (y = 0; y < half_height; ++y) {
565 CopyRow_16_C(src, row_tmp, width); // Copy top row into buffer
566 MirrorRow_16_C(src_bot, dst, width); // Mirror bottom row into top row
567 MirrorRow_16_C(row_tmp, dst_bot, width); // Mirror buffer into bottom row
568 src += src_stride;
569 dst += dst_stride;
570 src_bot -= src_stride;
571 dst_bot -= dst_stride;
572 }
573 free_aligned_buffer_64(row);
574 }
575
576 LIBYUV_API
RotatePlane_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height,enum RotationMode mode)577 int RotatePlane_16(const uint16_t* src,
578 int src_stride,
579 uint16_t* dst,
580 int dst_stride,
581 int width,
582 int height,
583 enum RotationMode mode) {
584 if (!src || width <= 0 || height == 0 || !dst) {
585 return -1;
586 }
587
588 // Negative height means invert the image.
589 if (height < 0) {
590 height = -height;
591 src = src + (height - 1) * src_stride;
592 src_stride = -src_stride;
593 }
594
595 switch (mode) {
596 case kRotate0:
597 // copy frame
598 CopyPlane_16(src, src_stride, dst, dst_stride, width, height);
599 return 0;
600 case kRotate90:
601 RotatePlane90_16(src, src_stride, dst, dst_stride, width, height);
602 return 0;
603 case kRotate270:
604 RotatePlane270_16(src, src_stride, dst, dst_stride, width, height);
605 return 0;
606 case kRotate180:
607 RotatePlane180_16(src, src_stride, dst, dst_stride, width, height);
608 return 0;
609 default:
610 break;
611 }
612 return -1;
613 }
614
615 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)616 int I420Rotate(const uint8_t* src_y,
617 int src_stride_y,
618 const uint8_t* src_u,
619 int src_stride_u,
620 const uint8_t* src_v,
621 int src_stride_v,
622 uint8_t* dst_y,
623 int dst_stride_y,
624 uint8_t* dst_u,
625 int dst_stride_u,
626 uint8_t* dst_v,
627 int dst_stride_v,
628 int width,
629 int height,
630 enum RotationMode mode) {
631 int halfwidth = (width + 1) >> 1;
632 int halfheight = (height + 1) >> 1;
633 if ((!src_y && dst_y) || !src_u || !src_v || width <= 0 || height == 0 ||
634 !dst_y || !dst_u || !dst_v) {
635 return -1;
636 }
637
638 // Negative height means invert the image.
639 if (height < 0) {
640 height = -height;
641 halfheight = (height + 1) >> 1;
642 src_y = src_y + (height - 1) * src_stride_y;
643 src_u = src_u + (halfheight - 1) * src_stride_u;
644 src_v = src_v + (halfheight - 1) * src_stride_v;
645 src_stride_y = -src_stride_y;
646 src_stride_u = -src_stride_u;
647 src_stride_v = -src_stride_v;
648 }
649
650 switch (mode) {
651 case kRotate0:
652 // copy frame
653 return I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
654 src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
655 dst_v, dst_stride_v, width, height);
656 case kRotate90:
657 RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
658 RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
659 halfheight);
660 RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
661 halfheight);
662 return 0;
663 case kRotate270:
664 RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
665 RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
666 halfheight);
667 RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
668 halfheight);
669 return 0;
670 case kRotate180:
671 RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
672 RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
673 halfheight);
674 RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
675 halfheight);
676 return 0;
677 default:
678 break;
679 }
680 return -1;
681 }
682
683 // I422 has half width x full height UV planes, so rotate by 90 and 270
684 // require scaling to maintain 422 subsampling.
685 LIBYUV_API
I422Rotate(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)686 int I422Rotate(const uint8_t* src_y,
687 int src_stride_y,
688 const uint8_t* src_u,
689 int src_stride_u,
690 const uint8_t* src_v,
691 int src_stride_v,
692 uint8_t* dst_y,
693 int dst_stride_y,
694 uint8_t* dst_u,
695 int dst_stride_u,
696 uint8_t* dst_v,
697 int dst_stride_v,
698 int width,
699 int height,
700 enum RotationMode mode) {
701 int halfwidth = (width + 1) >> 1;
702 int halfheight = (height + 1) >> 1;
703 int r;
704 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
705 !dst_u || !dst_v) {
706 return -1;
707 }
708 // Negative height means invert the image.
709 if (height < 0) {
710 height = -height;
711 src_y = src_y + (height - 1) * src_stride_y;
712 src_u = src_u + (height - 1) * src_stride_u;
713 src_v = src_v + (height - 1) * src_stride_v;
714 src_stride_y = -src_stride_y;
715 src_stride_u = -src_stride_u;
716 src_stride_v = -src_stride_v;
717 }
718
719 switch (mode) {
720 case kRotate0:
721 // Copy frame
722 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
723 CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
724 CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
725 return 0;
726
727 // Note on temporary Y plane for UV.
728 // Rotation of UV first fits within the Y destination plane rows.
729 // Y plane is width x height
730 // Y plane rotated is height x width
731 // UV plane is (width / 2) x height
732 // UV plane rotated is height x (width / 2)
733 // UV plane rotated+scaled is (height / 2) x width.
734 // UV plane rotated is a temporary that fits within the Y plane rotated.
735
736 case kRotate90:
737 RotatePlane90(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
738 height);
739 r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_u,
740 dst_stride_u, halfheight, width, kFilterBilinear);
741 if (r != 0) {
742 return r;
743 }
744 RotatePlane90(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
745 height);
746 r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_v,
747 dst_stride_v, halfheight, width, kFilterLinear);
748 if (r != 0) {
749 return r;
750 }
751 RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
752 return 0;
753 case kRotate270:
754 RotatePlane270(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
755 height);
756 r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_u,
757 dst_stride_u, halfheight, width, kFilterBilinear);
758 if (r != 0) {
759 return r;
760 }
761 RotatePlane270(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
762 height);
763 r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_v,
764 dst_stride_v, halfheight, width, kFilterLinear);
765 if (r != 0) {
766 return r;
767 }
768 RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
769 return 0;
770 case kRotate180:
771 RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
772 RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
773 height);
774 RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
775 height);
776 return 0;
777 default:
778 break;
779 }
780 return -1;
781 }
782
783 LIBYUV_API
I444Rotate(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)784 int I444Rotate(const uint8_t* src_y,
785 int src_stride_y,
786 const uint8_t* src_u,
787 int src_stride_u,
788 const uint8_t* src_v,
789 int src_stride_v,
790 uint8_t* dst_y,
791 int dst_stride_y,
792 uint8_t* dst_u,
793 int dst_stride_u,
794 uint8_t* dst_v,
795 int dst_stride_v,
796 int width,
797 int height,
798 enum RotationMode mode) {
799 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
800 !dst_u || !dst_v) {
801 return -1;
802 }
803
804 // Negative height means invert the image.
805 if (height < 0) {
806 height = -height;
807 src_y = src_y + (height - 1) * src_stride_y;
808 src_u = src_u + (height - 1) * src_stride_u;
809 src_v = src_v + (height - 1) * src_stride_v;
810 src_stride_y = -src_stride_y;
811 src_stride_u = -src_stride_u;
812 src_stride_v = -src_stride_v;
813 }
814
815 switch (mode) {
816 case kRotate0:
817 // copy frame
818 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
819 CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
820 CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
821 return 0;
822 case kRotate90:
823 RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
824 RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
825 RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
826 return 0;
827 case kRotate270:
828 RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
829 RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
830 RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
831 return 0;
832 case kRotate180:
833 RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
834 RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
835 RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
836 return 0;
837 default:
838 break;
839 }
840 return -1;
841 }
842
843 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)844 int NV12ToI420Rotate(const uint8_t* src_y,
845 int src_stride_y,
846 const uint8_t* src_uv,
847 int src_stride_uv,
848 uint8_t* dst_y,
849 int dst_stride_y,
850 uint8_t* dst_u,
851 int dst_stride_u,
852 uint8_t* dst_v,
853 int dst_stride_v,
854 int width,
855 int height,
856 enum RotationMode mode) {
857 int halfwidth = (width + 1) >> 1;
858 int halfheight = (height + 1) >> 1;
859 if (!src_y || !src_uv || width <= 0 || height == 0 || !dst_y || !dst_u ||
860 !dst_v) {
861 return -1;
862 }
863
864 // Negative height means invert the image.
865 if (height < 0) {
866 height = -height;
867 halfheight = (height + 1) >> 1;
868 src_y = src_y + (height - 1) * src_stride_y;
869 src_uv = src_uv + (halfheight - 1) * src_stride_uv;
870 src_stride_y = -src_stride_y;
871 src_stride_uv = -src_stride_uv;
872 }
873
874 switch (mode) {
875 case kRotate0:
876 // copy frame
877 return NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y,
878 dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
879 width, height);
880 case kRotate90:
881 RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
882 SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
883 dst_stride_v, halfwidth, halfheight);
884 return 0;
885 case kRotate270:
886 RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
887 SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
888 dst_stride_v, halfwidth, halfheight);
889 return 0;
890 case kRotate180:
891 RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
892 SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
893 dst_stride_v, halfwidth, halfheight);
894 return 0;
895 default:
896 break;
897 }
898 return -1;
899 }
900
SplitPixels(const uint8_t * src_u,int src_pixel_stride_uv,uint8_t * dst_u,int width)901 static void SplitPixels(const uint8_t* src_u,
902 int src_pixel_stride_uv,
903 uint8_t* dst_u,
904 int width) {
905 int i;
906 for (i = 0; i < width; ++i) {
907 *dst_u = *src_u;
908 ++dst_u;
909 src_u += src_pixel_stride_uv;
910 }
911 }
912
913 // Convert Android420 to I420 with Rotate
914 LIBYUV_API
Android420ToI420Rotate(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,int src_pixel_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 rotation)915 int Android420ToI420Rotate(const uint8_t* src_y,
916 int src_stride_y,
917 const uint8_t* src_u,
918 int src_stride_u,
919 const uint8_t* src_v,
920 int src_stride_v,
921 int src_pixel_stride_uv,
922 uint8_t* dst_y,
923 int dst_stride_y,
924 uint8_t* dst_u,
925 int dst_stride_u,
926 uint8_t* dst_v,
927 int dst_stride_v,
928 int width,
929 int height,
930 enum RotationMode rotation) {
931 int y;
932 const ptrdiff_t vu_off = src_v - src_u;
933 int halfwidth = (width + 1) >> 1;
934 int halfheight = (height + 1) >> 1;
935 if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
936 height == 0) {
937 return -1;
938 }
939 // Negative height means invert the image.
940 if (height < 0) {
941 height = -height;
942 halfheight = (height + 1) >> 1;
943 src_y = src_y + (height - 1) * src_stride_y;
944 src_u = src_u + (halfheight - 1) * src_stride_u;
945 src_v = src_v + (halfheight - 1) * src_stride_v;
946 src_stride_y = -src_stride_y;
947 src_stride_u = -src_stride_u;
948 src_stride_v = -src_stride_v;
949 }
950
951 if (dst_y) {
952 RotatePlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height,
953 rotation);
954 }
955
956 // Copy UV planes - I420
957 if (src_pixel_stride_uv == 1) {
958 RotatePlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight,
959 rotation);
960 RotatePlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight,
961 rotation);
962 return 0;
963 }
964 // Split UV planes - NV21
965 if (src_pixel_stride_uv == 2 && vu_off == -1 &&
966 src_stride_u == src_stride_v) {
967 SplitRotateUV(src_v, src_stride_v, dst_v, dst_stride_v, dst_u, dst_stride_u,
968 halfwidth, halfheight, rotation);
969 return 0;
970 }
971 // Split UV planes - NV12
972 if (src_pixel_stride_uv == 2 && vu_off == 1 && src_stride_u == src_stride_v) {
973 SplitRotateUV(src_u, src_stride_u, dst_u, dst_stride_u, dst_v, dst_stride_v,
974 halfwidth, halfheight, rotation);
975 return 0;
976 }
977
978 if (rotation == 0) {
979 for (y = 0; y < halfheight; ++y) {
980 SplitPixels(src_u, src_pixel_stride_uv, dst_u, halfwidth);
981 SplitPixels(src_v, src_pixel_stride_uv, dst_v, halfwidth);
982 src_u += src_stride_u;
983 src_v += src_stride_v;
984 dst_u += dst_stride_u;
985 dst_v += dst_stride_v;
986 }
987 return 0;
988 }
989 // unsupported type and/or rotation.
990 return -1;
991 }
992
993 LIBYUV_API
I010Rotate(const uint16_t * src_y,int src_stride_y,const uint16_t * src_u,int src_stride_u,const uint16_t * src_v,int src_stride_v,uint16_t * dst_y,int dst_stride_y,uint16_t * dst_u,int dst_stride_u,uint16_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)994 int I010Rotate(const uint16_t* src_y,
995 int src_stride_y,
996 const uint16_t* src_u,
997 int src_stride_u,
998 const uint16_t* src_v,
999 int src_stride_v,
1000 uint16_t* dst_y,
1001 int dst_stride_y,
1002 uint16_t* dst_u,
1003 int dst_stride_u,
1004 uint16_t* dst_v,
1005 int dst_stride_v,
1006 int width,
1007 int height,
1008 enum RotationMode mode) {
1009 int halfwidth = (width + 1) >> 1;
1010 int halfheight = (height + 1) >> 1;
1011 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1012 !dst_u || !dst_v || dst_stride_y < 0) {
1013 return -1;
1014 }
1015 // Negative height means invert the image.
1016 if (height < 0) {
1017 height = -height;
1018 src_y = src_y + (height - 1) * src_stride_y;
1019 src_u = src_u + (height - 1) * src_stride_u;
1020 src_v = src_v + (height - 1) * src_stride_v;
1021 src_stride_y = -src_stride_y;
1022 src_stride_u = -src_stride_u;
1023 src_stride_v = -src_stride_v;
1024 }
1025
1026 switch (mode) {
1027 case kRotate0:
1028 // copy frame
1029 return I010Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
1030 src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
1031 dst_v, dst_stride_v, width, height);
1032 case kRotate90:
1033 RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1034 RotatePlane90_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1035 halfheight);
1036 RotatePlane90_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1037 halfheight);
1038 return 0;
1039 case kRotate270:
1040 RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1041 height);
1042 RotatePlane270_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1043 halfheight);
1044 RotatePlane270_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1045 halfheight);
1046 return 0;
1047 case kRotate180:
1048 RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1049 height);
1050 RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1051 halfheight);
1052 RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1053 halfheight);
1054 return 0;
1055 default:
1056 break;
1057 }
1058 return -1;
1059 }
1060
1061 // I210 has half width x full height UV planes, so rotate by 90 and 270
1062 // require scaling to maintain 422 subsampling.
1063 LIBYUV_API
I210Rotate(const uint16_t * src_y,int src_stride_y,const uint16_t * src_u,int src_stride_u,const uint16_t * src_v,int src_stride_v,uint16_t * dst_y,int dst_stride_y,uint16_t * dst_u,int dst_stride_u,uint16_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)1064 int I210Rotate(const uint16_t* src_y,
1065 int src_stride_y,
1066 const uint16_t* src_u,
1067 int src_stride_u,
1068 const uint16_t* src_v,
1069 int src_stride_v,
1070 uint16_t* dst_y,
1071 int dst_stride_y,
1072 uint16_t* dst_u,
1073 int dst_stride_u,
1074 uint16_t* dst_v,
1075 int dst_stride_v,
1076 int width,
1077 int height,
1078 enum RotationMode mode) {
1079 int halfwidth = (width + 1) >> 1;
1080 int halfheight = (height + 1) >> 1;
1081 int r;
1082 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1083 !dst_u || !dst_v) {
1084 return -1;
1085 }
1086 // Negative height means invert the image.
1087 if (height < 0) {
1088 height = -height;
1089 src_y = src_y + (height - 1) * src_stride_y;
1090 src_u = src_u + (height - 1) * src_stride_u;
1091 src_v = src_v + (height - 1) * src_stride_v;
1092 src_stride_y = -src_stride_y;
1093 src_stride_u = -src_stride_u;
1094 src_stride_v = -src_stride_v;
1095 }
1096
1097 switch (mode) {
1098 case kRotate0:
1099 // Copy frame
1100 CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1101 CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
1102 CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
1103 return 0;
1104
1105 // Note on temporary Y plane for UV.
1106 // Rotation of UV first fits within the Y destination plane rows.
1107 // Y plane is width x height
1108 // Y plane rotated is height x width
1109 // UV plane is (width / 2) x height
1110 // UV plane rotated is height x (width / 2)
1111 // UV plane rotated+scaled is (height / 2) x width.
1112 // UV plane rotated is a temporary that fits within the Y plane rotated.
1113
1114 case kRotate90:
1115 RotatePlane90_16(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
1116 height);
1117 r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_u,
1118 dst_stride_u, halfheight, width, kFilterBilinear);
1119 if (r != 0) {
1120 return r;
1121 }
1122 RotatePlane90_16(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
1123 height);
1124 r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_v,
1125 dst_stride_v, halfheight, width, kFilterLinear);
1126 if (r != 0) {
1127 return r;
1128 }
1129 RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1130 return 0;
1131 case kRotate270:
1132 RotatePlane270_16(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
1133 height);
1134 r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_u,
1135 dst_stride_u, halfheight, width, kFilterBilinear);
1136 if (r != 0) {
1137 return r;
1138 }
1139 RotatePlane270_16(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
1140 height);
1141 r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_v,
1142 dst_stride_v, halfheight, width, kFilterLinear);
1143 if (r != 0) {
1144 return r;
1145 }
1146 RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1147 height);
1148 return 0;
1149 case kRotate180:
1150 RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1151 height);
1152 RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1153 height);
1154 RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1155 height);
1156 return 0;
1157 default:
1158 break;
1159 }
1160 return -1;
1161 }
1162
1163 LIBYUV_API
I410Rotate(const uint16_t * src_y,int src_stride_y,const uint16_t * src_u,int src_stride_u,const uint16_t * src_v,int src_stride_v,uint16_t * dst_y,int dst_stride_y,uint16_t * dst_u,int dst_stride_u,uint16_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)1164 int I410Rotate(const uint16_t* src_y,
1165 int src_stride_y,
1166 const uint16_t* src_u,
1167 int src_stride_u,
1168 const uint16_t* src_v,
1169 int src_stride_v,
1170 uint16_t* dst_y,
1171 int dst_stride_y,
1172 uint16_t* dst_u,
1173 int dst_stride_u,
1174 uint16_t* dst_v,
1175 int dst_stride_v,
1176 int width,
1177 int height,
1178 enum RotationMode mode) {
1179 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1180 !dst_u || !dst_v || dst_stride_y < 0) {
1181 return -1;
1182 }
1183 // Negative height means invert the image.
1184 if (height < 0) {
1185 height = -height;
1186 src_y = src_y + (height - 1) * src_stride_y;
1187 src_u = src_u + (height - 1) * src_stride_u;
1188 src_v = src_v + (height - 1) * src_stride_v;
1189 src_stride_y = -src_stride_y;
1190 src_stride_u = -src_stride_u;
1191 src_stride_v = -src_stride_v;
1192 }
1193
1194 switch (mode) {
1195 case kRotate0:
1196 // copy frame
1197 CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1198 CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
1199 CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
1200 return 0;
1201 case kRotate90:
1202 RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1203 RotatePlane90_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
1204 RotatePlane90_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
1205 return 0;
1206 case kRotate270:
1207 RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1208 height);
1209 RotatePlane270_16(src_u, src_stride_u, dst_u, dst_stride_u, width,
1210 height);
1211 RotatePlane270_16(src_v, src_stride_v, dst_v, dst_stride_v, width,
1212 height);
1213 return 0;
1214 case kRotate180:
1215 RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1216 height);
1217 RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, width,
1218 height);
1219 RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, width,
1220 height);
1221 return 0;
1222 default:
1223 break;
1224 }
1225 return -1;
1226 }
1227
1228 #ifdef __cplusplus
1229 } // extern "C"
1230 } // namespace libyuv
1231 #endif
1232