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