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