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