• 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 <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