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/row.h"
12
13 #include <stdio.h>
14
15 #ifdef __cplusplus
16 namespace libyuv {
17 extern "C" {
18 #endif
19
20 // This module is for GCC Neon
21 #if !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__) && \
22 !defined(__aarch64__)
23
24 // Read 8 Y, 4 U and 4 V from 422
25 #define READYUV422 \
26 "vld1.8 {d0}, [%0]! \n" \
27 "vld1.32 {d2[0]}, [%1]! \n" \
28 "vld1.32 {d2[1]}, [%2]! \n"
29
30 // Read 8 Y, 8 U and 8 V from 444
31 #define READYUV444 \
32 "vld1.8 {d0}, [%0]! \n" \
33 "vld1.8 {d2}, [%1]! \n" \
34 "vld1.8 {d3}, [%2]! \n" \
35 "vpaddl.u8 q1, q1 \n" \
36 "vrshrn.u16 d2, q1, #1 \n"
37
38 // Read 8 Y, and set 4 U and 4 V to 128
39 #define READYUV400 \
40 "vld1.8 {d0}, [%0]! \n" \
41 "vmov.u8 d2, #128 \n"
42
43 // Read 8 Y and 4 UV from NV12
44 #define READNV12 \
45 "vld1.8 {d0}, [%0]! \n" \
46 "vld1.8 {d2}, [%1]! \n" \
47 "vmov.u8 d3, d2 \n" /* split odd/even uv apart */ \
48 "vuzp.u8 d2, d3 \n" \
49 "vtrn.u32 d2, d3 \n"
50
51 // Read 8 Y and 4 VU from NV21
52 #define READNV21 \
53 "vld1.8 {d0}, [%0]! \n" \
54 "vld1.8 {d2}, [%1]! \n" \
55 "vmov.u8 d3, d2 \n" /* split odd/even uv apart */ \
56 "vuzp.u8 d3, d2 \n" \
57 "vtrn.u32 d2, d3 \n"
58
59 // Read 8 YUY2
60 #define READYUY2 \
61 "vld2.8 {d0, d2}, [%0]! \n" \
62 "vmov.u8 d3, d2 \n" \
63 "vuzp.u8 d2, d3 \n" \
64 "vtrn.u32 d2, d3 \n"
65
66 // Read 8 UYVY
67 #define READUYVY \
68 "vld2.8 {d2, d3}, [%0]! \n" \
69 "vmov.u8 d0, d3 \n" \
70 "vmov.u8 d3, d2 \n" \
71 "vuzp.u8 d2, d3 \n" \
72 "vtrn.u32 d2, d3 \n"
73
74 #define YUVTORGB_SETUP \
75 "vld1.8 {d24}, [%[kUVToRB]] \n" \
76 "vld1.8 {d25}, [%[kUVToG]] \n" \
77 "vld1.16 {d26[], d27[]}, [%[kUVBiasBGR]]! \n" \
78 "vld1.16 {d8[], d9[]}, [%[kUVBiasBGR]]! \n" \
79 "vld1.16 {d28[], d29[]}, [%[kUVBiasBGR]] \n" \
80 "vld1.32 {d30[], d31[]}, [%[kYToRgb]] \n"
81
82 #define YUVTORGB \
83 "vmull.u8 q8, d2, d24 \n" /* u/v B/R component */ \
84 "vmull.u8 q9, d2, d25 \n" /* u/v G component */ \
85 "vmovl.u8 q0, d0 \n" /* Y */ \
86 "vmovl.s16 q10, d1 \n" \
87 "vmovl.s16 q0, d0 \n" \
88 "vmul.s32 q10, q10, q15 \n" \
89 "vmul.s32 q0, q0, q15 \n" \
90 "vqshrun.s32 d0, q0, #16 \n" \
91 "vqshrun.s32 d1, q10, #16 \n" /* Y */ \
92 "vadd.s16 d18, d19 \n" \
93 "vshll.u16 q1, d16, #16 \n" /* Replicate u * UB */ \
94 "vshll.u16 q10, d17, #16 \n" /* Replicate v * VR */ \
95 "vshll.u16 q3, d18, #16 \n" /* Replicate (v*VG + u*UG)*/ \
96 "vaddw.u16 q1, q1, d16 \n" \
97 "vaddw.u16 q10, q10, d17 \n" \
98 "vaddw.u16 q3, q3, d18 \n" \
99 "vqadd.s16 q8, q0, q13 \n" /* B */ \
100 "vqadd.s16 q9, q0, q14 \n" /* R */ \
101 "vqadd.s16 q0, q0, q4 \n" /* G */ \
102 "vqadd.s16 q8, q8, q1 \n" /* B */ \
103 "vqadd.s16 q9, q9, q10 \n" /* R */ \
104 "vqsub.s16 q0, q0, q3 \n" /* G */ \
105 "vqshrun.s16 d20, q8, #6 \n" /* B */ \
106 "vqshrun.s16 d22, q9, #6 \n" /* R */ \
107 "vqshrun.s16 d21, q0, #6 \n" /* G */
108
I444ToARGBRow_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)109 void I444ToARGBRow_NEON(const uint8* src_y,
110 const uint8* src_u,
111 const uint8* src_v,
112 uint8* dst_argb,
113 const struct YuvConstants* yuvconstants,
114 int width) {
115 asm volatile(
116 YUVTORGB_SETUP
117 "vmov.u8 d23, #255 \n"
118 "1: \n" READYUV444 YUVTORGB
119 "subs %4, %4, #8 \n"
120 "vst4.8 {d20, d21, d22, d23}, [%3]! \n"
121 "bgt 1b \n"
122 : "+r"(src_y), // %0
123 "+r"(src_u), // %1
124 "+r"(src_v), // %2
125 "+r"(dst_argb), // %3
126 "+r"(width) // %4
127 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
128 [kUVToG] "r"(&yuvconstants->kUVToG),
129 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
130 [kYToRgb] "r"(&yuvconstants->kYToRgb)
131 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
132 "q12", "q13", "q14", "q15");
133 }
134
I422ToARGBRow_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)135 void I422ToARGBRow_NEON(const uint8* src_y,
136 const uint8* src_u,
137 const uint8* src_v,
138 uint8* dst_argb,
139 const struct YuvConstants* yuvconstants,
140 int width) {
141 asm volatile(
142 YUVTORGB_SETUP
143 "vmov.u8 d23, #255 \n"
144 "1: \n" READYUV422 YUVTORGB
145 "subs %4, %4, #8 \n"
146 "vst4.8 {d20, d21, d22, d23}, [%3]! \n"
147 "bgt 1b \n"
148 : "+r"(src_y), // %0
149 "+r"(src_u), // %1
150 "+r"(src_v), // %2
151 "+r"(dst_argb), // %3
152 "+r"(width) // %4
153 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
154 [kUVToG] "r"(&yuvconstants->kUVToG),
155 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
156 [kYToRgb] "r"(&yuvconstants->kYToRgb)
157 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
158 "q12", "q13", "q14", "q15");
159 }
160
I422AlphaToARGBRow_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,const uint8 * src_a,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)161 void I422AlphaToARGBRow_NEON(const uint8* src_y,
162 const uint8* src_u,
163 const uint8* src_v,
164 const uint8* src_a,
165 uint8* dst_argb,
166 const struct YuvConstants* yuvconstants,
167 int width) {
168 asm volatile(
169 YUVTORGB_SETUP
170 "1: \n" READYUV422 YUVTORGB
171 "subs %5, %5, #8 \n"
172 "vld1.8 {d23}, [%3]! \n"
173 "vst4.8 {d20, d21, d22, d23}, [%4]! \n"
174 "bgt 1b \n"
175 : "+r"(src_y), // %0
176 "+r"(src_u), // %1
177 "+r"(src_v), // %2
178 "+r"(src_a), // %3
179 "+r"(dst_argb), // %4
180 "+r"(width) // %5
181 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
182 [kUVToG] "r"(&yuvconstants->kUVToG),
183 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
184 [kYToRgb] "r"(&yuvconstants->kYToRgb)
185 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
186 "q12", "q13", "q14", "q15");
187 }
188
I422ToRGBARow_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_rgba,const struct YuvConstants * yuvconstants,int width)189 void I422ToRGBARow_NEON(const uint8* src_y,
190 const uint8* src_u,
191 const uint8* src_v,
192 uint8* dst_rgba,
193 const struct YuvConstants* yuvconstants,
194 int width) {
195 asm volatile(
196 YUVTORGB_SETUP
197 "1: \n" READYUV422 YUVTORGB
198 "subs %4, %4, #8 \n"
199 "vmov.u8 d19, #255 \n" // d19 modified by
200 // YUVTORGB
201 "vst4.8 {d19, d20, d21, d22}, [%3]! \n"
202 "bgt 1b \n"
203 : "+r"(src_y), // %0
204 "+r"(src_u), // %1
205 "+r"(src_v), // %2
206 "+r"(dst_rgba), // %3
207 "+r"(width) // %4
208 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
209 [kUVToG] "r"(&yuvconstants->kUVToG),
210 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
211 [kYToRgb] "r"(&yuvconstants->kYToRgb)
212 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
213 "q12", "q13", "q14", "q15");
214 }
215
I422ToRGB24Row_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_rgb24,const struct YuvConstants * yuvconstants,int width)216 void I422ToRGB24Row_NEON(const uint8* src_y,
217 const uint8* src_u,
218 const uint8* src_v,
219 uint8* dst_rgb24,
220 const struct YuvConstants* yuvconstants,
221 int width) {
222 asm volatile(
223 YUVTORGB_SETUP
224 "1: \n" READYUV422 YUVTORGB
225 "subs %4, %4, #8 \n"
226 "vst3.8 {d20, d21, d22}, [%3]! \n"
227 "bgt 1b \n"
228 : "+r"(src_y), // %0
229 "+r"(src_u), // %1
230 "+r"(src_v), // %2
231 "+r"(dst_rgb24), // %3
232 "+r"(width) // %4
233 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
234 [kUVToG] "r"(&yuvconstants->kUVToG),
235 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
236 [kYToRgb] "r"(&yuvconstants->kYToRgb)
237 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
238 "q12", "q13", "q14", "q15");
239 }
240
241 #define ARGBTORGB565 \
242 "vshll.u8 q0, d22, #8 \n" /* R */ \
243 "vshll.u8 q8, d21, #8 \n" /* G */ \
244 "vshll.u8 q9, d20, #8 \n" /* B */ \
245 "vsri.16 q0, q8, #5 \n" /* RG */ \
246 "vsri.16 q0, q9, #11 \n" /* RGB */
247
I422ToRGB565Row_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_rgb565,const struct YuvConstants * yuvconstants,int width)248 void I422ToRGB565Row_NEON(const uint8* src_y,
249 const uint8* src_u,
250 const uint8* src_v,
251 uint8* dst_rgb565,
252 const struct YuvConstants* yuvconstants,
253 int width) {
254 asm volatile(
255 YUVTORGB_SETUP
256 "1: \n" READYUV422 YUVTORGB
257 "subs %4, %4, #8 \n" ARGBTORGB565
258 "vst1.8 {q0}, [%3]! \n" // store 8 pixels RGB565.
259 "bgt 1b \n"
260 : "+r"(src_y), // %0
261 "+r"(src_u), // %1
262 "+r"(src_v), // %2
263 "+r"(dst_rgb565), // %3
264 "+r"(width) // %4
265 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
266 [kUVToG] "r"(&yuvconstants->kUVToG),
267 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
268 [kYToRgb] "r"(&yuvconstants->kYToRgb)
269 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
270 "q12", "q13", "q14", "q15");
271 }
272
273 #define ARGBTOARGB1555 \
274 "vshll.u8 q0, d23, #8 \n" /* A */ \
275 "vshll.u8 q8, d22, #8 \n" /* R */ \
276 "vshll.u8 q9, d21, #8 \n" /* G */ \
277 "vshll.u8 q10, d20, #8 \n" /* B */ \
278 "vsri.16 q0, q8, #1 \n" /* AR */ \
279 "vsri.16 q0, q9, #6 \n" /* ARG */ \
280 "vsri.16 q0, q10, #11 \n" /* ARGB */
281
I422ToARGB1555Row_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_argb1555,const struct YuvConstants * yuvconstants,int width)282 void I422ToARGB1555Row_NEON(const uint8* src_y,
283 const uint8* src_u,
284 const uint8* src_v,
285 uint8* dst_argb1555,
286 const struct YuvConstants* yuvconstants,
287 int width) {
288 asm volatile(
289 YUVTORGB_SETUP
290 "1: \n" READYUV422 YUVTORGB
291 "subs %4, %4, #8 \n"
292 "vmov.u8 d23, #255 \n" ARGBTOARGB1555
293 "vst1.8 {q0}, [%3]! \n" // store 8 pixels
294 // ARGB1555.
295 "bgt 1b \n"
296 : "+r"(src_y), // %0
297 "+r"(src_u), // %1
298 "+r"(src_v), // %2
299 "+r"(dst_argb1555), // %3
300 "+r"(width) // %4
301 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
302 [kUVToG] "r"(&yuvconstants->kUVToG),
303 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
304 [kYToRgb] "r"(&yuvconstants->kYToRgb)
305 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
306 "q12", "q13", "q14", "q15");
307 }
308
309 #define ARGBTOARGB4444 \
310 "vshr.u8 d20, d20, #4 \n" /* B */ \
311 "vbic.32 d21, d21, d4 \n" /* G */ \
312 "vshr.u8 d22, d22, #4 \n" /* R */ \
313 "vbic.32 d23, d23, d4 \n" /* A */ \
314 "vorr d0, d20, d21 \n" /* BG */ \
315 "vorr d1, d22, d23 \n" /* RA */ \
316 "vzip.u8 d0, d1 \n" /* BGRA */
317
I422ToARGB4444Row_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_argb4444,const struct YuvConstants * yuvconstants,int width)318 void I422ToARGB4444Row_NEON(const uint8* src_y,
319 const uint8* src_u,
320 const uint8* src_v,
321 uint8* dst_argb4444,
322 const struct YuvConstants* yuvconstants,
323 int width) {
324 asm volatile(
325 YUVTORGB_SETUP
326 "vmov.u8 d4, #0x0f \n" // bits to clear with
327 // vbic.
328 "1: \n" READYUV422 YUVTORGB
329 "subs %4, %4, #8 \n"
330 "vmov.u8 d23, #255 \n" ARGBTOARGB4444
331 "vst1.8 {q0}, [%3]! \n" // store 8 pixels
332 // ARGB4444.
333 "bgt 1b \n"
334 : "+r"(src_y), // %0
335 "+r"(src_u), // %1
336 "+r"(src_v), // %2
337 "+r"(dst_argb4444), // %3
338 "+r"(width) // %4
339 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
340 [kUVToG] "r"(&yuvconstants->kUVToG),
341 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
342 [kYToRgb] "r"(&yuvconstants->kYToRgb)
343 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
344 "q12", "q13", "q14", "q15");
345 }
346
I400ToARGBRow_NEON(const uint8 * src_y,uint8 * dst_argb,int width)347 void I400ToARGBRow_NEON(const uint8* src_y, uint8* dst_argb, int width) {
348 asm volatile(
349 YUVTORGB_SETUP
350 "vmov.u8 d23, #255 \n"
351 "1: \n" READYUV400 YUVTORGB
352 "subs %2, %2, #8 \n"
353 "vst4.8 {d20, d21, d22, d23}, [%1]! \n"
354 "bgt 1b \n"
355 : "+r"(src_y), // %0
356 "+r"(dst_argb), // %1
357 "+r"(width) // %2
358 : [kUVToRB] "r"(&kYuvI601Constants.kUVToRB),
359 [kUVToG] "r"(&kYuvI601Constants.kUVToG),
360 [kUVBiasBGR] "r"(&kYuvI601Constants.kUVBiasBGR),
361 [kYToRgb] "r"(&kYuvI601Constants.kYToRgb)
362 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
363 "q12", "q13", "q14", "q15");
364 }
365
J400ToARGBRow_NEON(const uint8 * src_y,uint8 * dst_argb,int width)366 void J400ToARGBRow_NEON(const uint8* src_y, uint8* dst_argb, int width) {
367 asm volatile(
368 "vmov.u8 d23, #255 \n"
369 "1: \n"
370 "vld1.8 {d20}, [%0]! \n"
371 "vmov d21, d20 \n"
372 "vmov d22, d20 \n"
373 "subs %2, %2, #8 \n"
374 "vst4.8 {d20, d21, d22, d23}, [%1]! \n"
375 "bgt 1b \n"
376 : "+r"(src_y), // %0
377 "+r"(dst_argb), // %1
378 "+r"(width) // %2
379 :
380 : "cc", "memory", "d20", "d21", "d22", "d23");
381 }
382
NV12ToARGBRow_NEON(const uint8 * src_y,const uint8 * src_uv,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)383 void NV12ToARGBRow_NEON(const uint8* src_y,
384 const uint8* src_uv,
385 uint8* dst_argb,
386 const struct YuvConstants* yuvconstants,
387 int width) {
388 asm volatile(YUVTORGB_SETUP
389 "vmov.u8 d23, #255 \n"
390 "1: \n" READNV12 YUVTORGB
391 "subs %3, %3, #8 \n"
392 "vst4.8 {d20, d21, d22, d23}, [%2]! \n"
393 "bgt 1b \n"
394 : "+r"(src_y), // %0
395 "+r"(src_uv), // %1
396 "+r"(dst_argb), // %2
397 "+r"(width) // %3
398 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
399 [kUVToG] "r"(&yuvconstants->kUVToG),
400 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
401 [kYToRgb] "r"(&yuvconstants->kYToRgb)
402 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9",
403 "q10", "q11", "q12", "q13", "q14", "q15");
404 }
405
NV21ToARGBRow_NEON(const uint8 * src_y,const uint8 * src_vu,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)406 void NV21ToARGBRow_NEON(const uint8* src_y,
407 const uint8* src_vu,
408 uint8* dst_argb,
409 const struct YuvConstants* yuvconstants,
410 int width) {
411 asm volatile(YUVTORGB_SETUP
412 "vmov.u8 d23, #255 \n"
413 "1: \n" READNV21 YUVTORGB
414 "subs %3, %3, #8 \n"
415 "vst4.8 {d20, d21, d22, d23}, [%2]! \n"
416 "bgt 1b \n"
417 : "+r"(src_y), // %0
418 "+r"(src_vu), // %1
419 "+r"(dst_argb), // %2
420 "+r"(width) // %3
421 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
422 [kUVToG] "r"(&yuvconstants->kUVToG),
423 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
424 [kYToRgb] "r"(&yuvconstants->kYToRgb)
425 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9",
426 "q10", "q11", "q12", "q13", "q14", "q15");
427 }
428
NV12ToRGB565Row_NEON(const uint8 * src_y,const uint8 * src_uv,uint8 * dst_rgb565,const struct YuvConstants * yuvconstants,int width)429 void NV12ToRGB565Row_NEON(const uint8* src_y,
430 const uint8* src_uv,
431 uint8* dst_rgb565,
432 const struct YuvConstants* yuvconstants,
433 int width) {
434 asm volatile(
435 YUVTORGB_SETUP
436 "1: \n" READNV12 YUVTORGB
437 "subs %3, %3, #8 \n" ARGBTORGB565
438 "vst1.8 {q0}, [%2]! \n" // store 8 pixels RGB565.
439 "bgt 1b \n"
440 : "+r"(src_y), // %0
441 "+r"(src_uv), // %1
442 "+r"(dst_rgb565), // %2
443 "+r"(width) // %3
444 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
445 [kUVToG] "r"(&yuvconstants->kUVToG),
446 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
447 [kYToRgb] "r"(&yuvconstants->kYToRgb)
448 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11",
449 "q12", "q13", "q14", "q15");
450 }
451
YUY2ToARGBRow_NEON(const uint8 * src_yuy2,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)452 void YUY2ToARGBRow_NEON(const uint8* src_yuy2,
453 uint8* dst_argb,
454 const struct YuvConstants* yuvconstants,
455 int width) {
456 asm volatile(YUVTORGB_SETUP
457 "vmov.u8 d23, #255 \n"
458 "1: \n" READYUY2 YUVTORGB
459 "subs %2, %2, #8 \n"
460 "vst4.8 {d20, d21, d22, d23}, [%1]! \n"
461 "bgt 1b \n"
462 : "+r"(src_yuy2), // %0
463 "+r"(dst_argb), // %1
464 "+r"(width) // %2
465 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
466 [kUVToG] "r"(&yuvconstants->kUVToG),
467 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
468 [kYToRgb] "r"(&yuvconstants->kYToRgb)
469 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9",
470 "q10", "q11", "q12", "q13", "q14", "q15");
471 }
472
UYVYToARGBRow_NEON(const uint8 * src_uyvy,uint8 * dst_argb,const struct YuvConstants * yuvconstants,int width)473 void UYVYToARGBRow_NEON(const uint8* src_uyvy,
474 uint8* dst_argb,
475 const struct YuvConstants* yuvconstants,
476 int width) {
477 asm volatile(YUVTORGB_SETUP
478 "vmov.u8 d23, #255 \n"
479 "1: \n" READUYVY YUVTORGB
480 "subs %2, %2, #8 \n"
481 "vst4.8 {d20, d21, d22, d23}, [%1]! \n"
482 "bgt 1b \n"
483 : "+r"(src_uyvy), // %0
484 "+r"(dst_argb), // %1
485 "+r"(width) // %2
486 : [kUVToRB] "r"(&yuvconstants->kUVToRB),
487 [kUVToG] "r"(&yuvconstants->kUVToG),
488 [kUVBiasBGR] "r"(&yuvconstants->kUVBiasBGR),
489 [kYToRgb] "r"(&yuvconstants->kYToRgb)
490 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q8", "q9",
491 "q10", "q11", "q12", "q13", "q14", "q15");
492 }
493
494 // Reads 16 pairs of UV and write even values to dst_u and odd to dst_v.
SplitUVRow_NEON(const uint8 * src_uv,uint8 * dst_u,uint8 * dst_v,int width)495 void SplitUVRow_NEON(const uint8* src_uv,
496 uint8* dst_u,
497 uint8* dst_v,
498 int width) {
499 asm volatile(
500 "1: \n"
501 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pairs of UV
502 "subs %3, %3, #16 \n" // 16 processed per loop
503 "vst1.8 {q0}, [%1]! \n" // store U
504 "vst1.8 {q1}, [%2]! \n" // store V
505 "bgt 1b \n"
506 : "+r"(src_uv), // %0
507 "+r"(dst_u), // %1
508 "+r"(dst_v), // %2
509 "+r"(width) // %3 // Output registers
510 : // Input registers
511 : "cc", "memory", "q0", "q1" // Clobber List
512 );
513 }
514
515 // Reads 16 U's and V's and writes out 16 pairs of UV.
MergeUVRow_NEON(const uint8 * src_u,const uint8 * src_v,uint8 * dst_uv,int width)516 void MergeUVRow_NEON(const uint8* src_u,
517 const uint8* src_v,
518 uint8* dst_uv,
519 int width) {
520 asm volatile(
521 "1: \n"
522 "vld1.8 {q0}, [%0]! \n" // load U
523 "vld1.8 {q1}, [%1]! \n" // load V
524 "subs %3, %3, #16 \n" // 16 processed per loop
525 "vst2.8 {q0, q1}, [%2]! \n" // store 16 pairs of UV
526 "bgt 1b \n"
527 : "+r"(src_u), // %0
528 "+r"(src_v), // %1
529 "+r"(dst_uv), // %2
530 "+r"(width) // %3 // Output registers
531 : // Input registers
532 : "cc", "memory", "q0", "q1" // Clobber List
533 );
534 }
535
536 // Reads 16 packed RGB and write to planar dst_r, dst_g, dst_b.
SplitRGBRow_NEON(const uint8 * src_rgb,uint8 * dst_r,uint8 * dst_g,uint8 * dst_b,int width)537 void SplitRGBRow_NEON(const uint8* src_rgb,
538 uint8* dst_r,
539 uint8* dst_g,
540 uint8* dst_b,
541 int width) {
542 asm volatile(
543 "1: \n"
544 "vld3.8 {d0, d2, d4}, [%0]! \n" // load 8 RGB
545 "vld3.8 {d1, d3, d5}, [%0]! \n" // next 8 RGB
546 "subs %4, %4, #16 \n" // 16 processed per loop
547 "vst1.8 {q0}, [%1]! \n" // store R
548 "vst1.8 {q1}, [%2]! \n" // store G
549 "vst1.8 {q2}, [%3]! \n" // store B
550 "bgt 1b \n"
551 : "+r"(src_rgb), // %0
552 "+r"(dst_r), // %1
553 "+r"(dst_g), // %2
554 "+r"(dst_b), // %3
555 "+r"(width) // %4
556 : // Input registers
557 : "cc", "memory", "d0", "d1", "d2" // Clobber List
558 );
559 }
560
561 // Reads 16 planar R's, G's and B's and writes out 16 packed RGB at a time
MergeRGBRow_NEON(const uint8 * src_r,const uint8 * src_g,const uint8 * src_b,uint8 * dst_rgb,int width)562 void MergeRGBRow_NEON(const uint8* src_r,
563 const uint8* src_g,
564 const uint8* src_b,
565 uint8* dst_rgb,
566 int width) {
567 asm volatile(
568 "1: \n"
569 "vld1.8 {q0}, [%0]! \n" // load R
570 "vld1.8 {q1}, [%1]! \n" // load G
571 "vld1.8 {q2}, [%2]! \n" // load B
572 "subs %4, %4, #16 \n" // 16 processed per loop
573 "vst3.8 {d0, d2, d4}, [%3]! \n" // store 8 RGB
574 "vst3.8 {d1, d3, d5}, [%3]! \n" // next 8 RGB
575 "bgt 1b \n"
576 : "+r"(src_r), // %0
577 "+r"(src_g), // %1
578 "+r"(src_b), // %2
579 "+r"(dst_rgb), // %3
580 "+r"(width) // %4
581 : // Input registers
582 : "cc", "memory", "q0", "q1", "q2" // Clobber List
583 );
584 }
585
586 // Copy multiple of 32. vld4.8 allow unaligned and is fastest on a15.
CopyRow_NEON(const uint8 * src,uint8 * dst,int count)587 void CopyRow_NEON(const uint8* src, uint8* dst, int count) {
588 asm volatile(
589 "1: \n"
590 "vld1.8 {d0, d1, d2, d3}, [%0]! \n" // load 32
591 "subs %2, %2, #32 \n" // 32 processed per loop
592 "vst1.8 {d0, d1, d2, d3}, [%1]! \n" // store 32
593 "bgt 1b \n"
594 : "+r"(src), // %0
595 "+r"(dst), // %1
596 "+r"(count) // %2 // Output registers
597 : // Input registers
598 : "cc", "memory", "q0", "q1" // Clobber List
599 );
600 }
601
602 // SetRow writes 'count' bytes using an 8 bit value repeated.
SetRow_NEON(uint8 * dst,uint8 v8,int count)603 void SetRow_NEON(uint8* dst, uint8 v8, int count) {
604 asm volatile(
605 "vdup.8 q0, %2 \n" // duplicate 16 bytes
606 "1: \n"
607 "subs %1, %1, #16 \n" // 16 bytes per loop
608 "vst1.8 {q0}, [%0]! \n" // store
609 "bgt 1b \n"
610 : "+r"(dst), // %0
611 "+r"(count) // %1
612 : "r"(v8) // %2
613 : "cc", "memory", "q0");
614 }
615
616 // ARGBSetRow writes 'count' pixels using an 32 bit value repeated.
ARGBSetRow_NEON(uint8 * dst,uint32 v32,int count)617 void ARGBSetRow_NEON(uint8* dst, uint32 v32, int count) {
618 asm volatile(
619 "vdup.u32 q0, %2 \n" // duplicate 4 ints
620 "1: \n"
621 "subs %1, %1, #4 \n" // 4 pixels per loop
622 "vst1.8 {q0}, [%0]! \n" // store
623 "bgt 1b \n"
624 : "+r"(dst), // %0
625 "+r"(count) // %1
626 : "r"(v32) // %2
627 : "cc", "memory", "q0");
628 }
629
MirrorRow_NEON(const uint8 * src,uint8 * dst,int width)630 void MirrorRow_NEON(const uint8* src, uint8* dst, int width) {
631 asm volatile(
632 // Start at end of source row.
633 "mov r3, #-16 \n"
634 "add %0, %0, %2 \n"
635 "sub %0, #16 \n"
636
637 "1: \n"
638 "vld1.8 {q0}, [%0], r3 \n" // src -= 16
639 "subs %2, #16 \n" // 16 pixels per loop.
640 "vrev64.8 q0, q0 \n"
641 "vst1.8 {d1}, [%1]! \n" // dst += 16
642 "vst1.8 {d0}, [%1]! \n"
643 "bgt 1b \n"
644 : "+r"(src), // %0
645 "+r"(dst), // %1
646 "+r"(width) // %2
647 :
648 : "cc", "memory", "r3", "q0");
649 }
650
MirrorUVRow_NEON(const uint8 * src_uv,uint8 * dst_u,uint8 * dst_v,int width)651 void MirrorUVRow_NEON(const uint8* src_uv,
652 uint8* dst_u,
653 uint8* dst_v,
654 int width) {
655 asm volatile(
656 // Start at end of source row.
657 "mov r12, #-16 \n"
658 "add %0, %0, %3, lsl #1 \n"
659 "sub %0, #16 \n"
660
661 "1: \n"
662 "vld2.8 {d0, d1}, [%0], r12 \n" // src -= 16
663 "subs %3, #8 \n" // 8 pixels per loop.
664 "vrev64.8 q0, q0 \n"
665 "vst1.8 {d0}, [%1]! \n" // dst += 8
666 "vst1.8 {d1}, [%2]! \n"
667 "bgt 1b \n"
668 : "+r"(src_uv), // %0
669 "+r"(dst_u), // %1
670 "+r"(dst_v), // %2
671 "+r"(width) // %3
672 :
673 : "cc", "memory", "r12", "q0");
674 }
675
ARGBMirrorRow_NEON(const uint8 * src,uint8 * dst,int width)676 void ARGBMirrorRow_NEON(const uint8* src, uint8* dst, int width) {
677 asm volatile(
678 // Start at end of source row.
679 "mov r3, #-16 \n"
680 "add %0, %0, %2, lsl #2 \n"
681 "sub %0, #16 \n"
682
683 "1: \n"
684 "vld1.8 {q0}, [%0], r3 \n" // src -= 16
685 "subs %2, #4 \n" // 4 pixels per loop.
686 "vrev64.32 q0, q0 \n"
687 "vst1.8 {d1}, [%1]! \n" // dst += 16
688 "vst1.8 {d0}, [%1]! \n"
689 "bgt 1b \n"
690 : "+r"(src), // %0
691 "+r"(dst), // %1
692 "+r"(width) // %2
693 :
694 : "cc", "memory", "r3", "q0");
695 }
696
RGB24ToARGBRow_NEON(const uint8 * src_rgb24,uint8 * dst_argb,int width)697 void RGB24ToARGBRow_NEON(const uint8* src_rgb24, uint8* dst_argb, int width) {
698 asm volatile(
699 "vmov.u8 d4, #255 \n" // Alpha
700 "1: \n"
701 "vld3.8 {d1, d2, d3}, [%0]! \n" // load 8 pixels of RGB24.
702 "subs %2, %2, #8 \n" // 8 processed per loop.
703 "vst4.8 {d1, d2, d3, d4}, [%1]! \n" // store 8 pixels of ARGB.
704 "bgt 1b \n"
705 : "+r"(src_rgb24), // %0
706 "+r"(dst_argb), // %1
707 "+r"(width) // %2
708 :
709 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List
710 );
711 }
712
RAWToARGBRow_NEON(const uint8 * src_raw,uint8 * dst_argb,int width)713 void RAWToARGBRow_NEON(const uint8* src_raw, uint8* dst_argb, int width) {
714 asm volatile(
715 "vmov.u8 d4, #255 \n" // Alpha
716 "1: \n"
717 "vld3.8 {d1, d2, d3}, [%0]! \n" // load 8 pixels of RAW.
718 "subs %2, %2, #8 \n" // 8 processed per loop.
719 "vswp.u8 d1, d3 \n" // swap R, B
720 "vst4.8 {d1, d2, d3, d4}, [%1]! \n" // store 8 pixels of ARGB.
721 "bgt 1b \n"
722 : "+r"(src_raw), // %0
723 "+r"(dst_argb), // %1
724 "+r"(width) // %2
725 :
726 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List
727 );
728 }
729
RAWToRGB24Row_NEON(const uint8 * src_raw,uint8 * dst_rgb24,int width)730 void RAWToRGB24Row_NEON(const uint8* src_raw, uint8* dst_rgb24, int width) {
731 asm volatile(
732 "1: \n"
733 "vld3.8 {d1, d2, d3}, [%0]! \n" // load 8 pixels of RAW.
734 "subs %2, %2, #8 \n" // 8 processed per loop.
735 "vswp.u8 d1, d3 \n" // swap R, B
736 "vst3.8 {d1, d2, d3}, [%1]! \n" // store 8 pixels of
737 // RGB24.
738 "bgt 1b \n"
739 : "+r"(src_raw), // %0
740 "+r"(dst_rgb24), // %1
741 "+r"(width) // %2
742 :
743 : "cc", "memory", "d1", "d2", "d3" // Clobber List
744 );
745 }
746
747 #define RGB565TOARGB \
748 "vshrn.u16 d6, q0, #5 \n" /* G xxGGGGGG */ \
749 "vuzp.u8 d0, d1 \n" /* d0 xxxBBBBB RRRRRxxx */ \
750 "vshl.u8 d6, d6, #2 \n" /* G GGGGGG00 upper 6 */ \
751 "vshr.u8 d1, d1, #3 \n" /* R 000RRRRR lower 5 */ \
752 "vshl.u8 q0, q0, #3 \n" /* B,R BBBBB000 upper 5 */ \
753 "vshr.u8 q2, q0, #5 \n" /* B,R 00000BBB lower 3 */ \
754 "vorr.u8 d0, d0, d4 \n" /* B */ \
755 "vshr.u8 d4, d6, #6 \n" /* G 000000GG lower 2 */ \
756 "vorr.u8 d2, d1, d5 \n" /* R */ \
757 "vorr.u8 d1, d4, d6 \n" /* G */
758
RGB565ToARGBRow_NEON(const uint8 * src_rgb565,uint8 * dst_argb,int width)759 void RGB565ToARGBRow_NEON(const uint8* src_rgb565, uint8* dst_argb, int width) {
760 asm volatile(
761 "vmov.u8 d3, #255 \n" // Alpha
762 "1: \n"
763 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels.
764 "subs %2, %2, #8 \n" // 8 processed per loop.
765 RGB565TOARGB
766 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB.
767 "bgt 1b \n"
768 : "+r"(src_rgb565), // %0
769 "+r"(dst_argb), // %1
770 "+r"(width) // %2
771 :
772 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List
773 );
774 }
775
776 #define ARGB1555TOARGB \
777 "vshrn.u16 d7, q0, #8 \n" /* A Arrrrrxx */ \
778 "vshr.u8 d6, d7, #2 \n" /* R xxxRRRRR */ \
779 "vshrn.u16 d5, q0, #5 \n" /* G xxxGGGGG */ \
780 "vmovn.u16 d4, q0 \n" /* B xxxBBBBB */ \
781 "vshr.u8 d7, d7, #7 \n" /* A 0000000A */ \
782 "vneg.s8 d7, d7 \n" /* A AAAAAAAA upper 8 */ \
783 "vshl.u8 d6, d6, #3 \n" /* R RRRRR000 upper 5 */ \
784 "vshr.u8 q1, q3, #5 \n" /* R,A 00000RRR lower 3 */ \
785 "vshl.u8 q0, q2, #3 \n" /* B,G BBBBB000 upper 5 */ \
786 "vshr.u8 q2, q0, #5 \n" /* B,G 00000BBB lower 3 */ \
787 "vorr.u8 q1, q1, q3 \n" /* R,A */ \
788 "vorr.u8 q0, q0, q2 \n" /* B,G */
789
790 // RGB555TOARGB is same as ARGB1555TOARGB but ignores alpha.
791 #define RGB555TOARGB \
792 "vshrn.u16 d6, q0, #5 \n" /* G xxxGGGGG */ \
793 "vuzp.u8 d0, d1 \n" /* d0 xxxBBBBB xRRRRRxx */ \
794 "vshl.u8 d6, d6, #3 \n" /* G GGGGG000 upper 5 */ \
795 "vshr.u8 d1, d1, #2 \n" /* R 00xRRRRR lower 5 */ \
796 "vshl.u8 q0, q0, #3 \n" /* B,R BBBBB000 upper 5 */ \
797 "vshr.u8 q2, q0, #5 \n" /* B,R 00000BBB lower 3 */ \
798 "vorr.u8 d0, d0, d4 \n" /* B */ \
799 "vshr.u8 d4, d6, #5 \n" /* G 00000GGG lower 3 */ \
800 "vorr.u8 d2, d1, d5 \n" /* R */ \
801 "vorr.u8 d1, d4, d6 \n" /* G */
802
ARGB1555ToARGBRow_NEON(const uint8 * src_argb1555,uint8 * dst_argb,int width)803 void ARGB1555ToARGBRow_NEON(const uint8* src_argb1555,
804 uint8* dst_argb,
805 int width) {
806 asm volatile(
807 "vmov.u8 d3, #255 \n" // Alpha
808 "1: \n"
809 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels.
810 "subs %2, %2, #8 \n" // 8 processed per loop.
811 ARGB1555TOARGB
812 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB.
813 "bgt 1b \n"
814 : "+r"(src_argb1555), // %0
815 "+r"(dst_argb), // %1
816 "+r"(width) // %2
817 :
818 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List
819 );
820 }
821
822 #define ARGB4444TOARGB \
823 "vuzp.u8 d0, d1 \n" /* d0 BG, d1 RA */ \
824 "vshl.u8 q2, q0, #4 \n" /* B,R BBBB0000 */ \
825 "vshr.u8 q1, q0, #4 \n" /* G,A 0000GGGG */ \
826 "vshr.u8 q0, q2, #4 \n" /* B,R 0000BBBB */ \
827 "vorr.u8 q0, q0, q2 \n" /* B,R BBBBBBBB */ \
828 "vshl.u8 q2, q1, #4 \n" /* G,A GGGG0000 */ \
829 "vorr.u8 q1, q1, q2 \n" /* G,A GGGGGGGG */ \
830 "vswp.u8 d1, d2 \n" /* B,R,G,A -> B,G,R,A */
831
ARGB4444ToARGBRow_NEON(const uint8 * src_argb4444,uint8 * dst_argb,int width)832 void ARGB4444ToARGBRow_NEON(const uint8* src_argb4444,
833 uint8* dst_argb,
834 int width) {
835 asm volatile(
836 "vmov.u8 d3, #255 \n" // Alpha
837 "1: \n"
838 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels.
839 "subs %2, %2, #8 \n" // 8 processed per loop.
840 ARGB4444TOARGB
841 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB.
842 "bgt 1b \n"
843 : "+r"(src_argb4444), // %0
844 "+r"(dst_argb), // %1
845 "+r"(width) // %2
846 :
847 : "cc", "memory", "q0", "q1", "q2" // Clobber List
848 );
849 }
850
ARGBToRGB24Row_NEON(const uint8 * src_argb,uint8 * dst_rgb24,int width)851 void ARGBToRGB24Row_NEON(const uint8* src_argb, uint8* dst_rgb24, int width) {
852 asm volatile(
853 "1: \n"
854 "vld4.8 {d1, d2, d3, d4}, [%0]! \n" // load 8 pixels of ARGB.
855 "subs %2, %2, #8 \n" // 8 processed per loop.
856 "vst3.8 {d1, d2, d3}, [%1]! \n" // store 8 pixels of
857 // RGB24.
858 "bgt 1b \n"
859 : "+r"(src_argb), // %0
860 "+r"(dst_rgb24), // %1
861 "+r"(width) // %2
862 :
863 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List
864 );
865 }
866
ARGBToRAWRow_NEON(const uint8 * src_argb,uint8 * dst_raw,int width)867 void ARGBToRAWRow_NEON(const uint8* src_argb, uint8* dst_raw, int width) {
868 asm volatile(
869 "1: \n"
870 "vld4.8 {d1, d2, d3, d4}, [%0]! \n" // load 8 pixels of ARGB.
871 "subs %2, %2, #8 \n" // 8 processed per loop.
872 "vswp.u8 d1, d3 \n" // swap R, B
873 "vst3.8 {d1, d2, d3}, [%1]! \n" // store 8 pixels of RAW.
874 "bgt 1b \n"
875 : "+r"(src_argb), // %0
876 "+r"(dst_raw), // %1
877 "+r"(width) // %2
878 :
879 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List
880 );
881 }
882
YUY2ToYRow_NEON(const uint8 * src_yuy2,uint8 * dst_y,int width)883 void YUY2ToYRow_NEON(const uint8* src_yuy2, uint8* dst_y, int width) {
884 asm volatile(
885 "1: \n"
886 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pixels of YUY2.
887 "subs %2, %2, #16 \n" // 16 processed per loop.
888 "vst1.8 {q0}, [%1]! \n" // store 16 pixels of Y.
889 "bgt 1b \n"
890 : "+r"(src_yuy2), // %0
891 "+r"(dst_y), // %1
892 "+r"(width) // %2
893 :
894 : "cc", "memory", "q0", "q1" // Clobber List
895 );
896 }
897
UYVYToYRow_NEON(const uint8 * src_uyvy,uint8 * dst_y,int width)898 void UYVYToYRow_NEON(const uint8* src_uyvy, uint8* dst_y, int width) {
899 asm volatile(
900 "1: \n"
901 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pixels of UYVY.
902 "subs %2, %2, #16 \n" // 16 processed per loop.
903 "vst1.8 {q1}, [%1]! \n" // store 16 pixels of Y.
904 "bgt 1b \n"
905 : "+r"(src_uyvy), // %0
906 "+r"(dst_y), // %1
907 "+r"(width) // %2
908 :
909 : "cc", "memory", "q0", "q1" // Clobber List
910 );
911 }
912
YUY2ToUV422Row_NEON(const uint8 * src_yuy2,uint8 * dst_u,uint8 * dst_v,int width)913 void YUY2ToUV422Row_NEON(const uint8* src_yuy2,
914 uint8* dst_u,
915 uint8* dst_v,
916 int width) {
917 asm volatile(
918 "1: \n"
919 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of YUY2.
920 "subs %3, %3, #16 \n" // 16 pixels = 8 UVs.
921 "vst1.8 {d1}, [%1]! \n" // store 8 U.
922 "vst1.8 {d3}, [%2]! \n" // store 8 V.
923 "bgt 1b \n"
924 : "+r"(src_yuy2), // %0
925 "+r"(dst_u), // %1
926 "+r"(dst_v), // %2
927 "+r"(width) // %3
928 :
929 : "cc", "memory", "d0", "d1", "d2", "d3" // Clobber List
930 );
931 }
932
UYVYToUV422Row_NEON(const uint8 * src_uyvy,uint8 * dst_u,uint8 * dst_v,int width)933 void UYVYToUV422Row_NEON(const uint8* src_uyvy,
934 uint8* dst_u,
935 uint8* dst_v,
936 int width) {
937 asm volatile(
938 "1: \n"
939 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of UYVY.
940 "subs %3, %3, #16 \n" // 16 pixels = 8 UVs.
941 "vst1.8 {d0}, [%1]! \n" // store 8 U.
942 "vst1.8 {d2}, [%2]! \n" // store 8 V.
943 "bgt 1b \n"
944 : "+r"(src_uyvy), // %0
945 "+r"(dst_u), // %1
946 "+r"(dst_v), // %2
947 "+r"(width) // %3
948 :
949 : "cc", "memory", "d0", "d1", "d2", "d3" // Clobber List
950 );
951 }
952
YUY2ToUVRow_NEON(const uint8 * src_yuy2,int stride_yuy2,uint8 * dst_u,uint8 * dst_v,int width)953 void YUY2ToUVRow_NEON(const uint8* src_yuy2,
954 int stride_yuy2,
955 uint8* dst_u,
956 uint8* dst_v,
957 int width) {
958 asm volatile(
959 "add %1, %0, %1 \n" // stride + src_yuy2
960 "1: \n"
961 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of YUY2.
962 "subs %4, %4, #16 \n" // 16 pixels = 8 UVs.
963 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load next row YUY2.
964 "vrhadd.u8 d1, d1, d5 \n" // average rows of U
965 "vrhadd.u8 d3, d3, d7 \n" // average rows of V
966 "vst1.8 {d1}, [%2]! \n" // store 8 U.
967 "vst1.8 {d3}, [%3]! \n" // store 8 V.
968 "bgt 1b \n"
969 : "+r"(src_yuy2), // %0
970 "+r"(stride_yuy2), // %1
971 "+r"(dst_u), // %2
972 "+r"(dst_v), // %3
973 "+r"(width) // %4
974 :
975 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6",
976 "d7" // Clobber List
977 );
978 }
979
UYVYToUVRow_NEON(const uint8 * src_uyvy,int stride_uyvy,uint8 * dst_u,uint8 * dst_v,int width)980 void UYVYToUVRow_NEON(const uint8* src_uyvy,
981 int stride_uyvy,
982 uint8* dst_u,
983 uint8* dst_v,
984 int width) {
985 asm volatile(
986 "add %1, %0, %1 \n" // stride + src_uyvy
987 "1: \n"
988 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of UYVY.
989 "subs %4, %4, #16 \n" // 16 pixels = 8 UVs.
990 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load next row UYVY.
991 "vrhadd.u8 d0, d0, d4 \n" // average rows of U
992 "vrhadd.u8 d2, d2, d6 \n" // average rows of V
993 "vst1.8 {d0}, [%2]! \n" // store 8 U.
994 "vst1.8 {d2}, [%3]! \n" // store 8 V.
995 "bgt 1b \n"
996 : "+r"(src_uyvy), // %0
997 "+r"(stride_uyvy), // %1
998 "+r"(dst_u), // %2
999 "+r"(dst_v), // %3
1000 "+r"(width) // %4
1001 :
1002 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6",
1003 "d7" // Clobber List
1004 );
1005 }
1006
1007 // For BGRAToARGB, ABGRToARGB, RGBAToARGB, and ARGBToRGBA.
ARGBShuffleRow_NEON(const uint8 * src_argb,uint8 * dst_argb,const uint8 * shuffler,int width)1008 void ARGBShuffleRow_NEON(const uint8* src_argb,
1009 uint8* dst_argb,
1010 const uint8* shuffler,
1011 int width) {
1012 asm volatile(
1013 "vld1.8 {q2}, [%3] \n" // shuffler
1014 "1: \n"
1015 "vld1.8 {q0}, [%0]! \n" // load 4 pixels.
1016 "subs %2, %2, #4 \n" // 4 processed per loop
1017 "vtbl.8 d2, {d0, d1}, d4 \n" // look up 2 first pixels
1018 "vtbl.8 d3, {d0, d1}, d5 \n" // look up 2 next pixels
1019 "vst1.8 {q1}, [%1]! \n" // store 4.
1020 "bgt 1b \n"
1021 : "+r"(src_argb), // %0
1022 "+r"(dst_argb), // %1
1023 "+r"(width) // %2
1024 : "r"(shuffler) // %3
1025 : "cc", "memory", "q0", "q1", "q2" // Clobber List
1026 );
1027 }
1028
I422ToYUY2Row_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_yuy2,int width)1029 void I422ToYUY2Row_NEON(const uint8* src_y,
1030 const uint8* src_u,
1031 const uint8* src_v,
1032 uint8* dst_yuy2,
1033 int width) {
1034 asm volatile(
1035 "1: \n"
1036 "vld2.8 {d0, d2}, [%0]! \n" // load 16 Ys
1037 "vld1.8 {d1}, [%1]! \n" // load 8 Us
1038 "vld1.8 {d3}, [%2]! \n" // load 8 Vs
1039 "subs %4, %4, #16 \n" // 16 pixels
1040 "vst4.8 {d0, d1, d2, d3}, [%3]! \n" // Store 8 YUY2/16 pixels.
1041 "bgt 1b \n"
1042 : "+r"(src_y), // %0
1043 "+r"(src_u), // %1
1044 "+r"(src_v), // %2
1045 "+r"(dst_yuy2), // %3
1046 "+r"(width) // %4
1047 :
1048 : "cc", "memory", "d0", "d1", "d2", "d3");
1049 }
1050
I422ToUYVYRow_NEON(const uint8 * src_y,const uint8 * src_u,const uint8 * src_v,uint8 * dst_uyvy,int width)1051 void I422ToUYVYRow_NEON(const uint8* src_y,
1052 const uint8* src_u,
1053 const uint8* src_v,
1054 uint8* dst_uyvy,
1055 int width) {
1056 asm volatile(
1057 "1: \n"
1058 "vld2.8 {d1, d3}, [%0]! \n" // load 16 Ys
1059 "vld1.8 {d0}, [%1]! \n" // load 8 Us
1060 "vld1.8 {d2}, [%2]! \n" // load 8 Vs
1061 "subs %4, %4, #16 \n" // 16 pixels
1062 "vst4.8 {d0, d1, d2, d3}, [%3]! \n" // Store 8 UYVY/16 pixels.
1063 "bgt 1b \n"
1064 : "+r"(src_y), // %0
1065 "+r"(src_u), // %1
1066 "+r"(src_v), // %2
1067 "+r"(dst_uyvy), // %3
1068 "+r"(width) // %4
1069 :
1070 : "cc", "memory", "d0", "d1", "d2", "d3");
1071 }
1072
ARGBToRGB565Row_NEON(const uint8 * src_argb,uint8 * dst_rgb565,int width)1073 void ARGBToRGB565Row_NEON(const uint8* src_argb, uint8* dst_rgb565, int width) {
1074 asm volatile(
1075 "1: \n"
1076 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB.
1077 "subs %2, %2, #8 \n" // 8 processed per loop.
1078 ARGBTORGB565
1079 "vst1.8 {q0}, [%1]! \n" // store 8 pixels RGB565.
1080 "bgt 1b \n"
1081 : "+r"(src_argb), // %0
1082 "+r"(dst_rgb565), // %1
1083 "+r"(width) // %2
1084 :
1085 : "cc", "memory", "q0", "q8", "q9", "q10", "q11");
1086 }
1087
ARGBToRGB565DitherRow_NEON(const uint8 * src_argb,uint8 * dst_rgb,const uint32 dither4,int width)1088 void ARGBToRGB565DitherRow_NEON(const uint8* src_argb,
1089 uint8* dst_rgb,
1090 const uint32 dither4,
1091 int width) {
1092 asm volatile(
1093 "vdup.32 d2, %2 \n" // dither4
1094 "1: \n"
1095 "vld4.8 {d20, d21, d22, d23}, [%1]! \n" // load 8 pixels of ARGB.
1096 "subs %3, %3, #8 \n" // 8 processed per loop.
1097 "vqadd.u8 d20, d20, d2 \n"
1098 "vqadd.u8 d21, d21, d2 \n"
1099 "vqadd.u8 d22, d22, d2 \n" // add for dither
1100 ARGBTORGB565
1101 "vst1.8 {q0}, [%0]! \n" // store 8 RGB565.
1102 "bgt 1b \n"
1103 : "+r"(dst_rgb) // %0
1104 : "r"(src_argb), // %1
1105 "r"(dither4), // %2
1106 "r"(width) // %3
1107 : "cc", "memory", "q0", "q1", "q8", "q9", "q10", "q11");
1108 }
1109
ARGBToARGB1555Row_NEON(const uint8 * src_argb,uint8 * dst_argb1555,int width)1110 void ARGBToARGB1555Row_NEON(const uint8* src_argb,
1111 uint8* dst_argb1555,
1112 int width) {
1113 asm volatile(
1114 "1: \n"
1115 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB.
1116 "subs %2, %2, #8 \n" // 8 processed per loop.
1117 ARGBTOARGB1555
1118 "vst1.8 {q0}, [%1]! \n" // store 8 ARGB1555.
1119 "bgt 1b \n"
1120 : "+r"(src_argb), // %0
1121 "+r"(dst_argb1555), // %1
1122 "+r"(width) // %2
1123 :
1124 : "cc", "memory", "q0", "q8", "q9", "q10", "q11");
1125 }
1126
ARGBToARGB4444Row_NEON(const uint8 * src_argb,uint8 * dst_argb4444,int width)1127 void ARGBToARGB4444Row_NEON(const uint8* src_argb,
1128 uint8* dst_argb4444,
1129 int width) {
1130 asm volatile(
1131 "vmov.u8 d4, #0x0f \n" // bits to clear with
1132 // vbic.
1133 "1: \n"
1134 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB.
1135 "subs %2, %2, #8 \n" // 8 processed per loop.
1136 ARGBTOARGB4444
1137 "vst1.8 {q0}, [%1]! \n" // store 8 ARGB4444.
1138 "bgt 1b \n"
1139 : "+r"(src_argb), // %0
1140 "+r"(dst_argb4444), // %1
1141 "+r"(width) // %2
1142 :
1143 : "cc", "memory", "q0", "q8", "q9", "q10", "q11");
1144 }
1145
ARGBToYRow_NEON(const uint8 * src_argb,uint8 * dst_y,int width)1146 void ARGBToYRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
1147 asm volatile(
1148 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient
1149 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient
1150 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient
1151 "vmov.u8 d27, #16 \n" // Add 16 constant
1152 "1: \n"
1153 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
1154 "subs %2, %2, #8 \n" // 8 processed per loop.
1155 "vmull.u8 q2, d0, d24 \n" // B
1156 "vmlal.u8 q2, d1, d25 \n" // G
1157 "vmlal.u8 q2, d2, d26 \n" // R
1158 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y
1159 "vqadd.u8 d0, d27 \n"
1160 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1161 "bgt 1b \n"
1162 : "+r"(src_argb), // %0
1163 "+r"(dst_y), // %1
1164 "+r"(width) // %2
1165 :
1166 : "cc", "memory", "q0", "q1", "q2", "q12", "q13");
1167 }
1168
ARGBExtractAlphaRow_NEON(const uint8 * src_argb,uint8 * dst_a,int width)1169 void ARGBExtractAlphaRow_NEON(const uint8* src_argb, uint8* dst_a, int width) {
1170 asm volatile(
1171 "1: \n"
1172 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels
1173 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels
1174 "subs %2, %2, #16 \n" // 16 processed per loop
1175 "vst1.8 {q3}, [%1]! \n" // store 16 A's.
1176 "bgt 1b \n"
1177 : "+r"(src_argb), // %0
1178 "+r"(dst_a), // %1
1179 "+r"(width) // %2
1180 :
1181 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List
1182 );
1183 }
1184
ARGBToYJRow_NEON(const uint8 * src_argb,uint8 * dst_y,int width)1185 void ARGBToYJRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
1186 asm volatile(
1187 "vmov.u8 d24, #15 \n" // B * 0.11400 coefficient
1188 "vmov.u8 d25, #75 \n" // G * 0.58700 coefficient
1189 "vmov.u8 d26, #38 \n" // R * 0.29900 coefficient
1190 "1: \n"
1191 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
1192 "subs %2, %2, #8 \n" // 8 processed per loop.
1193 "vmull.u8 q2, d0, d24 \n" // B
1194 "vmlal.u8 q2, d1, d25 \n" // G
1195 "vmlal.u8 q2, d2, d26 \n" // R
1196 "vqrshrun.s16 d0, q2, #7 \n" // 15 bit to 8 bit Y
1197 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1198 "bgt 1b \n"
1199 : "+r"(src_argb), // %0
1200 "+r"(dst_y), // %1
1201 "+r"(width) // %2
1202 :
1203 : "cc", "memory", "q0", "q1", "q2", "q12", "q13");
1204 }
1205
1206 // 8x1 pixels.
ARGBToUV444Row_NEON(const uint8 * src_argb,uint8 * dst_u,uint8 * dst_v,int width)1207 void ARGBToUV444Row_NEON(const uint8* src_argb,
1208 uint8* dst_u,
1209 uint8* dst_v,
1210 int width) {
1211 asm volatile(
1212 "vmov.u8 d24, #112 \n" // UB / VR 0.875
1213 // coefficient
1214 "vmov.u8 d25, #74 \n" // UG -0.5781 coefficient
1215 "vmov.u8 d26, #38 \n" // UR -0.2969 coefficient
1216 "vmov.u8 d27, #18 \n" // VB -0.1406 coefficient
1217 "vmov.u8 d28, #94 \n" // VG -0.7344 coefficient
1218 "vmov.u16 q15, #0x8080 \n" // 128.5
1219 "1: \n"
1220 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
1221 "subs %3, %3, #8 \n" // 8 processed per loop.
1222 "vmull.u8 q2, d0, d24 \n" // B
1223 "vmlsl.u8 q2, d1, d25 \n" // G
1224 "vmlsl.u8 q2, d2, d26 \n" // R
1225 "vadd.u16 q2, q2, q15 \n" // +128 -> unsigned
1226
1227 "vmull.u8 q3, d2, d24 \n" // R
1228 "vmlsl.u8 q3, d1, d28 \n" // G
1229 "vmlsl.u8 q3, d0, d27 \n" // B
1230 "vadd.u16 q3, q3, q15 \n" // +128 -> unsigned
1231
1232 "vqshrn.u16 d0, q2, #8 \n" // 16 bit to 8 bit U
1233 "vqshrn.u16 d1, q3, #8 \n" // 16 bit to 8 bit V
1234
1235 "vst1.8 {d0}, [%1]! \n" // store 8 pixels U.
1236 "vst1.8 {d1}, [%2]! \n" // store 8 pixels V.
1237 "bgt 1b \n"
1238 : "+r"(src_argb), // %0
1239 "+r"(dst_u), // %1
1240 "+r"(dst_v), // %2
1241 "+r"(width) // %3
1242 :
1243 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q12", "q13", "q14",
1244 "q15");
1245 }
1246
1247 // clang-format off
1248 // 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
1249 #define RGBTOUV(QB, QG, QR) \
1250 "vmul.s16 q8, " #QB ", q10 \n" /* B */ \
1251 "vmls.s16 q8, " #QG ", q11 \n" /* G */ \
1252 "vmls.s16 q8, " #QR ", q12 \n" /* R */ \
1253 "vadd.u16 q8, q8, q15 \n" /* +128 -> unsigned */ \
1254 "vmul.s16 q9, " #QR ", q10 \n" /* R */ \
1255 "vmls.s16 q9, " #QG ", q14 \n" /* G */ \
1256 "vmls.s16 q9, " #QB ", q13 \n" /* B */ \
1257 "vadd.u16 q9, q9, q15 \n" /* +128 -> unsigned */ \
1258 "vqshrn.u16 d0, q8, #8 \n" /* 16 bit to 8 bit U */ \
1259 "vqshrn.u16 d1, q9, #8 \n" /* 16 bit to 8 bit V */
1260 // clang-format on
1261
1262 // TODO(fbarchard): Consider vhadd vertical, then vpaddl horizontal, avoid shr.
ARGBToUVRow_NEON(const uint8 * src_argb,int src_stride_argb,uint8 * dst_u,uint8 * dst_v,int width)1263 void ARGBToUVRow_NEON(const uint8* src_argb,
1264 int src_stride_argb,
1265 uint8* dst_u,
1266 uint8* dst_v,
1267 int width) {
1268 asm volatile (
1269 "add %1, %0, %1 \n" // src_stride + src_argb
1270 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1271 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1272 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1273 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1274 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1275 "vmov.u16 q15, #0x8080 \n" // 128.5
1276 "1: \n"
1277 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
1278 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels.
1279 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
1280 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1281 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
1282 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ARGB pixels.
1283 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ARGB pixels.
1284 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts.
1285 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts.
1286 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts.
1287
1288 "vrshr.u16 q0, q0, #1 \n" // 2x average
1289 "vrshr.u16 q1, q1, #1 \n"
1290 "vrshr.u16 q2, q2, #1 \n"
1291
1292 "subs %4, %4, #16 \n" // 32 processed per loop.
1293 RGBTOUV(q0, q1, q2)
1294 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1295 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1296 "bgt 1b \n"
1297 : "+r"(src_argb), // %0
1298 "+r"(src_stride_argb), // %1
1299 "+r"(dst_u), // %2
1300 "+r"(dst_v), // %3
1301 "+r"(width) // %4
1302 :
1303 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1304 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1305 );
1306 }
1307
1308 // TODO(fbarchard): Subsample match C code.
ARGBToUVJRow_NEON(const uint8 * src_argb,int src_stride_argb,uint8 * dst_u,uint8 * dst_v,int width)1309 void ARGBToUVJRow_NEON(const uint8* src_argb,
1310 int src_stride_argb,
1311 uint8* dst_u,
1312 uint8* dst_v,
1313 int width) {
1314 asm volatile (
1315 "add %1, %0, %1 \n" // src_stride + src_argb
1316 "vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient
1317 "vmov.s16 q11, #84 / 2 \n" // UG -0.33126 coefficient
1318 "vmov.s16 q12, #43 / 2 \n" // UR -0.16874 coefficient
1319 "vmov.s16 q13, #20 / 2 \n" // VB -0.08131 coefficient
1320 "vmov.s16 q14, #107 / 2 \n" // VG -0.41869 coefficient
1321 "vmov.u16 q15, #0x8080 \n" // 128.5
1322 "1: \n"
1323 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
1324 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels.
1325 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
1326 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1327 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
1328 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ARGB pixels.
1329 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ARGB pixels.
1330 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts.
1331 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts.
1332 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts.
1333
1334 "vrshr.u16 q0, q0, #1 \n" // 2x average
1335 "vrshr.u16 q1, q1, #1 \n"
1336 "vrshr.u16 q2, q2, #1 \n"
1337
1338 "subs %4, %4, #16 \n" // 32 processed per loop.
1339 RGBTOUV(q0, q1, q2)
1340 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1341 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1342 "bgt 1b \n"
1343 : "+r"(src_argb), // %0
1344 "+r"(src_stride_argb), // %1
1345 "+r"(dst_u), // %2
1346 "+r"(dst_v), // %3
1347 "+r"(width) // %4
1348 :
1349 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1350 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1351 );
1352 }
1353
BGRAToUVRow_NEON(const uint8 * src_bgra,int src_stride_bgra,uint8 * dst_u,uint8 * dst_v,int width)1354 void BGRAToUVRow_NEON(const uint8* src_bgra,
1355 int src_stride_bgra,
1356 uint8* dst_u,
1357 uint8* dst_v,
1358 int width) {
1359 asm volatile (
1360 "add %1, %0, %1 \n" // src_stride + src_bgra
1361 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1362 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1363 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1364 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1365 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1366 "vmov.u16 q15, #0x8080 \n" // 128.5
1367 "1: \n"
1368 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 BGRA pixels.
1369 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 BGRA pixels.
1370 "vpaddl.u8 q3, q3 \n" // B 16 bytes -> 8 shorts.
1371 "vpaddl.u8 q2, q2 \n" // G 16 bytes -> 8 shorts.
1372 "vpaddl.u8 q1, q1 \n" // R 16 bytes -> 8 shorts.
1373 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more BGRA pixels.
1374 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 BGRA pixels.
1375 "vpadal.u8 q3, q7 \n" // B 16 bytes -> 8 shorts.
1376 "vpadal.u8 q2, q6 \n" // G 16 bytes -> 8 shorts.
1377 "vpadal.u8 q1, q5 \n" // R 16 bytes -> 8 shorts.
1378
1379 "vrshr.u16 q1, q1, #1 \n" // 2x average
1380 "vrshr.u16 q2, q2, #1 \n"
1381 "vrshr.u16 q3, q3, #1 \n"
1382
1383 "subs %4, %4, #16 \n" // 32 processed per loop.
1384 RGBTOUV(q3, q2, q1)
1385 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1386 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1387 "bgt 1b \n"
1388 : "+r"(src_bgra), // %0
1389 "+r"(src_stride_bgra), // %1
1390 "+r"(dst_u), // %2
1391 "+r"(dst_v), // %3
1392 "+r"(width) // %4
1393 :
1394 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1395 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1396 );
1397 }
1398
ABGRToUVRow_NEON(const uint8 * src_abgr,int src_stride_abgr,uint8 * dst_u,uint8 * dst_v,int width)1399 void ABGRToUVRow_NEON(const uint8* src_abgr,
1400 int src_stride_abgr,
1401 uint8* dst_u,
1402 uint8* dst_v,
1403 int width) {
1404 asm volatile (
1405 "add %1, %0, %1 \n" // src_stride + src_abgr
1406 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1407 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1408 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1409 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1410 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1411 "vmov.u16 q15, #0x8080 \n" // 128.5
1412 "1: \n"
1413 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ABGR pixels.
1414 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ABGR pixels.
1415 "vpaddl.u8 q2, q2 \n" // B 16 bytes -> 8 shorts.
1416 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1417 "vpaddl.u8 q0, q0 \n" // R 16 bytes -> 8 shorts.
1418 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ABGR pixels.
1419 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ABGR pixels.
1420 "vpadal.u8 q2, q6 \n" // B 16 bytes -> 8 shorts.
1421 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts.
1422 "vpadal.u8 q0, q4 \n" // R 16 bytes -> 8 shorts.
1423
1424 "vrshr.u16 q0, q0, #1 \n" // 2x average
1425 "vrshr.u16 q1, q1, #1 \n"
1426 "vrshr.u16 q2, q2, #1 \n"
1427
1428 "subs %4, %4, #16 \n" // 32 processed per loop.
1429 RGBTOUV(q2, q1, q0)
1430 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1431 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1432 "bgt 1b \n"
1433 : "+r"(src_abgr), // %0
1434 "+r"(src_stride_abgr), // %1
1435 "+r"(dst_u), // %2
1436 "+r"(dst_v), // %3
1437 "+r"(width) // %4
1438 :
1439 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1440 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1441 );
1442 }
1443
RGBAToUVRow_NEON(const uint8 * src_rgba,int src_stride_rgba,uint8 * dst_u,uint8 * dst_v,int width)1444 void RGBAToUVRow_NEON(const uint8* src_rgba,
1445 int src_stride_rgba,
1446 uint8* dst_u,
1447 uint8* dst_v,
1448 int width) {
1449 asm volatile (
1450 "add %1, %0, %1 \n" // src_stride + src_rgba
1451 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1452 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1453 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1454 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1455 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1456 "vmov.u16 q15, #0x8080 \n" // 128.5
1457 "1: \n"
1458 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 RGBA pixels.
1459 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 RGBA pixels.
1460 "vpaddl.u8 q0, q1 \n" // B 16 bytes -> 8 shorts.
1461 "vpaddl.u8 q1, q2 \n" // G 16 bytes -> 8 shorts.
1462 "vpaddl.u8 q2, q3 \n" // R 16 bytes -> 8 shorts.
1463 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more RGBA pixels.
1464 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 RGBA pixels.
1465 "vpadal.u8 q0, q5 \n" // B 16 bytes -> 8 shorts.
1466 "vpadal.u8 q1, q6 \n" // G 16 bytes -> 8 shorts.
1467 "vpadal.u8 q2, q7 \n" // R 16 bytes -> 8 shorts.
1468
1469 "vrshr.u16 q0, q0, #1 \n" // 2x average
1470 "vrshr.u16 q1, q1, #1 \n"
1471 "vrshr.u16 q2, q2, #1 \n"
1472
1473 "subs %4, %4, #16 \n" // 32 processed per loop.
1474 RGBTOUV(q0, q1, q2)
1475 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1476 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1477 "bgt 1b \n"
1478 : "+r"(src_rgba), // %0
1479 "+r"(src_stride_rgba), // %1
1480 "+r"(dst_u), // %2
1481 "+r"(dst_v), // %3
1482 "+r"(width) // %4
1483 :
1484 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1485 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1486 );
1487 }
1488
RGB24ToUVRow_NEON(const uint8 * src_rgb24,int src_stride_rgb24,uint8 * dst_u,uint8 * dst_v,int width)1489 void RGB24ToUVRow_NEON(const uint8* src_rgb24,
1490 int src_stride_rgb24,
1491 uint8* dst_u,
1492 uint8* dst_v,
1493 int width) {
1494 asm volatile (
1495 "add %1, %0, %1 \n" // src_stride + src_rgb24
1496 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1497 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1498 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1499 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1500 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1501 "vmov.u16 q15, #0x8080 \n" // 128.5
1502 "1: \n"
1503 "vld3.8 {d0, d2, d4}, [%0]! \n" // load 8 RGB24 pixels.
1504 "vld3.8 {d1, d3, d5}, [%0]! \n" // load next 8 RGB24 pixels.
1505 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
1506 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1507 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
1508 "vld3.8 {d8, d10, d12}, [%1]! \n" // load 8 more RGB24 pixels.
1509 "vld3.8 {d9, d11, d13}, [%1]! \n" // load last 8 RGB24 pixels.
1510 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts.
1511 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts.
1512 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts.
1513
1514 "vrshr.u16 q0, q0, #1 \n" // 2x average
1515 "vrshr.u16 q1, q1, #1 \n"
1516 "vrshr.u16 q2, q2, #1 \n"
1517
1518 "subs %4, %4, #16 \n" // 32 processed per loop.
1519 RGBTOUV(q0, q1, q2)
1520 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1521 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1522 "bgt 1b \n"
1523 : "+r"(src_rgb24), // %0
1524 "+r"(src_stride_rgb24), // %1
1525 "+r"(dst_u), // %2
1526 "+r"(dst_v), // %3
1527 "+r"(width) // %4
1528 :
1529 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1530 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1531 );
1532 }
1533
RAWToUVRow_NEON(const uint8 * src_raw,int src_stride_raw,uint8 * dst_u,uint8 * dst_v,int width)1534 void RAWToUVRow_NEON(const uint8* src_raw,
1535 int src_stride_raw,
1536 uint8* dst_u,
1537 uint8* dst_v,
1538 int width) {
1539 asm volatile (
1540 "add %1, %0, %1 \n" // src_stride + src_raw
1541 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient
1542 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1543 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1544 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1545 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1546 "vmov.u16 q15, #0x8080 \n" // 128.5
1547 "1: \n"
1548 "vld3.8 {d0, d2, d4}, [%0]! \n" // load 8 RAW pixels.
1549 "vld3.8 {d1, d3, d5}, [%0]! \n" // load next 8 RAW pixels.
1550 "vpaddl.u8 q2, q2 \n" // B 16 bytes -> 8 shorts.
1551 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1552 "vpaddl.u8 q0, q0 \n" // R 16 bytes -> 8 shorts.
1553 "vld3.8 {d8, d10, d12}, [%1]! \n" // load 8 more RAW pixels.
1554 "vld3.8 {d9, d11, d13}, [%1]! \n" // load last 8 RAW pixels.
1555 "vpadal.u8 q2, q6 \n" // B 16 bytes -> 8 shorts.
1556 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts.
1557 "vpadal.u8 q0, q4 \n" // R 16 bytes -> 8 shorts.
1558
1559 "vrshr.u16 q0, q0, #1 \n" // 2x average
1560 "vrshr.u16 q1, q1, #1 \n"
1561 "vrshr.u16 q2, q2, #1 \n"
1562
1563 "subs %4, %4, #16 \n" // 32 processed per loop.
1564 RGBTOUV(q2, q1, q0)
1565 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1566 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1567 "bgt 1b \n"
1568 : "+r"(src_raw), // %0
1569 "+r"(src_stride_raw), // %1
1570 "+r"(dst_u), // %2
1571 "+r"(dst_v), // %3
1572 "+r"(width) // %4
1573 :
1574 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
1575 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
1576 );
1577 }
1578
1579 // 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
RGB565ToUVRow_NEON(const uint8 * src_rgb565,int src_stride_rgb565,uint8 * dst_u,uint8 * dst_v,int width)1580 void RGB565ToUVRow_NEON(const uint8* src_rgb565,
1581 int src_stride_rgb565,
1582 uint8* dst_u,
1583 uint8* dst_v,
1584 int width) {
1585 asm volatile(
1586 "add %1, %0, %1 \n" // src_stride + src_argb
1587 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875
1588 // coefficient
1589 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1590 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1591 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1592 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1593 "vmov.u16 q15, #0x8080 \n" // 128.5
1594 "1: \n"
1595 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels.
1596 RGB565TOARGB
1597 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1598 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1599 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1600 "vld1.8 {q0}, [%0]! \n" // next 8 RGB565 pixels.
1601 RGB565TOARGB
1602 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1603 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1604 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1605
1606 "vld1.8 {q0}, [%1]! \n" // load 8 RGB565 pixels.
1607 RGB565TOARGB
1608 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1609 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1610 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1611 "vld1.8 {q0}, [%1]! \n" // next 8 RGB565 pixels.
1612 RGB565TOARGB
1613 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1614 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1615 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1616
1617 "vrshr.u16 q4, q4, #1 \n" // 2x average
1618 "vrshr.u16 q5, q5, #1 \n"
1619 "vrshr.u16 q6, q6, #1 \n"
1620
1621 "subs %4, %4, #16 \n" // 16 processed per loop.
1622 "vmul.s16 q8, q4, q10 \n" // B
1623 "vmls.s16 q8, q5, q11 \n" // G
1624 "vmls.s16 q8, q6, q12 \n" // R
1625 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned
1626 "vmul.s16 q9, q6, q10 \n" // R
1627 "vmls.s16 q9, q5, q14 \n" // G
1628 "vmls.s16 q9, q4, q13 \n" // B
1629 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned
1630 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U
1631 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V
1632 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1633 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1634 "bgt 1b \n"
1635 : "+r"(src_rgb565), // %0
1636 "+r"(src_stride_rgb565), // %1
1637 "+r"(dst_u), // %2
1638 "+r"(dst_v), // %3
1639 "+r"(width) // %4
1640 :
1641 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
1642 "q9", "q10", "q11", "q12", "q13", "q14", "q15");
1643 }
1644
1645 // 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
ARGB1555ToUVRow_NEON(const uint8 * src_argb1555,int src_stride_argb1555,uint8 * dst_u,uint8 * dst_v,int width)1646 void ARGB1555ToUVRow_NEON(const uint8* src_argb1555,
1647 int src_stride_argb1555,
1648 uint8* dst_u,
1649 uint8* dst_v,
1650 int width) {
1651 asm volatile(
1652 "add %1, %0, %1 \n" // src_stride + src_argb
1653 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875
1654 // coefficient
1655 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1656 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1657 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1658 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1659 "vmov.u16 q15, #0x8080 \n" // 128.5
1660 "1: \n"
1661 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels.
1662 RGB555TOARGB
1663 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1664 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1665 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1666 "vld1.8 {q0}, [%0]! \n" // next 8 ARGB1555 pixels.
1667 RGB555TOARGB
1668 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1669 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1670 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1671
1672 "vld1.8 {q0}, [%1]! \n" // load 8 ARGB1555 pixels.
1673 RGB555TOARGB
1674 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1675 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1676 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1677 "vld1.8 {q0}, [%1]! \n" // next 8 ARGB1555 pixels.
1678 RGB555TOARGB
1679 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1680 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1681 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1682
1683 "vrshr.u16 q4, q4, #1 \n" // 2x average
1684 "vrshr.u16 q5, q5, #1 \n"
1685 "vrshr.u16 q6, q6, #1 \n"
1686
1687 "subs %4, %4, #16 \n" // 16 processed per loop.
1688 "vmul.s16 q8, q4, q10 \n" // B
1689 "vmls.s16 q8, q5, q11 \n" // G
1690 "vmls.s16 q8, q6, q12 \n" // R
1691 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned
1692 "vmul.s16 q9, q6, q10 \n" // R
1693 "vmls.s16 q9, q5, q14 \n" // G
1694 "vmls.s16 q9, q4, q13 \n" // B
1695 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned
1696 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U
1697 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V
1698 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1699 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1700 "bgt 1b \n"
1701 : "+r"(src_argb1555), // %0
1702 "+r"(src_stride_argb1555), // %1
1703 "+r"(dst_u), // %2
1704 "+r"(dst_v), // %3
1705 "+r"(width) // %4
1706 :
1707 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
1708 "q9", "q10", "q11", "q12", "q13", "q14", "q15");
1709 }
1710
1711 // 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
ARGB4444ToUVRow_NEON(const uint8 * src_argb4444,int src_stride_argb4444,uint8 * dst_u,uint8 * dst_v,int width)1712 void ARGB4444ToUVRow_NEON(const uint8* src_argb4444,
1713 int src_stride_argb4444,
1714 uint8* dst_u,
1715 uint8* dst_v,
1716 int width) {
1717 asm volatile(
1718 "add %1, %0, %1 \n" // src_stride + src_argb
1719 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875
1720 // coefficient
1721 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient
1722 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient
1723 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient
1724 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient
1725 "vmov.u16 q15, #0x8080 \n" // 128.5
1726 "1: \n"
1727 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels.
1728 ARGB4444TOARGB
1729 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1730 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1731 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1732 "vld1.8 {q0}, [%0]! \n" // next 8 ARGB4444 pixels.
1733 ARGB4444TOARGB
1734 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1735 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1736 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1737
1738 "vld1.8 {q0}, [%1]! \n" // load 8 ARGB4444 pixels.
1739 ARGB4444TOARGB
1740 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts.
1741 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts.
1742 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts.
1743 "vld1.8 {q0}, [%1]! \n" // next 8 ARGB4444 pixels.
1744 ARGB4444TOARGB
1745 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts.
1746 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts.
1747 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts.
1748
1749 "vrshr.u16 q4, q4, #1 \n" // 2x average
1750 "vrshr.u16 q5, q5, #1 \n"
1751 "vrshr.u16 q6, q6, #1 \n"
1752
1753 "subs %4, %4, #16 \n" // 16 processed per loop.
1754 "vmul.s16 q8, q4, q10 \n" // B
1755 "vmls.s16 q8, q5, q11 \n" // G
1756 "vmls.s16 q8, q6, q12 \n" // R
1757 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned
1758 "vmul.s16 q9, q6, q10 \n" // R
1759 "vmls.s16 q9, q5, q14 \n" // G
1760 "vmls.s16 q9, q4, q13 \n" // B
1761 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned
1762 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U
1763 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V
1764 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U.
1765 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V.
1766 "bgt 1b \n"
1767 : "+r"(src_argb4444), // %0
1768 "+r"(src_stride_argb4444), // %1
1769 "+r"(dst_u), // %2
1770 "+r"(dst_v), // %3
1771 "+r"(width) // %4
1772 :
1773 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
1774 "q9", "q10", "q11", "q12", "q13", "q14", "q15");
1775 }
1776
RGB565ToYRow_NEON(const uint8 * src_rgb565,uint8 * dst_y,int width)1777 void RGB565ToYRow_NEON(const uint8* src_rgb565, uint8* dst_y, int width) {
1778 asm volatile(
1779 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient
1780 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient
1781 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient
1782 "vmov.u8 d27, #16 \n" // Add 16 constant
1783 "1: \n"
1784 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels.
1785 "subs %2, %2, #8 \n" // 8 processed per loop.
1786 RGB565TOARGB
1787 "vmull.u8 q2, d0, d24 \n" // B
1788 "vmlal.u8 q2, d1, d25 \n" // G
1789 "vmlal.u8 q2, d2, d26 \n" // R
1790 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y
1791 "vqadd.u8 d0, d27 \n"
1792 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1793 "bgt 1b \n"
1794 : "+r"(src_rgb565), // %0
1795 "+r"(dst_y), // %1
1796 "+r"(width) // %2
1797 :
1798 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13");
1799 }
1800
ARGB1555ToYRow_NEON(const uint8 * src_argb1555,uint8 * dst_y,int width)1801 void ARGB1555ToYRow_NEON(const uint8* src_argb1555, uint8* dst_y, int width) {
1802 asm volatile(
1803 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient
1804 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient
1805 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient
1806 "vmov.u8 d27, #16 \n" // Add 16 constant
1807 "1: \n"
1808 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels.
1809 "subs %2, %2, #8 \n" // 8 processed per loop.
1810 ARGB1555TOARGB
1811 "vmull.u8 q2, d0, d24 \n" // B
1812 "vmlal.u8 q2, d1, d25 \n" // G
1813 "vmlal.u8 q2, d2, d26 \n" // R
1814 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y
1815 "vqadd.u8 d0, d27 \n"
1816 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1817 "bgt 1b \n"
1818 : "+r"(src_argb1555), // %0
1819 "+r"(dst_y), // %1
1820 "+r"(width) // %2
1821 :
1822 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13");
1823 }
1824
ARGB4444ToYRow_NEON(const uint8 * src_argb4444,uint8 * dst_y,int width)1825 void ARGB4444ToYRow_NEON(const uint8* src_argb4444, uint8* dst_y, int width) {
1826 asm volatile(
1827 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient
1828 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient
1829 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient
1830 "vmov.u8 d27, #16 \n" // Add 16 constant
1831 "1: \n"
1832 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels.
1833 "subs %2, %2, #8 \n" // 8 processed per loop.
1834 ARGB4444TOARGB
1835 "vmull.u8 q2, d0, d24 \n" // B
1836 "vmlal.u8 q2, d1, d25 \n" // G
1837 "vmlal.u8 q2, d2, d26 \n" // R
1838 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y
1839 "vqadd.u8 d0, d27 \n"
1840 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1841 "bgt 1b \n"
1842 : "+r"(src_argb4444), // %0
1843 "+r"(dst_y), // %1
1844 "+r"(width) // %2
1845 :
1846 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13");
1847 }
1848
BGRAToYRow_NEON(const uint8 * src_bgra,uint8 * dst_y,int width)1849 void BGRAToYRow_NEON(const uint8* src_bgra, uint8* dst_y, int width) {
1850 asm volatile(
1851 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient
1852 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient
1853 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient
1854 "vmov.u8 d7, #16 \n" // Add 16 constant
1855 "1: \n"
1856 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of BGRA.
1857 "subs %2, %2, #8 \n" // 8 processed per loop.
1858 "vmull.u8 q8, d1, d4 \n" // R
1859 "vmlal.u8 q8, d2, d5 \n" // G
1860 "vmlal.u8 q8, d3, d6 \n" // B
1861 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y
1862 "vqadd.u8 d0, d7 \n"
1863 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1864 "bgt 1b \n"
1865 : "+r"(src_bgra), // %0
1866 "+r"(dst_y), // %1
1867 "+r"(width) // %2
1868 :
1869 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8");
1870 }
1871
ABGRToYRow_NEON(const uint8 * src_abgr,uint8 * dst_y,int width)1872 void ABGRToYRow_NEON(const uint8* src_abgr, uint8* dst_y, int width) {
1873 asm volatile(
1874 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient
1875 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient
1876 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient
1877 "vmov.u8 d7, #16 \n" // Add 16 constant
1878 "1: \n"
1879 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ABGR.
1880 "subs %2, %2, #8 \n" // 8 processed per loop.
1881 "vmull.u8 q8, d0, d4 \n" // R
1882 "vmlal.u8 q8, d1, d5 \n" // G
1883 "vmlal.u8 q8, d2, d6 \n" // B
1884 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y
1885 "vqadd.u8 d0, d7 \n"
1886 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1887 "bgt 1b \n"
1888 : "+r"(src_abgr), // %0
1889 "+r"(dst_y), // %1
1890 "+r"(width) // %2
1891 :
1892 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8");
1893 }
1894
RGBAToYRow_NEON(const uint8 * src_rgba,uint8 * dst_y,int width)1895 void RGBAToYRow_NEON(const uint8* src_rgba, uint8* dst_y, int width) {
1896 asm volatile(
1897 "vmov.u8 d4, #13 \n" // B * 0.1016 coefficient
1898 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient
1899 "vmov.u8 d6, #33 \n" // R * 0.2578 coefficient
1900 "vmov.u8 d7, #16 \n" // Add 16 constant
1901 "1: \n"
1902 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of RGBA.
1903 "subs %2, %2, #8 \n" // 8 processed per loop.
1904 "vmull.u8 q8, d1, d4 \n" // B
1905 "vmlal.u8 q8, d2, d5 \n" // G
1906 "vmlal.u8 q8, d3, d6 \n" // R
1907 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y
1908 "vqadd.u8 d0, d7 \n"
1909 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1910 "bgt 1b \n"
1911 : "+r"(src_rgba), // %0
1912 "+r"(dst_y), // %1
1913 "+r"(width) // %2
1914 :
1915 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8");
1916 }
1917
RGB24ToYRow_NEON(const uint8 * src_rgb24,uint8 * dst_y,int width)1918 void RGB24ToYRow_NEON(const uint8* src_rgb24, uint8* dst_y, int width) {
1919 asm volatile(
1920 "vmov.u8 d4, #13 \n" // B * 0.1016 coefficient
1921 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient
1922 "vmov.u8 d6, #33 \n" // R * 0.2578 coefficient
1923 "vmov.u8 d7, #16 \n" // Add 16 constant
1924 "1: \n"
1925 "vld3.8 {d0, d1, d2}, [%0]! \n" // load 8 pixels of RGB24.
1926 "subs %2, %2, #8 \n" // 8 processed per loop.
1927 "vmull.u8 q8, d0, d4 \n" // B
1928 "vmlal.u8 q8, d1, d5 \n" // G
1929 "vmlal.u8 q8, d2, d6 \n" // R
1930 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y
1931 "vqadd.u8 d0, d7 \n"
1932 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1933 "bgt 1b \n"
1934 : "+r"(src_rgb24), // %0
1935 "+r"(dst_y), // %1
1936 "+r"(width) // %2
1937 :
1938 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8");
1939 }
1940
RAWToYRow_NEON(const uint8 * src_raw,uint8 * dst_y,int width)1941 void RAWToYRow_NEON(const uint8* src_raw, uint8* dst_y, int width) {
1942 asm volatile(
1943 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient
1944 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient
1945 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient
1946 "vmov.u8 d7, #16 \n" // Add 16 constant
1947 "1: \n"
1948 "vld3.8 {d0, d1, d2}, [%0]! \n" // load 8 pixels of RAW.
1949 "subs %2, %2, #8 \n" // 8 processed per loop.
1950 "vmull.u8 q8, d0, d4 \n" // B
1951 "vmlal.u8 q8, d1, d5 \n" // G
1952 "vmlal.u8 q8, d2, d6 \n" // R
1953 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y
1954 "vqadd.u8 d0, d7 \n"
1955 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y.
1956 "bgt 1b \n"
1957 : "+r"(src_raw), // %0
1958 "+r"(dst_y), // %1
1959 "+r"(width) // %2
1960 :
1961 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8");
1962 }
1963
1964 // Bilinear filter 16x2 -> 16x1
InterpolateRow_NEON(uint8 * dst_ptr,const uint8 * src_ptr,ptrdiff_t src_stride,int dst_width,int source_y_fraction)1965 void InterpolateRow_NEON(uint8* dst_ptr,
1966 const uint8* src_ptr,
1967 ptrdiff_t src_stride,
1968 int dst_width,
1969 int source_y_fraction) {
1970 int y1_fraction = source_y_fraction;
1971 asm volatile(
1972 "cmp %4, #0 \n"
1973 "beq 100f \n"
1974 "add %2, %1 \n"
1975 "cmp %4, #128 \n"
1976 "beq 50f \n"
1977
1978 "vdup.8 d5, %4 \n"
1979 "rsb %4, #256 \n"
1980 "vdup.8 d4, %4 \n"
1981 // General purpose row blend.
1982 "1: \n"
1983 "vld1.8 {q0}, [%1]! \n"
1984 "vld1.8 {q1}, [%2]! \n"
1985 "subs %3, %3, #16 \n"
1986 "vmull.u8 q13, d0, d4 \n"
1987 "vmull.u8 q14, d1, d4 \n"
1988 "vmlal.u8 q13, d2, d5 \n"
1989 "vmlal.u8 q14, d3, d5 \n"
1990 "vrshrn.u16 d0, q13, #8 \n"
1991 "vrshrn.u16 d1, q14, #8 \n"
1992 "vst1.8 {q0}, [%0]! \n"
1993 "bgt 1b \n"
1994 "b 99f \n"
1995
1996 // Blend 50 / 50.
1997 "50: \n"
1998 "vld1.8 {q0}, [%1]! \n"
1999 "vld1.8 {q1}, [%2]! \n"
2000 "subs %3, %3, #16 \n"
2001 "vrhadd.u8 q0, q1 \n"
2002 "vst1.8 {q0}, [%0]! \n"
2003 "bgt 50b \n"
2004 "b 99f \n"
2005
2006 // Blend 100 / 0 - Copy row unchanged.
2007 "100: \n"
2008 "vld1.8 {q0}, [%1]! \n"
2009 "subs %3, %3, #16 \n"
2010 "vst1.8 {q0}, [%0]! \n"
2011 "bgt 100b \n"
2012
2013 "99: \n"
2014 : "+r"(dst_ptr), // %0
2015 "+r"(src_ptr), // %1
2016 "+r"(src_stride), // %2
2017 "+r"(dst_width), // %3
2018 "+r"(y1_fraction) // %4
2019 :
2020 : "cc", "memory", "q0", "q1", "d4", "d5", "q13", "q14");
2021 }
2022
2023 // dr * (256 - sa) / 256 + sr = dr - dr * sa / 256 + sr
ARGBBlendRow_NEON(const uint8 * src_argb0,const uint8 * src_argb1,uint8 * dst_argb,int width)2024 void ARGBBlendRow_NEON(const uint8* src_argb0,
2025 const uint8* src_argb1,
2026 uint8* dst_argb,
2027 int width) {
2028 asm volatile(
2029 "subs %3, #8 \n"
2030 "blt 89f \n"
2031 // Blend 8 pixels.
2032 "8: \n"
2033 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ARGB0.
2034 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 pixels of ARGB1.
2035 "subs %3, %3, #8 \n" // 8 processed per loop.
2036 "vmull.u8 q10, d4, d3 \n" // db * a
2037 "vmull.u8 q11, d5, d3 \n" // dg * a
2038 "vmull.u8 q12, d6, d3 \n" // dr * a
2039 "vqrshrn.u16 d20, q10, #8 \n" // db >>= 8
2040 "vqrshrn.u16 d21, q11, #8 \n" // dg >>= 8
2041 "vqrshrn.u16 d22, q12, #8 \n" // dr >>= 8
2042 "vqsub.u8 q2, q2, q10 \n" // dbg - dbg * a / 256
2043 "vqsub.u8 d6, d6, d22 \n" // dr - dr * a / 256
2044 "vqadd.u8 q0, q0, q2 \n" // + sbg
2045 "vqadd.u8 d2, d2, d6 \n" // + sr
2046 "vmov.u8 d3, #255 \n" // a = 255
2047 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 pixels of ARGB.
2048 "bge 8b \n"
2049
2050 "89: \n"
2051 "adds %3, #8-1 \n"
2052 "blt 99f \n"
2053
2054 // Blend 1 pixels.
2055 "1: \n"
2056 "vld4.8 {d0[0],d1[0],d2[0],d3[0]}, [%0]! \n" // load 1 pixel ARGB0.
2057 "vld4.8 {d4[0],d5[0],d6[0],d7[0]}, [%1]! \n" // load 1 pixel ARGB1.
2058 "subs %3, %3, #1 \n" // 1 processed per loop.
2059 "vmull.u8 q10, d4, d3 \n" // db * a
2060 "vmull.u8 q11, d5, d3 \n" // dg * a
2061 "vmull.u8 q12, d6, d3 \n" // dr * a
2062 "vqrshrn.u16 d20, q10, #8 \n" // db >>= 8
2063 "vqrshrn.u16 d21, q11, #8 \n" // dg >>= 8
2064 "vqrshrn.u16 d22, q12, #8 \n" // dr >>= 8
2065 "vqsub.u8 q2, q2, q10 \n" // dbg - dbg * a / 256
2066 "vqsub.u8 d6, d6, d22 \n" // dr - dr * a / 256
2067 "vqadd.u8 q0, q0, q2 \n" // + sbg
2068 "vqadd.u8 d2, d2, d6 \n" // + sr
2069 "vmov.u8 d3, #255 \n" // a = 255
2070 "vst4.8 {d0[0],d1[0],d2[0],d3[0]}, [%2]! \n" // store 1 pixel.
2071 "bge 1b \n"
2072
2073 "99: \n"
2074
2075 : "+r"(src_argb0), // %0
2076 "+r"(src_argb1), // %1
2077 "+r"(dst_argb), // %2
2078 "+r"(width) // %3
2079 :
2080 : "cc", "memory", "q0", "q1", "q2", "q3", "q10", "q11", "q12");
2081 }
2082
2083 // Attenuate 8 pixels at a time.
ARGBAttenuateRow_NEON(const uint8 * src_argb,uint8 * dst_argb,int width)2084 void ARGBAttenuateRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
2085 asm volatile(
2086 // Attenuate 8 pixels.
2087 "1: \n"
2088 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ARGB.
2089 "subs %2, %2, #8 \n" // 8 processed per loop.
2090 "vmull.u8 q10, d0, d3 \n" // b * a
2091 "vmull.u8 q11, d1, d3 \n" // g * a
2092 "vmull.u8 q12, d2, d3 \n" // r * a
2093 "vqrshrn.u16 d0, q10, #8 \n" // b >>= 8
2094 "vqrshrn.u16 d1, q11, #8 \n" // g >>= 8
2095 "vqrshrn.u16 d2, q12, #8 \n" // r >>= 8
2096 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB.
2097 "bgt 1b \n"
2098 : "+r"(src_argb), // %0
2099 "+r"(dst_argb), // %1
2100 "+r"(width) // %2
2101 :
2102 : "cc", "memory", "q0", "q1", "q10", "q11", "q12");
2103 }
2104
2105 // Quantize 8 ARGB pixels (32 bytes).
2106 // dst = (dst * scale >> 16) * interval_size + interval_offset;
ARGBQuantizeRow_NEON(uint8 * dst_argb,int scale,int interval_size,int interval_offset,int width)2107 void ARGBQuantizeRow_NEON(uint8* dst_argb,
2108 int scale,
2109 int interval_size,
2110 int interval_offset,
2111 int width) {
2112 asm volatile(
2113 "vdup.u16 q8, %2 \n"
2114 "vshr.u16 q8, q8, #1 \n" // scale >>= 1
2115 "vdup.u16 q9, %3 \n" // interval multiply.
2116 "vdup.u16 q10, %4 \n" // interval add
2117
2118 // 8 pixel loop.
2119 "1: \n"
2120 "vld4.8 {d0, d2, d4, d6}, [%0] \n" // load 8 pixels of ARGB.
2121 "subs %1, %1, #8 \n" // 8 processed per loop.
2122 "vmovl.u8 q0, d0 \n" // b (0 .. 255)
2123 "vmovl.u8 q1, d2 \n"
2124 "vmovl.u8 q2, d4 \n"
2125 "vqdmulh.s16 q0, q0, q8 \n" // b * scale
2126 "vqdmulh.s16 q1, q1, q8 \n" // g
2127 "vqdmulh.s16 q2, q2, q8 \n" // r
2128 "vmul.u16 q0, q0, q9 \n" // b * interval_size
2129 "vmul.u16 q1, q1, q9 \n" // g
2130 "vmul.u16 q2, q2, q9 \n" // r
2131 "vadd.u16 q0, q0, q10 \n" // b + interval_offset
2132 "vadd.u16 q1, q1, q10 \n" // g
2133 "vadd.u16 q2, q2, q10 \n" // r
2134 "vqmovn.u16 d0, q0 \n"
2135 "vqmovn.u16 d2, q1 \n"
2136 "vqmovn.u16 d4, q2 \n"
2137 "vst4.8 {d0, d2, d4, d6}, [%0]! \n" // store 8 pixels of ARGB.
2138 "bgt 1b \n"
2139 : "+r"(dst_argb), // %0
2140 "+r"(width) // %1
2141 : "r"(scale), // %2
2142 "r"(interval_size), // %3
2143 "r"(interval_offset) // %4
2144 : "cc", "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10");
2145 }
2146
2147 // Shade 8 pixels at a time by specified value.
2148 // NOTE vqrdmulh.s16 q10, q10, d0[0] must use a scaler register from 0 to 8.
2149 // Rounding in vqrdmulh does +1 to high if high bit of low s16 is set.
ARGBShadeRow_NEON(const uint8 * src_argb,uint8 * dst_argb,int width,uint32 value)2150 void ARGBShadeRow_NEON(const uint8* src_argb,
2151 uint8* dst_argb,
2152 int width,
2153 uint32 value) {
2154 asm volatile(
2155 "vdup.u32 q0, %3 \n" // duplicate scale value.
2156 "vzip.u8 d0, d1 \n" // d0 aarrggbb.
2157 "vshr.u16 q0, q0, #1 \n" // scale / 2.
2158
2159 // 8 pixel loop.
2160 "1: \n"
2161 "vld4.8 {d20, d22, d24, d26}, [%0]! \n" // load 8 pixels of ARGB.
2162 "subs %2, %2, #8 \n" // 8 processed per loop.
2163 "vmovl.u8 q10, d20 \n" // b (0 .. 255)
2164 "vmovl.u8 q11, d22 \n"
2165 "vmovl.u8 q12, d24 \n"
2166 "vmovl.u8 q13, d26 \n"
2167 "vqrdmulh.s16 q10, q10, d0[0] \n" // b * scale * 2
2168 "vqrdmulh.s16 q11, q11, d0[1] \n" // g
2169 "vqrdmulh.s16 q12, q12, d0[2] \n" // r
2170 "vqrdmulh.s16 q13, q13, d0[3] \n" // a
2171 "vqmovn.u16 d20, q10 \n"
2172 "vqmovn.u16 d22, q11 \n"
2173 "vqmovn.u16 d24, q12 \n"
2174 "vqmovn.u16 d26, q13 \n"
2175 "vst4.8 {d20, d22, d24, d26}, [%1]! \n" // store 8 pixels of ARGB.
2176 "bgt 1b \n"
2177 : "+r"(src_argb), // %0
2178 "+r"(dst_argb), // %1
2179 "+r"(width) // %2
2180 : "r"(value) // %3
2181 : "cc", "memory", "q0", "q10", "q11", "q12", "q13");
2182 }
2183
2184 // Convert 8 ARGB pixels (64 bytes) to 8 Gray ARGB pixels
2185 // Similar to ARGBToYJ but stores ARGB.
2186 // C code is (15 * b + 75 * g + 38 * r + 64) >> 7;
ARGBGrayRow_NEON(const uint8 * src_argb,uint8 * dst_argb,int width)2187 void ARGBGrayRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
2188 asm volatile(
2189 "vmov.u8 d24, #15 \n" // B * 0.11400 coefficient
2190 "vmov.u8 d25, #75 \n" // G * 0.58700 coefficient
2191 "vmov.u8 d26, #38 \n" // R * 0.29900 coefficient
2192 "1: \n"
2193 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
2194 "subs %2, %2, #8 \n" // 8 processed per loop.
2195 "vmull.u8 q2, d0, d24 \n" // B
2196 "vmlal.u8 q2, d1, d25 \n" // G
2197 "vmlal.u8 q2, d2, d26 \n" // R
2198 "vqrshrun.s16 d0, q2, #7 \n" // 15 bit to 8 bit B
2199 "vmov d1, d0 \n" // G
2200 "vmov d2, d0 \n" // R
2201 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 ARGB pixels.
2202 "bgt 1b \n"
2203 : "+r"(src_argb), // %0
2204 "+r"(dst_argb), // %1
2205 "+r"(width) // %2
2206 :
2207 : "cc", "memory", "q0", "q1", "q2", "q12", "q13");
2208 }
2209
2210 // Convert 8 ARGB pixels (32 bytes) to 8 Sepia ARGB pixels.
2211 // b = (r * 35 + g * 68 + b * 17) >> 7
2212 // g = (r * 45 + g * 88 + b * 22) >> 7
2213 // r = (r * 50 + g * 98 + b * 24) >> 7
ARGBSepiaRow_NEON(uint8 * dst_argb,int width)2214 void ARGBSepiaRow_NEON(uint8* dst_argb, int width) {
2215 asm volatile(
2216 "vmov.u8 d20, #17 \n" // BB coefficient
2217 "vmov.u8 d21, #68 \n" // BG coefficient
2218 "vmov.u8 d22, #35 \n" // BR coefficient
2219 "vmov.u8 d24, #22 \n" // GB coefficient
2220 "vmov.u8 d25, #88 \n" // GG coefficient
2221 "vmov.u8 d26, #45 \n" // GR coefficient
2222 "vmov.u8 d28, #24 \n" // BB coefficient
2223 "vmov.u8 d29, #98 \n" // BG coefficient
2224 "vmov.u8 d30, #50 \n" // BR coefficient
2225 "1: \n"
2226 "vld4.8 {d0, d1, d2, d3}, [%0] \n" // load 8 ARGB pixels.
2227 "subs %1, %1, #8 \n" // 8 processed per loop.
2228 "vmull.u8 q2, d0, d20 \n" // B to Sepia B
2229 "vmlal.u8 q2, d1, d21 \n" // G
2230 "vmlal.u8 q2, d2, d22 \n" // R
2231 "vmull.u8 q3, d0, d24 \n" // B to Sepia G
2232 "vmlal.u8 q3, d1, d25 \n" // G
2233 "vmlal.u8 q3, d2, d26 \n" // R
2234 "vmull.u8 q8, d0, d28 \n" // B to Sepia R
2235 "vmlal.u8 q8, d1, d29 \n" // G
2236 "vmlal.u8 q8, d2, d30 \n" // R
2237 "vqshrn.u16 d0, q2, #7 \n" // 16 bit to 8 bit B
2238 "vqshrn.u16 d1, q3, #7 \n" // 16 bit to 8 bit G
2239 "vqshrn.u16 d2, q8, #7 \n" // 16 bit to 8 bit R
2240 "vst4.8 {d0, d1, d2, d3}, [%0]! \n" // store 8 ARGB pixels.
2241 "bgt 1b \n"
2242 : "+r"(dst_argb), // %0
2243 "+r"(width) // %1
2244 :
2245 : "cc", "memory", "q0", "q1", "q2", "q3", "q10", "q11", "q12", "q13",
2246 "q14", "q15");
2247 }
2248
2249 // Tranform 8 ARGB pixels (32 bytes) with color matrix.
2250 // TODO(fbarchard): Was same as Sepia except matrix is provided. This function
2251 // needs to saturate. Consider doing a non-saturating version.
ARGBColorMatrixRow_NEON(const uint8 * src_argb,uint8 * dst_argb,const int8 * matrix_argb,int width)2252 void ARGBColorMatrixRow_NEON(const uint8* src_argb,
2253 uint8* dst_argb,
2254 const int8* matrix_argb,
2255 int width) {
2256 asm volatile(
2257 "vld1.8 {q2}, [%3] \n" // load 3 ARGB vectors.
2258 "vmovl.s8 q0, d4 \n" // B,G coefficients s16.
2259 "vmovl.s8 q1, d5 \n" // R,A coefficients s16.
2260
2261 "1: \n"
2262 "vld4.8 {d16, d18, d20, d22}, [%0]! \n" // load 8 ARGB pixels.
2263 "subs %2, %2, #8 \n" // 8 processed per loop.
2264 "vmovl.u8 q8, d16 \n" // b (0 .. 255) 16 bit
2265 "vmovl.u8 q9, d18 \n" // g
2266 "vmovl.u8 q10, d20 \n" // r
2267 "vmovl.u8 q11, d22 \n" // a
2268 "vmul.s16 q12, q8, d0[0] \n" // B = B * Matrix B
2269 "vmul.s16 q13, q8, d1[0] \n" // G = B * Matrix G
2270 "vmul.s16 q14, q8, d2[0] \n" // R = B * Matrix R
2271 "vmul.s16 q15, q8, d3[0] \n" // A = B * Matrix A
2272 "vmul.s16 q4, q9, d0[1] \n" // B += G * Matrix B
2273 "vmul.s16 q5, q9, d1[1] \n" // G += G * Matrix G
2274 "vmul.s16 q6, q9, d2[1] \n" // R += G * Matrix R
2275 "vmul.s16 q7, q9, d3[1] \n" // A += G * Matrix A
2276 "vqadd.s16 q12, q12, q4 \n" // Accumulate B
2277 "vqadd.s16 q13, q13, q5 \n" // Accumulate G
2278 "vqadd.s16 q14, q14, q6 \n" // Accumulate R
2279 "vqadd.s16 q15, q15, q7 \n" // Accumulate A
2280 "vmul.s16 q4, q10, d0[2] \n" // B += R * Matrix B
2281 "vmul.s16 q5, q10, d1[2] \n" // G += R * Matrix G
2282 "vmul.s16 q6, q10, d2[2] \n" // R += R * Matrix R
2283 "vmul.s16 q7, q10, d3[2] \n" // A += R * Matrix A
2284 "vqadd.s16 q12, q12, q4 \n" // Accumulate B
2285 "vqadd.s16 q13, q13, q5 \n" // Accumulate G
2286 "vqadd.s16 q14, q14, q6 \n" // Accumulate R
2287 "vqadd.s16 q15, q15, q7 \n" // Accumulate A
2288 "vmul.s16 q4, q11, d0[3] \n" // B += A * Matrix B
2289 "vmul.s16 q5, q11, d1[3] \n" // G += A * Matrix G
2290 "vmul.s16 q6, q11, d2[3] \n" // R += A * Matrix R
2291 "vmul.s16 q7, q11, d3[3] \n" // A += A * Matrix A
2292 "vqadd.s16 q12, q12, q4 \n" // Accumulate B
2293 "vqadd.s16 q13, q13, q5 \n" // Accumulate G
2294 "vqadd.s16 q14, q14, q6 \n" // Accumulate R
2295 "vqadd.s16 q15, q15, q7 \n" // Accumulate A
2296 "vqshrun.s16 d16, q12, #6 \n" // 16 bit to 8 bit B
2297 "vqshrun.s16 d18, q13, #6 \n" // 16 bit to 8 bit G
2298 "vqshrun.s16 d20, q14, #6 \n" // 16 bit to 8 bit R
2299 "vqshrun.s16 d22, q15, #6 \n" // 16 bit to 8 bit A
2300 "vst4.8 {d16, d18, d20, d22}, [%1]! \n" // store 8 ARGB pixels.
2301 "bgt 1b \n"
2302 : "+r"(src_argb), // %0
2303 "+r"(dst_argb), // %1
2304 "+r"(width) // %2
2305 : "r"(matrix_argb) // %3
2306 : "cc", "memory", "q0", "q1", "q2", "q4", "q5", "q6", "q7", "q8", "q9",
2307 "q10", "q11", "q12", "q13", "q14", "q15");
2308 }
2309
2310 // Multiply 2 rows of ARGB pixels together, 8 pixels at a time.
ARGBMultiplyRow_NEON(const uint8 * src_argb0,const uint8 * src_argb1,uint8 * dst_argb,int width)2311 void ARGBMultiplyRow_NEON(const uint8* src_argb0,
2312 const uint8* src_argb1,
2313 uint8* dst_argb,
2314 int width) {
2315 asm volatile(
2316 // 8 pixel loop.
2317 "1: \n"
2318 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
2319 "vld4.8 {d1, d3, d5, d7}, [%1]! \n" // load 8 more ARGB
2320 "subs %3, %3, #8 \n" // 8 processed per loop.
2321 "vmull.u8 q0, d0, d1 \n" // multiply B
2322 "vmull.u8 q1, d2, d3 \n" // multiply G
2323 "vmull.u8 q2, d4, d5 \n" // multiply R
2324 "vmull.u8 q3, d6, d7 \n" // multiply A
2325 "vrshrn.u16 d0, q0, #8 \n" // 16 bit to 8 bit B
2326 "vrshrn.u16 d1, q1, #8 \n" // 16 bit to 8 bit G
2327 "vrshrn.u16 d2, q2, #8 \n" // 16 bit to 8 bit R
2328 "vrshrn.u16 d3, q3, #8 \n" // 16 bit to 8 bit A
2329 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels.
2330 "bgt 1b \n"
2331 : "+r"(src_argb0), // %0
2332 "+r"(src_argb1), // %1
2333 "+r"(dst_argb), // %2
2334 "+r"(width) // %3
2335 :
2336 : "cc", "memory", "q0", "q1", "q2", "q3");
2337 }
2338
2339 // Add 2 rows of ARGB pixels together, 8 pixels at a time.
ARGBAddRow_NEON(const uint8 * src_argb0,const uint8 * src_argb1,uint8 * dst_argb,int width)2340 void ARGBAddRow_NEON(const uint8* src_argb0,
2341 const uint8* src_argb1,
2342 uint8* dst_argb,
2343 int width) {
2344 asm volatile(
2345 // 8 pixel loop.
2346 "1: \n"
2347 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
2348 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 more ARGB
2349 "subs %3, %3, #8 \n" // 8 processed per loop.
2350 "vqadd.u8 q0, q0, q2 \n" // add B, G
2351 "vqadd.u8 q1, q1, q3 \n" // add R, A
2352 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels.
2353 "bgt 1b \n"
2354 : "+r"(src_argb0), // %0
2355 "+r"(src_argb1), // %1
2356 "+r"(dst_argb), // %2
2357 "+r"(width) // %3
2358 :
2359 : "cc", "memory", "q0", "q1", "q2", "q3");
2360 }
2361
2362 // Subtract 2 rows of ARGB pixels, 8 pixels at a time.
ARGBSubtractRow_NEON(const uint8 * src_argb0,const uint8 * src_argb1,uint8 * dst_argb,int width)2363 void ARGBSubtractRow_NEON(const uint8* src_argb0,
2364 const uint8* src_argb1,
2365 uint8* dst_argb,
2366 int width) {
2367 asm volatile(
2368 // 8 pixel loop.
2369 "1: \n"
2370 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels.
2371 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 more ARGB
2372 "subs %3, %3, #8 \n" // 8 processed per loop.
2373 "vqsub.u8 q0, q0, q2 \n" // subtract B, G
2374 "vqsub.u8 q1, q1, q3 \n" // subtract R, A
2375 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels.
2376 "bgt 1b \n"
2377 : "+r"(src_argb0), // %0
2378 "+r"(src_argb1), // %1
2379 "+r"(dst_argb), // %2
2380 "+r"(width) // %3
2381 :
2382 : "cc", "memory", "q0", "q1", "q2", "q3");
2383 }
2384
2385 // Adds Sobel X and Sobel Y and stores Sobel into ARGB.
2386 // A = 255
2387 // R = Sobel
2388 // G = Sobel
2389 // B = Sobel
SobelRow_NEON(const uint8 * src_sobelx,const uint8 * src_sobely,uint8 * dst_argb,int width)2390 void SobelRow_NEON(const uint8* src_sobelx,
2391 const uint8* src_sobely,
2392 uint8* dst_argb,
2393 int width) {
2394 asm volatile(
2395 "vmov.u8 d3, #255 \n" // alpha
2396 // 8 pixel loop.
2397 "1: \n"
2398 "vld1.8 {d0}, [%0]! \n" // load 8 sobelx.
2399 "vld1.8 {d1}, [%1]! \n" // load 8 sobely.
2400 "subs %3, %3, #8 \n" // 8 processed per loop.
2401 "vqadd.u8 d0, d0, d1 \n" // add
2402 "vmov.u8 d1, d0 \n"
2403 "vmov.u8 d2, d0 \n"
2404 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels.
2405 "bgt 1b \n"
2406 : "+r"(src_sobelx), // %0
2407 "+r"(src_sobely), // %1
2408 "+r"(dst_argb), // %2
2409 "+r"(width) // %3
2410 :
2411 : "cc", "memory", "q0", "q1");
2412 }
2413
2414 // Adds Sobel X and Sobel Y and stores Sobel into plane.
SobelToPlaneRow_NEON(const uint8 * src_sobelx,const uint8 * src_sobely,uint8 * dst_y,int width)2415 void SobelToPlaneRow_NEON(const uint8* src_sobelx,
2416 const uint8* src_sobely,
2417 uint8* dst_y,
2418 int width) {
2419 asm volatile(
2420 // 16 pixel loop.
2421 "1: \n"
2422 "vld1.8 {q0}, [%0]! \n" // load 16 sobelx.
2423 "vld1.8 {q1}, [%1]! \n" // load 16 sobely.
2424 "subs %3, %3, #16 \n" // 16 processed per loop.
2425 "vqadd.u8 q0, q0, q1 \n" // add
2426 "vst1.8 {q0}, [%2]! \n" // store 16 pixels.
2427 "bgt 1b \n"
2428 : "+r"(src_sobelx), // %0
2429 "+r"(src_sobely), // %1
2430 "+r"(dst_y), // %2
2431 "+r"(width) // %3
2432 :
2433 : "cc", "memory", "q0", "q1");
2434 }
2435
2436 // Mixes Sobel X, Sobel Y and Sobel into ARGB.
2437 // A = 255
2438 // R = Sobel X
2439 // G = Sobel
2440 // B = Sobel Y
SobelXYRow_NEON(const uint8 * src_sobelx,const uint8 * src_sobely,uint8 * dst_argb,int width)2441 void SobelXYRow_NEON(const uint8* src_sobelx,
2442 const uint8* src_sobely,
2443 uint8* dst_argb,
2444 int width) {
2445 asm volatile(
2446 "vmov.u8 d3, #255 \n" // alpha
2447 // 8 pixel loop.
2448 "1: \n"
2449 "vld1.8 {d2}, [%0]! \n" // load 8 sobelx.
2450 "vld1.8 {d0}, [%1]! \n" // load 8 sobely.
2451 "subs %3, %3, #8 \n" // 8 processed per loop.
2452 "vqadd.u8 d1, d0, d2 \n" // add
2453 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels.
2454 "bgt 1b \n"
2455 : "+r"(src_sobelx), // %0
2456 "+r"(src_sobely), // %1
2457 "+r"(dst_argb), // %2
2458 "+r"(width) // %3
2459 :
2460 : "cc", "memory", "q0", "q1");
2461 }
2462
2463 // SobelX as a matrix is
2464 // -1 0 1
2465 // -2 0 2
2466 // -1 0 1
SobelXRow_NEON(const uint8 * src_y0,const uint8 * src_y1,const uint8 * src_y2,uint8 * dst_sobelx,int width)2467 void SobelXRow_NEON(const uint8* src_y0,
2468 const uint8* src_y1,
2469 const uint8* src_y2,
2470 uint8* dst_sobelx,
2471 int width) {
2472 asm volatile(
2473 "1: \n"
2474 "vld1.8 {d0}, [%0],%5 \n" // top
2475 "vld1.8 {d1}, [%0],%6 \n"
2476 "vsubl.u8 q0, d0, d1 \n"
2477 "vld1.8 {d2}, [%1],%5 \n" // center * 2
2478 "vld1.8 {d3}, [%1],%6 \n"
2479 "vsubl.u8 q1, d2, d3 \n"
2480 "vadd.s16 q0, q0, q1 \n"
2481 "vadd.s16 q0, q0, q1 \n"
2482 "vld1.8 {d2}, [%2],%5 \n" // bottom
2483 "vld1.8 {d3}, [%2],%6 \n"
2484 "subs %4, %4, #8 \n" // 8 pixels
2485 "vsubl.u8 q1, d2, d3 \n"
2486 "vadd.s16 q0, q0, q1 \n"
2487 "vabs.s16 q0, q0 \n"
2488 "vqmovn.u16 d0, q0 \n"
2489 "vst1.8 {d0}, [%3]! \n" // store 8 sobelx
2490 "bgt 1b \n"
2491 : "+r"(src_y0), // %0
2492 "+r"(src_y1), // %1
2493 "+r"(src_y2), // %2
2494 "+r"(dst_sobelx), // %3
2495 "+r"(width) // %4
2496 : "r"(2), // %5
2497 "r"(6) // %6
2498 : "cc", "memory", "q0", "q1" // Clobber List
2499 );
2500 }
2501
2502 // SobelY as a matrix is
2503 // -1 -2 -1
2504 // 0 0 0
2505 // 1 2 1
SobelYRow_NEON(const uint8 * src_y0,const uint8 * src_y1,uint8 * dst_sobely,int width)2506 void SobelYRow_NEON(const uint8* src_y0,
2507 const uint8* src_y1,
2508 uint8* dst_sobely,
2509 int width) {
2510 asm volatile(
2511 "1: \n"
2512 "vld1.8 {d0}, [%0],%4 \n" // left
2513 "vld1.8 {d1}, [%1],%4 \n"
2514 "vsubl.u8 q0, d0, d1 \n"
2515 "vld1.8 {d2}, [%0],%4 \n" // center * 2
2516 "vld1.8 {d3}, [%1],%4 \n"
2517 "vsubl.u8 q1, d2, d3 \n"
2518 "vadd.s16 q0, q0, q1 \n"
2519 "vadd.s16 q0, q0, q1 \n"
2520 "vld1.8 {d2}, [%0],%5 \n" // right
2521 "vld1.8 {d3}, [%1],%5 \n"
2522 "subs %3, %3, #8 \n" // 8 pixels
2523 "vsubl.u8 q1, d2, d3 \n"
2524 "vadd.s16 q0, q0, q1 \n"
2525 "vabs.s16 q0, q0 \n"
2526 "vqmovn.u16 d0, q0 \n"
2527 "vst1.8 {d0}, [%2]! \n" // store 8 sobely
2528 "bgt 1b \n"
2529 : "+r"(src_y0), // %0
2530 "+r"(src_y1), // %1
2531 "+r"(dst_sobely), // %2
2532 "+r"(width) // %3
2533 : "r"(1), // %4
2534 "r"(6) // %5
2535 : "cc", "memory", "q0", "q1" // Clobber List
2536 );
2537 }
2538
HalfFloat1Row_NEON(const uint16 * src,uint16 * dst,float,int width)2539 void HalfFloat1Row_NEON(const uint16* src, uint16* dst, float, int width) {
2540 asm volatile(
2541 "vdup.32 q0, %3 \n"
2542
2543 "1: \n"
2544 "vld1.8 {q1}, [%0]! \n" // load 8 shorts
2545 "subs %2, %2, #8 \n" // 8 pixels per loop
2546 "vmovl.u16 q2, d2 \n" // 8 int's
2547 "vmovl.u16 q3, d3 \n"
2548 "vcvt.f32.u32 q2, q2 \n" // 8 floats
2549 "vcvt.f32.u32 q3, q3 \n"
2550 "vmul.f32 q2, q2, q0 \n" // adjust exponent
2551 "vmul.f32 q3, q3, q0 \n"
2552 "vqshrn.u32 d2, q2, #13 \n" // isolate halffloat
2553 "vqshrn.u32 d3, q3, #13 \n"
2554 "vst1.8 {q1}, [%1]! \n"
2555 "bgt 1b \n"
2556 : "+r"(src), // %0
2557 "+r"(dst), // %1
2558 "+r"(width) // %2
2559 : "r"(1.9259299444e-34f) // %3
2560 : "cc", "memory", "q0", "q1", "q2", "q3");
2561 }
2562
2563 // TODO(fbarchard): multiply by element.
HalfFloatRow_NEON(const uint16 * src,uint16 * dst,float scale,int width)2564 void HalfFloatRow_NEON(const uint16* src, uint16* dst, float scale, int width) {
2565 asm volatile(
2566 "vdup.32 q0, %3 \n"
2567
2568 "1: \n"
2569 "vld1.8 {q1}, [%0]! \n" // load 8 shorts
2570 "subs %2, %2, #8 \n" // 8 pixels per loop
2571 "vmovl.u16 q2, d2 \n" // 8 int's
2572 "vmovl.u16 q3, d3 \n"
2573 "vcvt.f32.u32 q2, q2 \n" // 8 floats
2574 "vcvt.f32.u32 q3, q3 \n"
2575 "vmul.f32 q2, q2, q0 \n" // adjust exponent
2576 "vmul.f32 q3, q3, q0 \n"
2577 "vqshrn.u32 d2, q2, #13 \n" // isolate halffloat
2578 "vqshrn.u32 d3, q3, #13 \n"
2579 "vst1.8 {q1}, [%1]! \n"
2580 "bgt 1b \n"
2581 : "+r"(src), // %0
2582 "+r"(dst), // %1
2583 "+r"(width) // %2
2584 : "r"(scale * 1.9259299444e-34f) // %3
2585 : "cc", "memory", "q0", "q1", "q2", "q3");
2586 }
2587
2588 #endif // !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__)..
2589
2590 #ifdef __cplusplus
2591 } // extern "C"
2592 } // namespace libyuv
2593 #endif
2594