1 /*
2  *  Copyright 2012 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/row.h"
17 
18 #ifdef __cplusplus
19 namespace libyuv {
20 extern "C" {
ScanEOI(const uint8 * sample,size_t sample_size)21 #endif
22 
23 // ARGBScale has a function to copy pixels to a row, striding each source
24 // pixel by a constant.
25 #if !defined(LIBYUV_DISABLE_X86) && \
26     (defined(_M_IX86) || \
27     (defined(__x86_64__) && !defined(__native_client__)) || defined(__i386__))
28 #define HAS_SCALEARGBROWDOWNEVEN_SSE2
29 void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride,
30                                int src_stepx, uint8* dst_ptr, int dst_width);
31 #endif
32 #if !defined(LIBYUV_DISABLE_NEON) && !defined(__native_client__) && \
33     (defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
34 #define HAS_SCALEARGBROWDOWNEVEN_NEON
35 void ScaleARGBRowDownEven_NEON(const uint8* src_ptr, int src_stride,
36                                int src_stepx, uint8* dst_ptr, int dst_width);
37 #endif
38 
39 void ScaleARGBRowDownEven_C(const uint8* src_ptr, int,
40                             int src_stepx, uint8* dst_ptr, int dst_width);
41 
ValidateJpeg(const uint8 * sample,size_t sample_size)42 static void ARGBTranspose(const uint8* src, int src_stride,
43                           uint8* dst, int dst_stride, int width, int height) {
44   int i;
45   int src_pixel_step = src_stride >> 2;
46   void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride,
47       int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C;
48 #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
49   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(height, 4)) {  // Width of dest.
50     ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2;
51   }
52 #endif
53 #if defined(HAS_SCALEARGBROWDOWNEVEN_NEON)
54   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(height, 4)) {  // Width of dest.
55     ScaleARGBRowDownEven = ScaleARGBRowDownEven_NEON;
56   }
57 #endif
58 
59   for (i = 0; i < width; ++i) {  // column of source to row of dest.
60     ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height);
61     dst += dst_stride;
62     src += 4;
63   }
64 }
65 
66 void ARGBRotate90(const uint8* src, int src_stride,
67                   uint8* dst, int dst_stride, int width, int height) {
68   // Rotate by 90 is a ARGBTranspose with the source read
69   // from bottom to top. So set the source pointer to the end
70   // of the buffer and flip the sign of the source stride.
71   src += src_stride * (height - 1);
72   src_stride = -src_stride;
73   ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
74 }
75 
76 void ARGBRotate270(const uint8* src, int src_stride,
77                     uint8* dst, int dst_stride, int width, int height) {
78   // Rotate by 270 is a ARGBTranspose with the destination written
79   // from bottom to top. So set the destination pointer to the end
80   // of the buffer and flip the sign of the destination stride.
81   dst += dst_stride * (width - 1);
82   dst_stride = -dst_stride;
83   ARGBTranspose(src, src_stride, dst, dst_stride, width, height);
84 }
85 
86 void ARGBRotate180(const uint8* src, int src_stride,
87                    uint8* dst, int dst_stride, int width, int height) {
88   // Swap first and last row and mirror the content. Uses a temporary row.
89   align_buffer_64(row, width * 4);
90   const uint8* src_bot = src + src_stride * (height - 1);
91   uint8* dst_bot = dst + dst_stride * (height - 1);
92   int half_height = (height + 1) >> 1;
93   int y;
94   void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) =
95       ARGBMirrorRow_C;
96   void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
97 #if defined(HAS_ARGBMIRRORROW_NEON)
98   if (TestCpuFlag(kCpuHasNEON)) {
99     ARGBMirrorRow = ARGBMirrorRow_Any_NEON;
100     if (IS_ALIGNED(width, 4)) {
101       ARGBMirrorRow = ARGBMirrorRow_NEON;
102     }
103   }
104 #endif
105 #if defined(HAS_ARGBMIRRORROW_SSE2)
106   if (TestCpuFlag(kCpuHasSSE2)) {
107     ARGBMirrorRow = ARGBMirrorRow_Any_SSE2;
108     if (IS_ALIGNED(width, 4)) {
109       ARGBMirrorRow = ARGBMirrorRow_SSE2;
110     }
111   }
112 #endif
113 #if defined(HAS_ARGBMIRRORROW_AVX2)
114   if (TestCpuFlag(kCpuHasAVX2)) {
115     ARGBMirrorRow = ARGBMirrorRow_Any_AVX2;
116     if (IS_ALIGNED(width, 8)) {
117       ARGBMirrorRow = ARGBMirrorRow_AVX2;
118     }
119   }
120 #endif
121 #if defined(HAS_COPYROW_SSE2)
122   if (TestCpuFlag(kCpuHasSSE2)) {
123     CopyRow = IS_ALIGNED(width * 4, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
124   }
125 #endif
126 #if defined(HAS_COPYROW_AVX)
127   if (TestCpuFlag(kCpuHasAVX)) {
128     CopyRow = IS_ALIGNED(width * 4, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
129   }
130 #endif
131 #if defined(HAS_COPYROW_ERMS)
132   if (TestCpuFlag(kCpuHasERMS)) {
133     CopyRow = CopyRow_ERMS;
134   }
135 #endif
136 #if defined(HAS_COPYROW_NEON)
137   if (TestCpuFlag(kCpuHasNEON)) {
138     CopyRow = IS_ALIGNED(width * 4, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
139   }
140 #endif
141 #if defined(HAS_COPYROW_MIPS)
142   if (TestCpuFlag(kCpuHasMIPS)) {
143     CopyRow = CopyRow_MIPS;
144   }
145 #endif
146 
147   // Odd height will harmlessly mirror the middle row twice.
148   for (y = 0; y < half_height; ++y) {
149     ARGBMirrorRow(src, row, width);  // Mirror first row into a buffer
150     ARGBMirrorRow(src_bot, dst, width);  // Mirror last row into first row
151     CopyRow(row, dst_bot, width * 4);  // Copy first mirrored row into last
152     src += src_stride;
153     dst += dst_stride;
154     src_bot -= src_stride;
155     dst_bot -= dst_stride;
156   }
157   free_aligned_buffer_64(row);
158 }
159 
160 LIBYUV_API
161 int ARGBRotate(const uint8* src_argb, int src_stride_argb,
162                uint8* dst_argb, int dst_stride_argb, int width, int height,
163                enum RotationMode mode) {
164   if (!src_argb || width <= 0 || height == 0 || !dst_argb) {
165     return -1;
166   }
167 
168   // Negative height means invert the image.
169   if (height < 0) {
170     height = -height;
171     src_argb = src_argb + (height - 1) * src_stride_argb;
172     src_stride_argb = -src_stride_argb;
173   }
174 
175   switch (mode) {
176     case kRotate0:
177       // copy frame
178       return ARGBCopy(src_argb, src_stride_argb,
179                       dst_argb, dst_stride_argb,
180                       width, height);
181     case kRotate90:
182       ARGBRotate90(src_argb, src_stride_argb,
183                    dst_argb, dst_stride_argb,
184                    width, height);
185       return 0;
186     case kRotate270:
187       ARGBRotate270(src_argb, src_stride_argb,
188                     dst_argb, dst_stride_argb,
189                     width, height);
190       return 0;
191     case kRotate180:
192       ARGBRotate180(src_argb, src_stride_argb,
193                     dst_argb, dst_stride_argb,
194                     width, height);
195       return 0;
196     default:
197       break;
198   }
199   return -1;
200 }
201 
202 #ifdef __cplusplus
203 }  // extern "C"
204 }  // namespace libyuv
205 #endif
206