• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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