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, 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
ARGBTranspose(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate90(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate270(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate180(const uint8 * src,int src_stride,uint8 * dst,int dst_stride,int width,int height)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
ARGBRotate(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height,enum RotationMode mode)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