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