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