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