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