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 #ifdef __cplusplus
14 namespace libyuv {
15 extern "C" {
16 #endif
17
18 // This module is for GCC Neon.
19 #if !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__) && \
20 !defined(__aarch64__)
21
22 // NEON downscalers with interpolation.
23 // Provided by Fritz Koenig
24
25 // Read 32x1 throw away even pixels, and write 16x1.
ScaleRowDown2_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)26 void ScaleRowDown2_NEON(const uint8_t* src_ptr,
27 ptrdiff_t src_stride,
28 uint8_t* dst,
29 int dst_width) {
30 (void)src_stride;
31 asm volatile(
32 "1: \n"
33 // load even pixels into q0, odd into q1
34 "vld2.8 {q0, q1}, [%0]! \n"
35 "subs %2, %2, #16 \n" // 16 processed per loop
36 "vst1.8 {q1}, [%1]! \n" // store odd pixels
37 "bgt 1b \n"
38 : "+r"(src_ptr), // %0
39 "+r"(dst), // %1
40 "+r"(dst_width) // %2
41 :
42 : "q0", "q1" // Clobber List
43 );
44 }
45
46 // Read 32x1 average down and write 16x1.
ScaleRowDown2Linear_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)47 void ScaleRowDown2Linear_NEON(const uint8_t* src_ptr,
48 ptrdiff_t src_stride,
49 uint8_t* dst,
50 int dst_width) {
51 (void)src_stride;
52 asm volatile(
53 "1: \n"
54 "vld2.8 {q0, q1}, [%0]! \n" // load 32 pixels
55 "subs %2, %2, #16 \n" // 16 processed per loop
56 "vrhadd.u8 q0, q0, q1 \n" // rounding half add
57 "vst1.8 {q0}, [%1]! \n"
58 "bgt 1b \n"
59 : "+r"(src_ptr), // %0
60 "+r"(dst), // %1
61 "+r"(dst_width) // %2
62 :
63 : "q0", "q1" // Clobber List
64 );
65 }
66
67 // Read 32x2 average down and write 16x1.
ScaleRowDown2Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)68 void ScaleRowDown2Box_NEON(const uint8_t* src_ptr,
69 ptrdiff_t src_stride,
70 uint8_t* dst,
71 int dst_width) {
72 asm volatile(
73 // change the stride to row 2 pointer
74 "add %1, %0 \n"
75 "1: \n"
76 "vld1.8 {q0, q1}, [%0]! \n" // load row 1 and post inc
77 "vld1.8 {q2, q3}, [%1]! \n" // load row 2 and post inc
78 "subs %3, %3, #16 \n" // 16 processed per loop
79 "vpaddl.u8 q0, q0 \n" // row 1 add adjacent
80 "vpaddl.u8 q1, q1 \n"
81 "vpadal.u8 q0, q2 \n" // row 2 add adjacent +
82 // row1
83 "vpadal.u8 q1, q3 \n"
84 "vrshrn.u16 d0, q0, #2 \n" // downshift, round and
85 // pack
86 "vrshrn.u16 d1, q1, #2 \n"
87 "vst1.8 {q0}, [%2]! \n"
88 "bgt 1b \n"
89 : "+r"(src_ptr), // %0
90 "+r"(src_stride), // %1
91 "+r"(dst), // %2
92 "+r"(dst_width) // %3
93 :
94 : "q0", "q1", "q2", "q3" // Clobber List
95 );
96 }
97
ScaleRowDown4_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)98 void ScaleRowDown4_NEON(const uint8_t* src_ptr,
99 ptrdiff_t src_stride,
100 uint8_t* dst_ptr,
101 int dst_width) {
102 (void)src_stride;
103 asm volatile(
104 "1: \n"
105 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
106 "subs %2, %2, #8 \n" // 8 processed per loop
107 "vst1.8 {d2}, [%1]! \n"
108 "bgt 1b \n"
109 : "+r"(src_ptr), // %0
110 "+r"(dst_ptr), // %1
111 "+r"(dst_width) // %2
112 :
113 : "q0", "q1", "memory", "cc");
114 }
115
ScaleRowDown4Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)116 void ScaleRowDown4Box_NEON(const uint8_t* src_ptr,
117 ptrdiff_t src_stride,
118 uint8_t* dst_ptr,
119 int dst_width) {
120 const uint8_t* src_ptr1 = src_ptr + src_stride;
121 const uint8_t* src_ptr2 = src_ptr + src_stride * 2;
122 const uint8_t* src_ptr3 = src_ptr + src_stride * 3;
123 asm volatile(
124 "1: \n"
125 "vld1.8 {q0}, [%0]! \n" // load up 16x4
126 "vld1.8 {q1}, [%3]! \n"
127 "vld1.8 {q2}, [%4]! \n"
128 "vld1.8 {q3}, [%5]! \n"
129 "subs %2, %2, #4 \n"
130 "vpaddl.u8 q0, q0 \n"
131 "vpadal.u8 q0, q1 \n"
132 "vpadal.u8 q0, q2 \n"
133 "vpadal.u8 q0, q3 \n"
134 "vpaddl.u16 q0, q0 \n"
135 "vrshrn.u32 d0, q0, #4 \n" // divide by 16 w/rounding
136 "vmovn.u16 d0, q0 \n"
137 "vst1.32 {d0[0]}, [%1]! \n"
138 "bgt 1b \n"
139 : "+r"(src_ptr), // %0
140 "+r"(dst_ptr), // %1
141 "+r"(dst_width), // %2
142 "+r"(src_ptr1), // %3
143 "+r"(src_ptr2), // %4
144 "+r"(src_ptr3) // %5
145 :
146 : "q0", "q1", "q2", "q3", "memory", "cc");
147 }
148
149 // Down scale from 4 to 3 pixels. Use the neon multilane read/write
150 // to load up the every 4th pixel into a 4 different registers.
151 // Point samples 32 pixels to 24 pixels.
ScaleRowDown34_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)152 void ScaleRowDown34_NEON(const uint8_t* src_ptr,
153 ptrdiff_t src_stride,
154 uint8_t* dst_ptr,
155 int dst_width) {
156 (void)src_stride;
157 asm volatile(
158 "1: \n"
159 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
160 "subs %2, %2, #24 \n"
161 "vmov d2, d3 \n" // order d0, d1, d2
162 "vst3.8 {d0, d1, d2}, [%1]! \n"
163 "bgt 1b \n"
164 : "+r"(src_ptr), // %0
165 "+r"(dst_ptr), // %1
166 "+r"(dst_width) // %2
167 :
168 : "d0", "d1", "d2", "d3", "memory", "cc");
169 }
170
ScaleRowDown34_0_Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)171 void ScaleRowDown34_0_Box_NEON(const uint8_t* src_ptr,
172 ptrdiff_t src_stride,
173 uint8_t* dst_ptr,
174 int dst_width) {
175 asm volatile(
176 "vmov.u8 d24, #3 \n"
177 "add %3, %0 \n"
178 "1: \n"
179 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
180 "vld4.8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
181 "subs %2, %2, #24 \n"
182
183 // filter src line 0 with src line 1
184 // expand chars to shorts to allow for room
185 // when adding lines together
186 "vmovl.u8 q8, d4 \n"
187 "vmovl.u8 q9, d5 \n"
188 "vmovl.u8 q10, d6 \n"
189 "vmovl.u8 q11, d7 \n"
190
191 // 3 * line_0 + line_1
192 "vmlal.u8 q8, d0, d24 \n"
193 "vmlal.u8 q9, d1, d24 \n"
194 "vmlal.u8 q10, d2, d24 \n"
195 "vmlal.u8 q11, d3, d24 \n"
196
197 // (3 * line_0 + line_1 + 2) >> 2
198 "vqrshrn.u16 d0, q8, #2 \n"
199 "vqrshrn.u16 d1, q9, #2 \n"
200 "vqrshrn.u16 d2, q10, #2 \n"
201 "vqrshrn.u16 d3, q11, #2 \n"
202
203 // a0 = (src[0] * 3 + s[1] * 1 + 2) >> 2
204 "vmovl.u8 q8, d1 \n"
205 "vmlal.u8 q8, d0, d24 \n"
206 "vqrshrn.u16 d0, q8, #2 \n"
207
208 // a1 = (src[1] * 1 + s[2] * 1 + 1) >> 1
209 "vrhadd.u8 d1, d1, d2 \n"
210
211 // a2 = (src[2] * 1 + s[3] * 3 + 2) >> 2
212 "vmovl.u8 q8, d2 \n"
213 "vmlal.u8 q8, d3, d24 \n"
214 "vqrshrn.u16 d2, q8, #2 \n"
215
216 "vst3.8 {d0, d1, d2}, [%1]! \n"
217
218 "bgt 1b \n"
219 : "+r"(src_ptr), // %0
220 "+r"(dst_ptr), // %1
221 "+r"(dst_width), // %2
222 "+r"(src_stride) // %3
223 :
224 : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "d24", "memory",
225 "cc");
226 }
227
ScaleRowDown34_1_Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)228 void ScaleRowDown34_1_Box_NEON(const uint8_t* src_ptr,
229 ptrdiff_t src_stride,
230 uint8_t* dst_ptr,
231 int dst_width) {
232 asm volatile(
233 "vmov.u8 d24, #3 \n"
234 "add %3, %0 \n"
235 "1: \n"
236 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
237 "vld4.8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
238 "subs %2, %2, #24 \n"
239 // average src line 0 with src line 1
240 "vrhadd.u8 q0, q0, q2 \n"
241 "vrhadd.u8 q1, q1, q3 \n"
242
243 // a0 = (src[0] * 3 + s[1] * 1 + 2) >> 2
244 "vmovl.u8 q3, d1 \n"
245 "vmlal.u8 q3, d0, d24 \n"
246 "vqrshrn.u16 d0, q3, #2 \n"
247
248 // a1 = (src[1] * 1 + s[2] * 1 + 1) >> 1
249 "vrhadd.u8 d1, d1, d2 \n"
250
251 // a2 = (src[2] * 1 + s[3] * 3 + 2) >> 2
252 "vmovl.u8 q3, d2 \n"
253 "vmlal.u8 q3, d3, d24 \n"
254 "vqrshrn.u16 d2, q3, #2 \n"
255
256 "vst3.8 {d0, d1, d2}, [%1]! \n"
257 "bgt 1b \n"
258 : "+r"(src_ptr), // %0
259 "+r"(dst_ptr), // %1
260 "+r"(dst_width), // %2
261 "+r"(src_stride) // %3
262 :
263 : "r4", "q0", "q1", "q2", "q3", "d24", "memory", "cc");
264 }
265
266 #define HAS_SCALEROWDOWN38_NEON
267 static const uvec8 kShuf38 = {0, 3, 6, 8, 11, 14, 16, 19,
268 22, 24, 27, 30, 0, 0, 0, 0};
269 static const uvec8 kShuf38_2 = {0, 8, 16, 2, 10, 17, 4, 12,
270 18, 6, 14, 19, 0, 0, 0, 0};
271 static const vec16 kMult38_Div6 = {65536 / 12, 65536 / 12, 65536 / 12,
272 65536 / 12, 65536 / 12, 65536 / 12,
273 65536 / 12, 65536 / 12};
274 static const vec16 kMult38_Div9 = {65536 / 18, 65536 / 18, 65536 / 18,
275 65536 / 18, 65536 / 18, 65536 / 18,
276 65536 / 18, 65536 / 18};
277
278 // 32 -> 12
ScaleRowDown38_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)279 void ScaleRowDown38_NEON(const uint8_t* src_ptr,
280 ptrdiff_t src_stride,
281 uint8_t* dst_ptr,
282 int dst_width) {
283 (void)src_stride;
284 asm volatile(
285 "vld1.8 {q3}, [%3] \n"
286 "1: \n"
287 "vld1.8 {d0, d1, d2, d3}, [%0]! \n"
288 "subs %2, %2, #12 \n"
289 "vtbl.u8 d4, {d0, d1, d2, d3}, d6 \n"
290 "vtbl.u8 d5, {d0, d1, d2, d3}, d7 \n"
291 "vst1.8 {d4}, [%1]! \n"
292 "vst1.32 {d5[0]}, [%1]! \n"
293 "bgt 1b \n"
294 : "+r"(src_ptr), // %0
295 "+r"(dst_ptr), // %1
296 "+r"(dst_width) // %2
297 : "r"(&kShuf38) // %3
298 : "d0", "d1", "d2", "d3", "d4", "d5", "memory", "cc");
299 }
300
301 // 32x3 -> 12x1
ScaleRowDown38_3_Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)302 void OMITFP ScaleRowDown38_3_Box_NEON(const uint8_t* src_ptr,
303 ptrdiff_t src_stride,
304 uint8_t* dst_ptr,
305 int dst_width) {
306 const uint8_t* src_ptr1 = src_ptr + src_stride * 2;
307
308 asm volatile(
309 "vld1.16 {q13}, [%5] \n"
310 "vld1.8 {q14}, [%6] \n"
311 "vld1.8 {q15}, [%7] \n"
312 "add %3, %0 \n"
313 "1: \n"
314
315 // d0 = 00 40 01 41 02 42 03 43
316 // d1 = 10 50 11 51 12 52 13 53
317 // d2 = 20 60 21 61 22 62 23 63
318 // d3 = 30 70 31 71 32 72 33 73
319 "vld4.8 {d0, d1, d2, d3}, [%0]! \n"
320 "vld4.8 {d4, d5, d6, d7}, [%3]! \n"
321 "vld4.8 {d16, d17, d18, d19}, [%4]! \n"
322 "subs %2, %2, #12 \n"
323
324 // Shuffle the input data around to get align the data
325 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
326 // d0 = 00 10 01 11 02 12 03 13
327 // d1 = 40 50 41 51 42 52 43 53
328 "vtrn.u8 d0, d1 \n"
329 "vtrn.u8 d4, d5 \n"
330 "vtrn.u8 d16, d17 \n"
331
332 // d2 = 20 30 21 31 22 32 23 33
333 // d3 = 60 70 61 71 62 72 63 73
334 "vtrn.u8 d2, d3 \n"
335 "vtrn.u8 d6, d7 \n"
336 "vtrn.u8 d18, d19 \n"
337
338 // d0 = 00+10 01+11 02+12 03+13
339 // d2 = 40+50 41+51 42+52 43+53
340 "vpaddl.u8 q0, q0 \n"
341 "vpaddl.u8 q2, q2 \n"
342 "vpaddl.u8 q8, q8 \n"
343
344 // d3 = 60+70 61+71 62+72 63+73
345 "vpaddl.u8 d3, d3 \n"
346 "vpaddl.u8 d7, d7 \n"
347 "vpaddl.u8 d19, d19 \n"
348
349 // combine source lines
350 "vadd.u16 q0, q2 \n"
351 "vadd.u16 q0, q8 \n"
352 "vadd.u16 d4, d3, d7 \n"
353 "vadd.u16 d4, d19 \n"
354
355 // dst_ptr[3] = (s[6 + st * 0] + s[7 + st * 0]
356 // + s[6 + st * 1] + s[7 + st * 1]
357 // + s[6 + st * 2] + s[7 + st * 2]) / 6
358 "vqrdmulh.s16 q2, q2, q13 \n"
359 "vmovn.u16 d4, q2 \n"
360
361 // Shuffle 2,3 reg around so that 2 can be added to the
362 // 0,1 reg and 3 can be added to the 4,5 reg. This
363 // requires expanding from u8 to u16 as the 0,1 and 4,5
364 // registers are already expanded. Then do transposes
365 // to get aligned.
366 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
367 "vmovl.u8 q1, d2 \n"
368 "vmovl.u8 q3, d6 \n"
369 "vmovl.u8 q9, d18 \n"
370
371 // combine source lines
372 "vadd.u16 q1, q3 \n"
373 "vadd.u16 q1, q9 \n"
374
375 // d4 = xx 20 xx 30 xx 22 xx 32
376 // d5 = xx 21 xx 31 xx 23 xx 33
377 "vtrn.u32 d2, d3 \n"
378
379 // d4 = xx 20 xx 21 xx 22 xx 23
380 // d5 = xx 30 xx 31 xx 32 xx 33
381 "vtrn.u16 d2, d3 \n"
382
383 // 0+1+2, 3+4+5
384 "vadd.u16 q0, q1 \n"
385
386 // Need to divide, but can't downshift as the the value
387 // isn't a power of 2. So multiply by 65536 / n
388 // and take the upper 16 bits.
389 "vqrdmulh.s16 q0, q0, q15 \n"
390
391 // Align for table lookup, vtbl requires registers to
392 // be adjacent
393 "vmov.u8 d2, d4 \n"
394
395 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
396 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
397
398 "vst1.8 {d3}, [%1]! \n"
399 "vst1.32 {d4[0]}, [%1]! \n"
400 "bgt 1b \n"
401 : "+r"(src_ptr), // %0
402 "+r"(dst_ptr), // %1
403 "+r"(dst_width), // %2
404 "+r"(src_stride), // %3
405 "+r"(src_ptr1) // %4
406 : "r"(&kMult38_Div6), // %5
407 "r"(&kShuf38_2), // %6
408 "r"(&kMult38_Div9) // %7
409 : "q0", "q1", "q2", "q3", "q8", "q9", "q13", "q14", "q15", "memory",
410 "cc");
411 }
412
413 // 32x2 -> 12x1
ScaleRowDown38_2_Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,int dst_width)414 void ScaleRowDown38_2_Box_NEON(const uint8_t* src_ptr,
415 ptrdiff_t src_stride,
416 uint8_t* dst_ptr,
417 int dst_width) {
418 asm volatile(
419 "vld1.16 {q13}, [%4] \n"
420 "vld1.8 {q14}, [%5] \n"
421 "add %3, %0 \n"
422 "1: \n"
423
424 // d0 = 00 40 01 41 02 42 03 43
425 // d1 = 10 50 11 51 12 52 13 53
426 // d2 = 20 60 21 61 22 62 23 63
427 // d3 = 30 70 31 71 32 72 33 73
428 "vld4.8 {d0, d1, d2, d3}, [%0]! \n"
429 "vld4.8 {d4, d5, d6, d7}, [%3]! \n"
430 "subs %2, %2, #12 \n"
431
432 // Shuffle the input data around to get align the data
433 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
434 // d0 = 00 10 01 11 02 12 03 13
435 // d1 = 40 50 41 51 42 52 43 53
436 "vtrn.u8 d0, d1 \n"
437 "vtrn.u8 d4, d5 \n"
438
439 // d2 = 20 30 21 31 22 32 23 33
440 // d3 = 60 70 61 71 62 72 63 73
441 "vtrn.u8 d2, d3 \n"
442 "vtrn.u8 d6, d7 \n"
443
444 // d0 = 00+10 01+11 02+12 03+13
445 // d2 = 40+50 41+51 42+52 43+53
446 "vpaddl.u8 q0, q0 \n"
447 "vpaddl.u8 q2, q2 \n"
448
449 // d3 = 60+70 61+71 62+72 63+73
450 "vpaddl.u8 d3, d3 \n"
451 "vpaddl.u8 d7, d7 \n"
452
453 // combine source lines
454 "vadd.u16 q0, q2 \n"
455 "vadd.u16 d4, d3, d7 \n"
456
457 // dst_ptr[3] = (s[6] + s[7] + s[6+st] + s[7+st]) / 4
458 "vqrshrn.u16 d4, q2, #2 \n"
459
460 // Shuffle 2,3 reg around so that 2 can be added to the
461 // 0,1 reg and 3 can be added to the 4,5 reg. This
462 // requires expanding from u8 to u16 as the 0,1 and 4,5
463 // registers are already expanded. Then do transposes
464 // to get aligned.
465 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
466 "vmovl.u8 q1, d2 \n"
467 "vmovl.u8 q3, d6 \n"
468
469 // combine source lines
470 "vadd.u16 q1, q3 \n"
471
472 // d4 = xx 20 xx 30 xx 22 xx 32
473 // d5 = xx 21 xx 31 xx 23 xx 33
474 "vtrn.u32 d2, d3 \n"
475
476 // d4 = xx 20 xx 21 xx 22 xx 23
477 // d5 = xx 30 xx 31 xx 32 xx 33
478 "vtrn.u16 d2, d3 \n"
479
480 // 0+1+2, 3+4+5
481 "vadd.u16 q0, q1 \n"
482
483 // Need to divide, but can't downshift as the the value
484 // isn't a power of 2. So multiply by 65536 / n
485 // and take the upper 16 bits.
486 "vqrdmulh.s16 q0, q0, q13 \n"
487
488 // Align for table lookup, vtbl requires registers to
489 // be adjacent
490 "vmov.u8 d2, d4 \n"
491
492 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
493 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
494
495 "vst1.8 {d3}, [%1]! \n"
496 "vst1.32 {d4[0]}, [%1]! \n"
497 "bgt 1b \n"
498 : "+r"(src_ptr), // %0
499 "+r"(dst_ptr), // %1
500 "+r"(dst_width), // %2
501 "+r"(src_stride) // %3
502 : "r"(&kMult38_Div6), // %4
503 "r"(&kShuf38_2) // %5
504 : "q0", "q1", "q2", "q3", "q13", "q14", "memory", "cc");
505 }
506
ScaleRowUp2_Linear_NEON(const uint8_t * src_ptr,uint8_t * dst_ptr,int dst_width)507 void ScaleRowUp2_Linear_NEON(const uint8_t* src_ptr,
508 uint8_t* dst_ptr,
509 int dst_width) {
510 const uint8_t* src_temp = src_ptr + 1;
511 asm volatile(
512 "vmov.u8 d30, #3 \n"
513
514 "1: \n"
515 "vld1.8 {d4}, [%0]! \n" // 01234567
516 "vld1.8 {d5}, [%3]! \n" // 12345678
517
518 "vmovl.u8 q0, d4 \n" // 01234567 (16b)
519 "vmovl.u8 q1, d5 \n" // 12345678 (16b)
520 "vmlal.u8 q0, d5, d30 \n" // 3*near+far (odd)
521 "vmlal.u8 q1, d4, d30 \n" // 3*near+far (even)
522
523 "vrshrn.u16 d1, q0, #2 \n" // 3/4*near+1/4*far (odd)
524 "vrshrn.u16 d0, q1, #2 \n" // 3/4*near+1/4*far (even)
525
526 "vst2.8 {d0, d1}, [%1]! \n" // store
527 "subs %2, %2, #16 \n" // 8 sample -> 16 sample
528 "bgt 1b \n"
529 : "+r"(src_ptr), // %0
530 "+r"(dst_ptr), // %1
531 "+r"(dst_width), // %2
532 "+r"(src_temp) // %3
533 :
534 : "memory", "cc", "q0", "q1", "q2", "q15" // Clobber List
535 );
536 }
537
ScaleRowUp2_Bilinear_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,int dst_width)538 void ScaleRowUp2_Bilinear_NEON(const uint8_t* src_ptr,
539 ptrdiff_t src_stride,
540 uint8_t* dst_ptr,
541 ptrdiff_t dst_stride,
542 int dst_width) {
543 const uint8_t* src_ptr1 = src_ptr + src_stride;
544 uint8_t* dst_ptr1 = dst_ptr + dst_stride;
545 const uint8_t* src_temp = src_ptr + 1;
546 const uint8_t* src_temp1 = src_ptr1 + 1;
547
548 asm volatile(
549 "vmov.u16 q15, #3 \n"
550 "vmov.u8 d28, #3 \n"
551
552 "1: \n"
553 "vld1.8 {d4}, [%0]! \n" // 01234567
554 "vld1.8 {d5}, [%5]! \n" // 12345678
555
556 "vmovl.u8 q0, d4 \n" // 01234567 (16b)
557 "vmovl.u8 q1, d5 \n" // 12345678 (16b)
558 "vmlal.u8 q0, d5, d28 \n" // 3*near+far (1, odd)
559 "vmlal.u8 q1, d4, d28 \n" // 3*near+far (1, even)
560
561 "vld1.8 {d8}, [%1]! \n"
562 "vld1.8 {d9}, [%6]! \n"
563
564 "vmovl.u8 q2, d8 \n"
565 "vmovl.u8 q3, d9 \n"
566 "vmlal.u8 q2, d9, d28 \n" // 3*near+far (2, odd)
567 "vmlal.u8 q3, d8, d28 \n" // 3*near+far (2, even)
568
569 // e o
570 // q1 q0
571 // q3 q2
572
573 "vmovq q4, q2 \n"
574 "vmovq q5, q3 \n"
575 "vmla.u16 q4, q0, q15 \n" // 9 3 3 1 (1, odd)
576 "vmla.u16 q5, q1, q15 \n" // 9 3 3 1 (1, even)
577 "vmla.u16 q0, q2, q15 \n" // 9 3 3 1 (2, odd)
578 "vmla.u16 q1, q3, q15 \n" // 9 3 3 1 (2, even)
579
580 // e o
581 // q5 q4
582 // q1 q0
583
584 "vrshrn.u16 d2, q1, #4 \n" // 2, even
585 "vrshrn.u16 d3, q0, #4 \n" // 2, odd
586 "vrshrn.u16 d0, q5, #4 \n" // 1, even
587 "vrshrn.u16 d1, q4, #4 \n" // 1, odd
588
589 "vst2.8 {d0, d1}, [%2]! \n" // store
590 "vst2.8 {d2, d3}, [%3]! \n" // store
591 "subs %4, %4, #16 \n" // 8 sample -> 16 sample
592 "bgt 1b \n"
593 : "+r"(src_ptr), // %0
594 "+r"(src_ptr1), // %1
595 "+r"(dst_ptr), // %2
596 "+r"(dst_ptr1), // %3
597 "+r"(dst_width), // %4
598 "+r"(src_temp), // %5
599 "+r"(src_temp1) // %6
600 :
601 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5", "d28",
602 "q15" // Clobber List
603 );
604 }
605
ScaleRowUp2_Linear_12_NEON(const uint16_t * src_ptr,uint16_t * dst_ptr,int dst_width)606 void ScaleRowUp2_Linear_12_NEON(const uint16_t* src_ptr,
607 uint16_t* dst_ptr,
608 int dst_width) {
609 const uint16_t* src_temp = src_ptr + 1;
610 asm volatile(
611 "vmov.u16 q15, #3 \n"
612
613 "1: \n"
614 "vld1.16 {q1}, [%0]! \n" // 01234567 (16b)
615 "vld1.16 {q0}, [%3]! \n" // 12345678 (16b)
616
617 "vmovq q2, q0 \n"
618 "vmla.u16 q0, q1, q15 \n" // 3*near+far (odd)
619 "vmla.u16 q1, q2, q15 \n" // 3*near+far (even)
620
621 "vrshr.u16 q0, q0, #2 \n" // 3/4*near+1/4*far (odd)
622 "vrshr.u16 q1, q1, #2 \n" // 3/4*near+1/4*far (even)
623
624 "vst2.16 {d0, d1, d2, d3}, [%1]! \n" // store
625 "subs %2, %2, #16 \n" // 8 sample -> 16 sample
626 "bgt 1b \n"
627 : "+r"(src_ptr), // %0
628 "+r"(dst_ptr), // %1
629 "+r"(dst_width), // %2
630 "+r"(src_temp) // %3
631 :
632 : "memory", "cc", "q0", "q1", "q2", "q15" // Clobber List
633 );
634 }
635
ScaleRowUp2_Bilinear_12_NEON(const uint16_t * src_ptr,ptrdiff_t src_stride,uint16_t * dst_ptr,ptrdiff_t dst_stride,int dst_width)636 void ScaleRowUp2_Bilinear_12_NEON(const uint16_t* src_ptr,
637 ptrdiff_t src_stride,
638 uint16_t* dst_ptr,
639 ptrdiff_t dst_stride,
640 int dst_width) {
641 const uint16_t* src_ptr1 = src_ptr + src_stride;
642 uint16_t* dst_ptr1 = dst_ptr + dst_stride;
643 const uint16_t* src_temp = src_ptr + 1;
644 const uint16_t* src_temp1 = src_ptr1 + 1;
645
646 asm volatile(
647 "vmov.u16 q15, #3 \n"
648
649 "1: \n"
650 "vld1.16 {q0}, [%0]! \n" // 01234567 (16b)
651 "vld1.16 {q1}, [%5]! \n" // 12345678 (16b)
652
653 "vmovq q2, q0 \n"
654 "vmla.u16 q0, q1, q15 \n" // 3*near+far (odd)
655 "vmla.u16 q1, q2, q15 \n" // 3*near+far (even)
656
657 "vld1.16 {q2}, [%1]! \n" // 01234567 (16b)
658 "vld1.16 {q3}, [%6]! \n" // 12345678 (16b)
659
660 "vmovq q4, q2 \n"
661 "vmla.u16 q2, q3, q15 \n" // 3*near+far (odd)
662 "vmla.u16 q3, q4, q15 \n" // 3*near+far (even)
663
664 "vmovq q4, q2 \n"
665 "vmovq q5, q3 \n"
666 "vmla.u16 q4, q0, q15 \n" // 9 3 3 1 (1, odd)
667 "vmla.u16 q5, q1, q15 \n" // 9 3 3 1 (1, even)
668 "vmla.u16 q0, q2, q15 \n" // 9 3 3 1 (2, odd)
669 "vmla.u16 q1, q3, q15 \n" // 9 3 3 1 (2, even)
670
671 "vrshr.u16 q2, q1, #4 \n" // 2, even
672 "vrshr.u16 q3, q0, #4 \n" // 2, odd
673 "vrshr.u16 q0, q5, #4 \n" // 1, even
674 "vrshr.u16 q1, q4, #4 \n" // 1, odd
675
676 "vst2.16 {d0, d1, d2, d3}, [%2]! \n" // store
677 "vst2.16 {d4, d5, d6, d7}, [%3]! \n" // store
678 "subs %4, %4, #16 \n" // 8 sample -> 16 sample
679 "bgt 1b \n"
680 : "+r"(src_ptr), // %0
681 "+r"(src_ptr1), // %1
682 "+r"(dst_ptr), // %2
683 "+r"(dst_ptr1), // %3
684 "+r"(dst_width), // %4
685 "+r"(src_temp), // %5
686 "+r"(src_temp1) // %6
687 :
688 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5",
689 "q15" // Clobber List
690 );
691 }
692
ScaleRowUp2_Linear_16_NEON(const uint16_t * src_ptr,uint16_t * dst_ptr,int dst_width)693 void ScaleRowUp2_Linear_16_NEON(const uint16_t* src_ptr,
694 uint16_t* dst_ptr,
695 int dst_width) {
696 const uint16_t* src_temp = src_ptr + 1;
697 asm volatile(
698 "vmov.u16 d31, #3 \n"
699
700 "1: \n"
701 "vld1.16 {q0}, [%0]! \n" // 01234567 (16b)
702 "vld1.16 {q1}, [%3]! \n" // 12345678 (16b)
703
704 "vmovl.u16 q2, d0 \n" // 0123 (32b)
705 "vmovl.u16 q3, d1 \n" // 4567 (32b)
706 "vmovl.u16 q4, d2 \n" // 1234 (32b)
707 "vmovl.u16 q5, d3 \n" // 5678 (32b)
708
709 "vmlal.u16 q2, d2, d31 \n"
710 "vmlal.u16 q3, d3, d31 \n"
711 "vmlal.u16 q4, d0, d31 \n"
712 "vmlal.u16 q5, d1, d31 \n"
713
714 "vrshrn.u32 d0, q4, #2 \n"
715 "vrshrn.u32 d1, q5, #2 \n"
716 "vrshrn.u32 d2, q2, #2 \n"
717 "vrshrn.u32 d3, q3, #2 \n"
718
719 "vst2.16 {q0, q1}, [%1]! \n" // store
720 "subs %2, %2, #16 \n" // 8 sample -> 16 sample
721 "bgt 1b \n"
722 : "+r"(src_ptr), // %0
723 "+r"(dst_ptr), // %1
724 "+r"(dst_width), // %2
725 "+r"(src_temp) // %3
726 :
727 : "memory", "cc", "q0", "q1", "q2", "q15" // Clobber List
728 );
729 }
730
ScaleRowUp2_Bilinear_16_NEON(const uint16_t * src_ptr,ptrdiff_t src_stride,uint16_t * dst_ptr,ptrdiff_t dst_stride,int dst_width)731 void ScaleRowUp2_Bilinear_16_NEON(const uint16_t* src_ptr,
732 ptrdiff_t src_stride,
733 uint16_t* dst_ptr,
734 ptrdiff_t dst_stride,
735 int dst_width) {
736 const uint16_t* src_ptr1 = src_ptr + src_stride;
737 uint16_t* dst_ptr1 = dst_ptr + dst_stride;
738 const uint16_t* src_temp = src_ptr + 1;
739 const uint16_t* src_temp1 = src_ptr1 + 1;
740
741 asm volatile(
742 "vmov.u16 d31, #3 \n"
743 "vmov.u32 q14, #3 \n"
744
745 "1: \n"
746 "vld1.16 {d0}, [%0]! \n" // 0123 (16b)
747 "vld1.16 {d1}, [%5]! \n" // 1234 (16b)
748 "vmovl.u16 q2, d0 \n" // 0123 (32b)
749 "vmovl.u16 q3, d1 \n" // 1234 (32b)
750 "vmlal.u16 q2, d1, d31 \n"
751 "vmlal.u16 q3, d0, d31 \n"
752
753 "vld1.16 {d0}, [%1]! \n" // 0123 (16b)
754 "vld1.16 {d1}, [%6]! \n" // 1234 (16b)
755 "vmovl.u16 q4, d0 \n" // 0123 (32b)
756 "vmovl.u16 q5, d1 \n" // 1234 (32b)
757 "vmlal.u16 q4, d1, d31 \n"
758 "vmlal.u16 q5, d0, d31 \n"
759
760 "vmovq q0, q4 \n"
761 "vmovq q1, q5 \n"
762 "vmla.u32 q4, q2, q14 \n"
763 "vmla.u32 q5, q3, q14 \n"
764 "vmla.u32 q2, q0, q14 \n"
765 "vmla.u32 q3, q1, q14 \n"
766
767 "vrshrn.u32 d1, q4, #4 \n"
768 "vrshrn.u32 d0, q5, #4 \n"
769 "vrshrn.u32 d3, q2, #4 \n"
770 "vrshrn.u32 d2, q3, #4 \n"
771
772 "vst2.16 {d0, d1}, [%2]! \n" // store
773 "vst2.16 {d2, d3}, [%3]! \n" // store
774 "subs %4, %4, #8 \n" // 4 sample -> 8 sample
775 "bgt 1b \n"
776 : "+r"(src_ptr), // %0
777 "+r"(src_ptr1), // %1
778 "+r"(dst_ptr), // %2
779 "+r"(dst_ptr1), // %3
780 "+r"(dst_width), // %4
781 "+r"(src_temp), // %5
782 "+r"(src_temp1) // %6
783 :
784 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q14",
785 "d31" // Clobber List
786 );
787 }
788
ScaleUVRowUp2_Linear_NEON(const uint8_t * src_ptr,uint8_t * dst_ptr,int dst_width)789 void ScaleUVRowUp2_Linear_NEON(const uint8_t* src_ptr,
790 uint8_t* dst_ptr,
791 int dst_width) {
792 const uint8_t* src_temp = src_ptr + 2;
793 asm volatile(
794 "vmov.u8 d30, #3 \n"
795
796 "1: \n"
797 "vld1.8 {d4}, [%0]! \n" // 00112233 (1u1v)
798 "vld1.8 {d5}, [%3]! \n" // 11223344 (1u1v)
799
800 "vmovl.u8 q0, d4 \n" // 00112233 (1u1v, 16b)
801 "vmovl.u8 q1, d5 \n" // 11223344 (1u1v, 16b)
802 "vmlal.u8 q0, d5, d30 \n" // 3*near+far (odd)
803 "vmlal.u8 q1, d4, d30 \n" // 3*near+far (even)
804
805 "vrshrn.u16 d1, q0, #2 \n" // 3/4*near+1/4*far (odd)
806 "vrshrn.u16 d0, q1, #2 \n" // 3/4*near+1/4*far (even)
807
808 "vst2.16 {d0, d1}, [%1]! \n" // store
809 "subs %2, %2, #8 \n" // 4 uv -> 8 uv
810 "bgt 1b \n"
811 : "+r"(src_ptr), // %0
812 "+r"(dst_ptr), // %1
813 "+r"(dst_width), // %2
814 "+r"(src_temp) // %3
815 :
816 : "memory", "cc", "q0", "q1", "q2", "d30" // Clobber List
817 );
818 }
819
ScaleUVRowUp2_Bilinear_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,int dst_width)820 void ScaleUVRowUp2_Bilinear_NEON(const uint8_t* src_ptr,
821 ptrdiff_t src_stride,
822 uint8_t* dst_ptr,
823 ptrdiff_t dst_stride,
824 int dst_width) {
825 const uint8_t* src_ptr1 = src_ptr + src_stride;
826 uint8_t* dst_ptr1 = dst_ptr + dst_stride;
827 const uint8_t* src_temp = src_ptr + 2;
828 const uint8_t* src_temp1 = src_ptr1 + 2;
829
830 asm volatile(
831 "vmov.u16 q15, #3 \n"
832 "vmov.u8 d28, #3 \n"
833
834 "1: \n"
835 "vld1.8 {d4}, [%0]! \n" // 00112233 (1u1v)
836 "vld1.8 {d5}, [%5]! \n" // 11223344 (1u1v)
837
838 "vmovl.u8 q0, d4 \n" // 00112233 (1u1v, 16b)
839 "vmovl.u8 q1, d5 \n" // 11223344 (1u1v, 16b)
840 "vmlal.u8 q0, d5, d28 \n" // 3*near+far (1, odd)
841 "vmlal.u8 q1, d4, d28 \n" // 3*near+far (1, even)
842
843 "vld1.8 {d8}, [%1]! \n" // 00112233 (1u1v)
844 "vld1.8 {d9}, [%6]! \n" // 11223344 (1u1v)
845
846 "vmovl.u8 q2, d8 \n" // 00112233 (1u1v, 16b)
847 "vmovl.u8 q3, d9 \n" // 11223344 (1u1v, 16b)
848 "vmlal.u8 q2, d9, d28 \n" // 3*near+far (2, odd)
849 "vmlal.u8 q3, d8, d28 \n" // 3*near+far (2, even)
850
851 // e o
852 // q1 q0
853 // q3 q2
854
855 "vmovq q4, q2 \n"
856 "vmovq q5, q3 \n"
857 "vmla.u16 q4, q0, q15 \n" // 9 3 3 1 (1, odd)
858 "vmla.u16 q5, q1, q15 \n" // 9 3 3 1 (1, even)
859 "vmla.u16 q0, q2, q15 \n" // 9 3 3 1 (2, odd)
860 "vmla.u16 q1, q3, q15 \n" // 9 3 3 1 (2, even)
861
862 // e o
863 // q5 q4
864 // q1 q0
865
866 "vrshrn.u16 d2, q1, #4 \n" // 2, even
867 "vrshrn.u16 d3, q0, #4 \n" // 2, odd
868 "vrshrn.u16 d0, q5, #4 \n" // 1, even
869 "vrshrn.u16 d1, q4, #4 \n" // 1, odd
870
871 "vst2.16 {d0, d1}, [%2]! \n" // store
872 "vst2.16 {d2, d3}, [%3]! \n" // store
873 "subs %4, %4, #8 \n" // 4 uv -> 8 uv
874 "bgt 1b \n"
875 : "+r"(src_ptr), // %0
876 "+r"(src_ptr1), // %1
877 "+r"(dst_ptr), // %2
878 "+r"(dst_ptr1), // %3
879 "+r"(dst_width), // %4
880 "+r"(src_temp), // %5
881 "+r"(src_temp1) // %6
882 :
883 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5", "d28",
884 "q15" // Clobber List
885 );
886 }
887
ScaleUVRowUp2_Linear_16_NEON(const uint16_t * src_ptr,uint16_t * dst_ptr,int dst_width)888 void ScaleUVRowUp2_Linear_16_NEON(const uint16_t* src_ptr,
889 uint16_t* dst_ptr,
890 int dst_width) {
891 const uint16_t* src_temp = src_ptr + 2;
892 asm volatile(
893 "vmov.u16 d30, #3 \n"
894
895 "1: \n"
896 "vld1.16 {q0}, [%0]! \n" // 00112233 (1u1v, 16)
897 "vld1.16 {q1}, [%3]! \n" // 11223344 (1u1v, 16)
898
899 "vmovl.u16 q2, d0 \n" // 0011 (1u1v, 32b)
900 "vmovl.u16 q3, d2 \n" // 1122 (1u1v, 32b)
901 "vmovl.u16 q4, d1 \n" // 2233 (1u1v, 32b)
902 "vmovl.u16 q5, d3 \n" // 3344 (1u1v, 32b)
903 "vmlal.u16 q2, d2, d30 \n" // 3*near+far (odd)
904 "vmlal.u16 q3, d0, d30 \n" // 3*near+far (even)
905 "vmlal.u16 q4, d3, d30 \n" // 3*near+far (odd)
906 "vmlal.u16 q5, d1, d30 \n" // 3*near+far (even)
907
908 "vrshrn.u32 d1, q2, #2 \n" // 3/4*near+1/4*far (odd)
909 "vrshrn.u32 d0, q3, #2 \n" // 3/4*near+1/4*far (even)
910 "vrshrn.u32 d3, q4, #2 \n" // 3/4*near+1/4*far (odd)
911 "vrshrn.u32 d2, q5, #2 \n" // 3/4*near+1/4*far (even)
912
913 "vst2.32 {d0, d1}, [%1]! \n" // store
914 "vst2.32 {d2, d3}, [%1]! \n" // store
915 "subs %2, %2, #8 \n" // 4 uv -> 8 uv
916 "bgt 1b \n"
917 : "+r"(src_ptr), // %0
918 "+r"(dst_ptr), // %1
919 "+r"(dst_width), // %2
920 "+r"(src_temp) // %3
921 :
922 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5",
923 "d30" // Clobber List
924 );
925 }
926
ScaleUVRowUp2_Bilinear_16_NEON(const uint16_t * src_ptr,ptrdiff_t src_stride,uint16_t * dst_ptr,ptrdiff_t dst_stride,int dst_width)927 void ScaleUVRowUp2_Bilinear_16_NEON(const uint16_t* src_ptr,
928 ptrdiff_t src_stride,
929 uint16_t* dst_ptr,
930 ptrdiff_t dst_stride,
931 int dst_width) {
932 const uint16_t* src_ptr1 = src_ptr + src_stride;
933 uint16_t* dst_ptr1 = dst_ptr + dst_stride;
934 const uint16_t* src_temp = src_ptr + 2;
935 const uint16_t* src_temp1 = src_ptr1 + 2;
936
937 asm volatile(
938 "vmov.u16 d30, #3 \n"
939 "vmov.u32 q14, #3 \n"
940
941 "1: \n"
942 "vld1.8 {d0}, [%0]! \n" // 0011 (1u1v)
943 "vld1.8 {d1}, [%5]! \n" // 1122 (1u1v)
944 "vmovl.u16 q2, d0 \n" // 0011 (1u1v, 32b)
945 "vmovl.u16 q3, d1 \n" // 1122 (1u1v, 32b)
946 "vmlal.u16 q2, d1, d30 \n" // 3*near+far (1, odd)
947 "vmlal.u16 q3, d0, d30 \n" // 3*near+far (1, even)
948
949 "vld1.8 {d0}, [%1]! \n" // 0011 (1u1v)
950 "vld1.8 {d1}, [%6]! \n" // 1122 (1u1v)
951 "vmovl.u16 q4, d0 \n" // 0011 (1u1v, 32b)
952 "vmovl.u16 q5, d1 \n" // 1122 (1u1v, 32b)
953 "vmlal.u16 q4, d1, d30 \n" // 3*near+far (2, odd)
954 "vmlal.u16 q5, d0, d30 \n" // 3*near+far (2, even)
955
956 "vmovq q0, q4 \n"
957 "vmovq q1, q5 \n"
958 "vmla.u32 q4, q2, q14 \n" // 9 3 3 1 (1, odd)
959 "vmla.u32 q5, q3, q14 \n" // 9 3 3 1 (1, even)
960 "vmla.u32 q2, q0, q14 \n" // 9 3 3 1 (2, odd)
961 "vmla.u32 q3, q1, q14 \n" // 9 3 3 1 (2, even)
962
963 "vrshrn.u32 d1, q4, #4 \n" // 1, odd
964 "vrshrn.u32 d0, q5, #4 \n" // 1, even
965 "vrshrn.u32 d3, q2, #4 \n" // 2, odd
966 "vrshrn.u32 d2, q3, #4 \n" // 2, even
967
968 "vst2.32 {d0, d1}, [%2]! \n" // store
969 "vst2.32 {d2, d3}, [%3]! \n" // store
970 "subs %4, %4, #4 \n" // 2 uv -> 4 uv
971 "bgt 1b \n"
972 : "+r"(src_ptr), // %0
973 "+r"(src_ptr1), // %1
974 "+r"(dst_ptr), // %2
975 "+r"(dst_ptr1), // %3
976 "+r"(dst_width), // %4
977 "+r"(src_temp), // %5
978 "+r"(src_temp1) // %6
979 :
980 : "memory", "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q14",
981 "d30" // Clobber List
982 );
983 }
984
985 // Add a row of bytes to a row of shorts. Used for box filter.
986 // Reads 16 bytes and accumulates to 16 shorts at a time.
ScaleAddRow_NEON(const uint8_t * src_ptr,uint16_t * dst_ptr,int src_width)987 void ScaleAddRow_NEON(const uint8_t* src_ptr,
988 uint16_t* dst_ptr,
989 int src_width) {
990 asm volatile(
991 "1: \n"
992 "vld1.16 {q1, q2}, [%1] \n" // load accumulator
993 "vld1.8 {q0}, [%0]! \n" // load 16 bytes
994 "vaddw.u8 q2, q2, d1 \n" // add
995 "vaddw.u8 q1, q1, d0 \n"
996 "vst1.16 {q1, q2}, [%1]! \n" // store accumulator
997 "subs %2, %2, #16 \n" // 16 processed per loop
998 "bgt 1b \n"
999 : "+r"(src_ptr), // %0
1000 "+r"(dst_ptr), // %1
1001 "+r"(src_width) // %2
1002 :
1003 : "memory", "cc", "q0", "q1", "q2" // Clobber List
1004 );
1005 }
1006
1007 // TODO(Yang Zhang): Investigate less load instructions for
1008 // the x/dx stepping
1009 #define LOAD2_DATA8_LANE(n) \
1010 "lsr %5, %3, #16 \n" \
1011 "add %6, %1, %5 \n" \
1012 "add %3, %3, %4 \n" \
1013 "vld2.8 {d6[" #n "], d7[" #n "]}, [%6] \n"
1014
1015 // The NEON version mimics this formula (from row_common.cc):
1016 // #define BLENDER(a, b, f) (uint8_t)((int)(a) +
1017 // ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16))
1018
ScaleFilterCols_NEON(uint8_t * dst_ptr,const uint8_t * src_ptr,int dst_width,int x,int dx)1019 void ScaleFilterCols_NEON(uint8_t* dst_ptr,
1020 const uint8_t* src_ptr,
1021 int dst_width,
1022 int x,
1023 int dx) {
1024 int dx_offset[4] = {0, 1, 2, 3};
1025 int* tmp = dx_offset;
1026 const uint8_t* src_tmp = src_ptr;
1027 asm volatile (
1028 "vdup.32 q0, %3 \n" // x
1029 "vdup.32 q1, %4 \n" // dx
1030 "vld1.32 {q2}, [%5] \n" // 0 1 2 3
1031 "vshl.i32 q3, q1, #2 \n" // 4 * dx
1032 "vmul.s32 q1, q1, q2 \n"
1033 // x , x + 1 * dx, x + 2 * dx, x + 3 * dx
1034 "vadd.s32 q1, q1, q0 \n"
1035 // x + 4 * dx, x + 5 * dx, x + 6 * dx, x + 7 * dx
1036 "vadd.s32 q2, q1, q3 \n"
1037 "vshl.i32 q0, q3, #1 \n" // 8 * dx
1038 "1: \n"
1039 LOAD2_DATA8_LANE(0)
1040 LOAD2_DATA8_LANE(1)
1041 LOAD2_DATA8_LANE(2)
1042 LOAD2_DATA8_LANE(3)
1043 LOAD2_DATA8_LANE(4)
1044 LOAD2_DATA8_LANE(5)
1045 LOAD2_DATA8_LANE(6)
1046 LOAD2_DATA8_LANE(7)
1047 "vmov q10, q1 \n"
1048 "vmov q11, q2 \n"
1049 "vuzp.16 q10, q11 \n"
1050 "vmovl.u8 q8, d6 \n"
1051 "vmovl.u8 q9, d7 \n"
1052 "vsubl.s16 q11, d18, d16 \n"
1053 "vsubl.s16 q12, d19, d17 \n"
1054 "vmovl.u16 q13, d20 \n"
1055 "vmovl.u16 q10, d21 \n"
1056 "vmul.s32 q11, q11, q13 \n"
1057 "vmul.s32 q12, q12, q10 \n"
1058 "vrshrn.s32 d18, q11, #16 \n"
1059 "vrshrn.s32 d19, q12, #16 \n"
1060 "vadd.s16 q8, q8, q9 \n"
1061 "vmovn.s16 d6, q8 \n"
1062
1063 "vst1.8 {d6}, [%0]! \n" // store pixels
1064 "vadd.s32 q1, q1, q0 \n"
1065 "vadd.s32 q2, q2, q0 \n"
1066 "subs %2, %2, #8 \n" // 8 processed per loop
1067 "bgt 1b \n"
1068 : "+r"(dst_ptr), // %0
1069 "+r"(src_ptr), // %1
1070 "+r"(dst_width), // %2
1071 "+r"(x), // %3
1072 "+r"(dx), // %4
1073 "+r"(tmp), // %5
1074 "+r"(src_tmp) // %6
1075 :
1076 : "memory", "cc", "q0", "q1", "q2", "q3",
1077 "q8", "q9", "q10", "q11", "q12", "q13"
1078 );
1079 }
1080
1081 #undef LOAD2_DATA8_LANE
1082
1083 // 16x2 -> 16x1
ScaleFilterRows_NEON(uint8_t * dst_ptr,const uint8_t * src_ptr,ptrdiff_t src_stride,int dst_width,int source_y_fraction)1084 void ScaleFilterRows_NEON(uint8_t* dst_ptr,
1085 const uint8_t* src_ptr,
1086 ptrdiff_t src_stride,
1087 int dst_width,
1088 int source_y_fraction) {
1089 asm volatile(
1090 "cmp %4, #0 \n"
1091 "beq 100f \n"
1092 "add %2, %1 \n"
1093 "cmp %4, #64 \n"
1094 "beq 75f \n"
1095 "cmp %4, #128 \n"
1096 "beq 50f \n"
1097 "cmp %4, #192 \n"
1098 "beq 25f \n"
1099
1100 "vdup.8 d5, %4 \n"
1101 "rsb %4, #256 \n"
1102 "vdup.8 d4, %4 \n"
1103 // General purpose row blend.
1104 "1: \n"
1105 "vld1.8 {q0}, [%1]! \n"
1106 "vld1.8 {q1}, [%2]! \n"
1107 "subs %3, %3, #16 \n"
1108 "vmull.u8 q13, d0, d4 \n"
1109 "vmull.u8 q14, d1, d4 \n"
1110 "vmlal.u8 q13, d2, d5 \n"
1111 "vmlal.u8 q14, d3, d5 \n"
1112 "vrshrn.u16 d0, q13, #8 \n"
1113 "vrshrn.u16 d1, q14, #8 \n"
1114 "vst1.8 {q0}, [%0]! \n"
1115 "bgt 1b \n"
1116 "b 99f \n"
1117
1118 // Blend 25 / 75.
1119 "25: \n"
1120 "vld1.8 {q0}, [%1]! \n"
1121 "vld1.8 {q1}, [%2]! \n"
1122 "subs %3, %3, #16 \n"
1123 "vrhadd.u8 q0, q1 \n"
1124 "vrhadd.u8 q0, q1 \n"
1125 "vst1.8 {q0}, [%0]! \n"
1126 "bgt 25b \n"
1127 "b 99f \n"
1128
1129 // Blend 50 / 50.
1130 "50: \n"
1131 "vld1.8 {q0}, [%1]! \n"
1132 "vld1.8 {q1}, [%2]! \n"
1133 "subs %3, %3, #16 \n"
1134 "vrhadd.u8 q0, q1 \n"
1135 "vst1.8 {q0}, [%0]! \n"
1136 "bgt 50b \n"
1137 "b 99f \n"
1138
1139 // Blend 75 / 25.
1140 "75: \n"
1141 "vld1.8 {q1}, [%1]! \n"
1142 "vld1.8 {q0}, [%2]! \n"
1143 "subs %3, %3, #16 \n"
1144 "vrhadd.u8 q0, q1 \n"
1145 "vrhadd.u8 q0, q1 \n"
1146 "vst1.8 {q0}, [%0]! \n"
1147 "bgt 75b \n"
1148 "b 99f \n"
1149
1150 // Blend 100 / 0 - Copy row unchanged.
1151 "100: \n"
1152 "vld1.8 {q0}, [%1]! \n"
1153 "subs %3, %3, #16 \n"
1154 "vst1.8 {q0}, [%0]! \n"
1155 "bgt 100b \n"
1156
1157 "99: \n"
1158 "vst1.8 {d1[7]}, [%0] \n"
1159 : "+r"(dst_ptr), // %0
1160 "+r"(src_ptr), // %1
1161 "+r"(src_stride), // %2
1162 "+r"(dst_width), // %3
1163 "+r"(source_y_fraction) // %4
1164 :
1165 : "q0", "q1", "d4", "d5", "q13", "q14", "memory", "cc");
1166 }
1167
ScaleARGBRowDown2_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)1168 void ScaleARGBRowDown2_NEON(const uint8_t* src_ptr,
1169 ptrdiff_t src_stride,
1170 uint8_t* dst,
1171 int dst_width) {
1172 (void)src_stride;
1173 asm volatile(
1174 "1: \n"
1175 "vld4.32 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
1176 "vld4.32 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
1177 "subs %2, %2, #8 \n" // 8 processed per loop
1178 "vmov q2, q1 \n" // load next 8 ARGB
1179 "vst2.32 {q2, q3}, [%1]! \n" // store odd pixels
1180 "bgt 1b \n"
1181 : "+r"(src_ptr), // %0
1182 "+r"(dst), // %1
1183 "+r"(dst_width) // %2
1184 :
1185 : "memory", "cc", "q0", "q1", "q2", "q3" // Clobber List
1186 );
1187 }
1188
1189 // 46: f964 018d vld4.32 {d16,d18,d20,d22}, [r4]!
1190 // 4a: 3e04 subs r6, #4
1191 // 4c: f964 118d vld4.32 {d17,d19,d21,d23}, [r4]!
1192 // 50: ef64 21f4 vorr q9, q10, q10
1193 // 54: f942 038d vst2.32 {d16-d19}, [r2]!
1194 // 58: d1f5 bne.n 46 <ScaleARGBRowDown2_C+0x46>
1195
ScaleARGBRowDown2Linear_NEON(const uint8_t * src_argb,ptrdiff_t src_stride,uint8_t * dst_argb,int dst_width)1196 void ScaleARGBRowDown2Linear_NEON(const uint8_t* src_argb,
1197 ptrdiff_t src_stride,
1198 uint8_t* dst_argb,
1199 int dst_width) {
1200 (void)src_stride;
1201 asm volatile(
1202 "1: \n"
1203 "vld4.32 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
1204 "vld4.32 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
1205 "subs %2, %2, #8 \n" // 8 processed per loop
1206 "vrhadd.u8 q0, q0, q1 \n" // rounding half add
1207 "vrhadd.u8 q1, q2, q3 \n" // rounding half add
1208 "vst2.32 {q0, q1}, [%1]! \n"
1209 "bgt 1b \n"
1210 : "+r"(src_argb), // %0
1211 "+r"(dst_argb), // %1
1212 "+r"(dst_width) // %2
1213 :
1214 : "memory", "cc", "q0", "q1", "q2", "q3" // Clobber List
1215 );
1216 }
1217
ScaleARGBRowDown2Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)1218 void ScaleARGBRowDown2Box_NEON(const uint8_t* src_ptr,
1219 ptrdiff_t src_stride,
1220 uint8_t* dst,
1221 int dst_width) {
1222 asm volatile(
1223 // change the stride to row 2 pointer
1224 "add %1, %1, %0 \n"
1225 "1: \n"
1226 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
1227 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
1228 "subs %3, %3, #8 \n" // 8 processed per loop.
1229 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
1230 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
1231 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
1232 "vpaddl.u8 q3, q3 \n" // A 16 bytes -> 8 shorts.
1233 "vld4.8 {d16, d18, d20, d22}, [%1]! \n" // load 8 more ARGB
1234 "vld4.8 {d17, d19, d21, d23}, [%1]! \n" // load last 8 ARGB
1235 "vpadal.u8 q0, q8 \n" // B 16 bytes -> 8 shorts.
1236 "vpadal.u8 q1, q9 \n" // G 16 bytes -> 8 shorts.
1237 "vpadal.u8 q2, q10 \n" // R 16 bytes -> 8 shorts.
1238 "vpadal.u8 q3, q11 \n" // A 16 bytes -> 8 shorts.
1239 "vrshrn.u16 d0, q0, #2 \n" // round and pack to bytes
1240 "vrshrn.u16 d1, q1, #2 \n"
1241 "vrshrn.u16 d2, q2, #2 \n"
1242 "vrshrn.u16 d3, q3, #2 \n"
1243 "vst4.8 {d0, d1, d2, d3}, [%2]! \n"
1244 "bgt 1b \n"
1245 : "+r"(src_ptr), // %0
1246 "+r"(src_stride), // %1
1247 "+r"(dst), // %2
1248 "+r"(dst_width) // %3
1249 :
1250 : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11");
1251 }
1252
1253 // Reads 4 pixels at a time.
1254 // Alignment requirement: src_argb 4 byte aligned.
ScaleARGBRowDownEven_NEON(const uint8_t * src_argb,ptrdiff_t src_stride,int src_stepx,uint8_t * dst_argb,int dst_width)1255 void ScaleARGBRowDownEven_NEON(const uint8_t* src_argb,
1256 ptrdiff_t src_stride,
1257 int src_stepx,
1258 uint8_t* dst_argb,
1259 int dst_width) {
1260 (void)src_stride;
1261 asm volatile(
1262 "mov r12, %3, lsl #2 \n"
1263 "1: \n"
1264 "vld1.32 {d0[0]}, [%0], r12 \n"
1265 "vld1.32 {d0[1]}, [%0], r12 \n"
1266 "vld1.32 {d1[0]}, [%0], r12 \n"
1267 "vld1.32 {d1[1]}, [%0], r12 \n"
1268 "subs %2, %2, #4 \n" // 4 pixels per loop.
1269 "vst1.8 {q0}, [%1]! \n"
1270 "bgt 1b \n"
1271 : "+r"(src_argb), // %0
1272 "+r"(dst_argb), // %1
1273 "+r"(dst_width) // %2
1274 : "r"(src_stepx) // %3
1275 : "memory", "cc", "r12", "q0");
1276 }
1277
1278 // Reads 4 pixels at a time.
1279 // Alignment requirement: src_argb 4 byte aligned.
ScaleARGBRowDownEvenBox_NEON(const uint8_t * src_argb,ptrdiff_t src_stride,int src_stepx,uint8_t * dst_argb,int dst_width)1280 void ScaleARGBRowDownEvenBox_NEON(const uint8_t* src_argb,
1281 ptrdiff_t src_stride,
1282 int src_stepx,
1283 uint8_t* dst_argb,
1284 int dst_width) {
1285 asm volatile(
1286 "mov r12, %4, lsl #2 \n"
1287 "add %1, %1, %0 \n"
1288 "1: \n"
1289 "vld1.8 {d0}, [%0], r12 \n" // 4 2x2 blocks -> 2x1
1290 "vld1.8 {d1}, [%1], r12 \n"
1291 "vld1.8 {d2}, [%0], r12 \n"
1292 "vld1.8 {d3}, [%1], r12 \n"
1293 "vld1.8 {d4}, [%0], r12 \n"
1294 "vld1.8 {d5}, [%1], r12 \n"
1295 "vld1.8 {d6}, [%0], r12 \n"
1296 "vld1.8 {d7}, [%1], r12 \n"
1297 "vaddl.u8 q0, d0, d1 \n"
1298 "vaddl.u8 q1, d2, d3 \n"
1299 "vaddl.u8 q2, d4, d5 \n"
1300 "vaddl.u8 q3, d6, d7 \n"
1301 "vswp.8 d1, d2 \n" // ab_cd -> ac_bd
1302 "vswp.8 d5, d6 \n" // ef_gh -> eg_fh
1303 "vadd.u16 q0, q0, q1 \n" // (a+b)_(c+d)
1304 "vadd.u16 q2, q2, q3 \n" // (e+f)_(g+h)
1305 "vrshrn.u16 d0, q0, #2 \n" // first 2 pixels.
1306 "vrshrn.u16 d1, q2, #2 \n" // next 2 pixels.
1307 "subs %3, %3, #4 \n" // 4 pixels per loop.
1308 "vst1.8 {q0}, [%2]! \n"
1309 "bgt 1b \n"
1310 : "+r"(src_argb), // %0
1311 "+r"(src_stride), // %1
1312 "+r"(dst_argb), // %2
1313 "+r"(dst_width) // %3
1314 : "r"(src_stepx) // %4
1315 : "memory", "cc", "r12", "q0", "q1", "q2", "q3");
1316 }
1317
1318 // TODO(Yang Zhang): Investigate less load instructions for
1319 // the x/dx stepping
1320 #define LOAD1_DATA32_LANE(dn, n) \
1321 "lsr %5, %3, #16 \n" \
1322 "add %6, %1, %5, lsl #2 \n" \
1323 "add %3, %3, %4 \n" \
1324 "vld1.32 {" #dn "[" #n "]}, [%6] \n"
1325
ScaleARGBCols_NEON(uint8_t * dst_argb,const uint8_t * src_argb,int dst_width,int x,int dx)1326 void ScaleARGBCols_NEON(uint8_t* dst_argb,
1327 const uint8_t* src_argb,
1328 int dst_width,
1329 int x,
1330 int dx) {
1331 int tmp;
1332 const uint8_t* src_tmp = src_argb;
1333 asm volatile(
1334 "1: \n"
1335 // clang-format off
1336 LOAD1_DATA32_LANE(d0, 0)
1337 LOAD1_DATA32_LANE(d0, 1)
1338 LOAD1_DATA32_LANE(d1, 0)
1339 LOAD1_DATA32_LANE(d1, 1)
1340 LOAD1_DATA32_LANE(d2, 0)
1341 LOAD1_DATA32_LANE(d2, 1)
1342 LOAD1_DATA32_LANE(d3, 0)
1343 LOAD1_DATA32_LANE(d3, 1)
1344 // clang-format on
1345 "vst1.32 {q0, q1}, [%0]! \n" // store pixels
1346 "subs %2, %2, #8 \n" // 8 processed per loop
1347 "bgt 1b \n"
1348 : "+r"(dst_argb), // %0
1349 "+r"(src_argb), // %1
1350 "+r"(dst_width), // %2
1351 "+r"(x), // %3
1352 "+r"(dx), // %4
1353 "=&r"(tmp), // %5
1354 "+r"(src_tmp) // %6
1355 :
1356 : "memory", "cc", "q0", "q1");
1357 }
1358
1359 #undef LOAD1_DATA32_LANE
1360
1361 // TODO(Yang Zhang): Investigate less load instructions for
1362 // the x/dx stepping
1363 #define LOAD2_DATA32_LANE(dn1, dn2, n) \
1364 "lsr %5, %3, #16 \n" \
1365 "add %6, %1, %5, lsl #2 \n" \
1366 "add %3, %3, %4 \n" \
1367 "vld2.32 {" #dn1 "[" #n "], " #dn2 "[" #n "]}, [%6] \n"
1368
ScaleARGBFilterCols_NEON(uint8_t * dst_argb,const uint8_t * src_argb,int dst_width,int x,int dx)1369 void ScaleARGBFilterCols_NEON(uint8_t* dst_argb,
1370 const uint8_t* src_argb,
1371 int dst_width,
1372 int x,
1373 int dx) {
1374 int dx_offset[4] = {0, 1, 2, 3};
1375 int* tmp = dx_offset;
1376 const uint8_t* src_tmp = src_argb;
1377 asm volatile (
1378 "vdup.32 q0, %3 \n" // x
1379 "vdup.32 q1, %4 \n" // dx
1380 "vld1.32 {q2}, [%5] \n" // 0 1 2 3
1381 "vshl.i32 q9, q1, #2 \n" // 4 * dx
1382 "vmul.s32 q1, q1, q2 \n"
1383 "vmov.i8 q3, #0x7f \n" // 0x7F
1384 "vmov.i16 q15, #0x7f \n" // 0x7F
1385 // x , x + 1 * dx, x + 2 * dx, x + 3 * dx
1386 "vadd.s32 q8, q1, q0 \n"
1387 "1: \n"
1388 // d0, d1: a
1389 // d2, d3: b
1390 LOAD2_DATA32_LANE(d0, d2, 0)
1391 LOAD2_DATA32_LANE(d0, d2, 1)
1392 LOAD2_DATA32_LANE(d1, d3, 0)
1393 LOAD2_DATA32_LANE(d1, d3, 1)
1394 "vshrn.i32 d22, q8, #9 \n"
1395 "vand.16 d22, d22, d30 \n"
1396 "vdup.8 d24, d22[0] \n"
1397 "vdup.8 d25, d22[2] \n"
1398 "vdup.8 d26, d22[4] \n"
1399 "vdup.8 d27, d22[6] \n"
1400 "vext.8 d4, d24, d25, #4 \n"
1401 "vext.8 d5, d26, d27, #4 \n" // f
1402 "veor.8 q10, q2, q3 \n" // 0x7f ^ f
1403 "vmull.u8 q11, d0, d20 \n"
1404 "vmull.u8 q12, d1, d21 \n"
1405 "vmull.u8 q13, d2, d4 \n"
1406 "vmull.u8 q14, d3, d5 \n"
1407 "vadd.i16 q11, q11, q13 \n"
1408 "vadd.i16 q12, q12, q14 \n"
1409 "vshrn.i16 d0, q11, #7 \n"
1410 "vshrn.i16 d1, q12, #7 \n"
1411
1412 "vst1.32 {d0, d1}, [%0]! \n" // store pixels
1413 "vadd.s32 q8, q8, q9 \n"
1414 "subs %2, %2, #4 \n" // 4 processed per loop
1415 "bgt 1b \n"
1416 : "+r"(dst_argb), // %0
1417 "+r"(src_argb), // %1
1418 "+r"(dst_width), // %2
1419 "+r"(x), // %3
1420 "+r"(dx), // %4
1421 "+r"(tmp), // %5
1422 "+r"(src_tmp) // %6
1423 :
1424 : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9",
1425 "q10", "q11", "q12", "q13", "q14", "q15"
1426 );
1427 }
1428
1429 #undef LOAD2_DATA32_LANE
1430
ScaleUVRowDown2_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)1431 void ScaleUVRowDown2_NEON(const uint8_t* src_ptr,
1432 ptrdiff_t src_stride,
1433 uint8_t* dst,
1434 int dst_width) {
1435 (void)src_stride;
1436 asm volatile(
1437 "1: \n"
1438 "vld2.16 {d0, d2}, [%0]! \n" // load 8 UV pixels.
1439 "vld2.16 {d1, d3}, [%0]! \n" // load next 8 UV
1440 "subs %2, %2, #8 \n" // 8 processed per loop.
1441 "vst1.16 {q1}, [%1]! \n" // store 8 UV
1442 "bgt 1b \n"
1443 : "+r"(src_ptr), // %0
1444 "+r"(dst), // %1
1445 "+r"(dst_width) // %2
1446 :
1447 : "memory", "cc", "q0", "q1");
1448 }
1449
ScaleUVRowDown2Linear_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)1450 void ScaleUVRowDown2Linear_NEON(const uint8_t* src_ptr,
1451 ptrdiff_t src_stride,
1452 uint8_t* dst,
1453 int dst_width) {
1454 (void)src_stride;
1455 asm volatile(
1456 "1: \n"
1457 "vld2.16 {d0, d2}, [%0]! \n" // load 8 UV pixels.
1458 "vld2.16 {d1, d3}, [%0]! \n" // load next 8 UV
1459 "subs %2, %2, #8 \n" // 8 processed per loop.
1460 "vrhadd.u8 q0, q0, q1 \n" // rounding half add
1461 "vst1.16 {q0}, [%1]! \n" // store 8 UV
1462 "bgt 1b \n"
1463 : "+r"(src_ptr), // %0
1464 "+r"(dst), // %1
1465 "+r"(dst_width) // %2
1466 :
1467 : "memory", "cc", "q0", "q1");
1468 }
1469
ScaleUVRowDown2Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)1470 void ScaleUVRowDown2Box_NEON(const uint8_t* src_ptr,
1471 ptrdiff_t src_stride,
1472 uint8_t* dst,
1473 int dst_width) {
1474 asm volatile(
1475 // change the stride to row 2 pointer
1476 "add %1, %1, %0 \n"
1477 "1: \n"
1478 "vld2.8 {d0, d2}, [%0]! \n" // load 8 UV pixels.
1479 "vld2.8 {d1, d3}, [%0]! \n" // load next 8 UV
1480 "subs %3, %3, #8 \n" // 8 processed per loop.
1481 "vpaddl.u8 q0, q0 \n" // U 16 bytes -> 8 shorts.
1482 "vpaddl.u8 q1, q1 \n" // V 16 bytes -> 8 shorts.
1483 "vld2.8 {d16, d18}, [%1]! \n" // load 8 more UV
1484 "vld2.8 {d17, d19}, [%1]! \n" // load last 8 UV
1485 "vpadal.u8 q0, q8 \n" // U 16 bytes -> 8 shorts.
1486 "vpadal.u8 q1, q9 \n" // V 16 bytes -> 8 shorts.
1487 "vrshrn.u16 d0, q0, #2 \n" // round and pack to bytes
1488 "vrshrn.u16 d1, q1, #2 \n"
1489 "vst2.8 {d0, d1}, [%2]! \n"
1490 "bgt 1b \n"
1491 : "+r"(src_ptr), // %0
1492 "+r"(src_stride), // %1
1493 "+r"(dst), // %2
1494 "+r"(dst_width) // %3
1495 :
1496 : "memory", "cc", "q0", "q1", "q8", "q9");
1497 }
1498
1499 // Reads 4 pixels at a time.
ScaleUVRowDownEven_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,int src_stepx,uint8_t * dst_ptr,int dst_width)1500 void ScaleUVRowDownEven_NEON(const uint8_t* src_ptr,
1501 ptrdiff_t src_stride,
1502 int src_stepx, // pixel step
1503 uint8_t* dst_ptr,
1504 int dst_width) {
1505 const uint8_t* src1_ptr = src_ptr + src_stepx * 2;
1506 const uint8_t* src2_ptr = src_ptr + src_stepx * 4;
1507 const uint8_t* src3_ptr = src_ptr + src_stepx * 6;
1508 (void)src_stride;
1509 asm volatile(
1510 "1: \n"
1511 "vld1.16 {d0[0]}, [%0], %6 \n"
1512 "vld1.16 {d0[1]}, [%1], %6 \n"
1513 "vld1.16 {d0[2]}, [%2], %6 \n"
1514 "vld1.16 {d0[3]}, [%3], %6 \n"
1515 "subs %5, %5, #4 \n" // 4 pixels per loop.
1516 "vst1.8 {d0}, [%4]! \n"
1517 "bgt 1b \n"
1518 : "+r"(src_ptr), // %0
1519 "+r"(src1_ptr), // %1
1520 "+r"(src2_ptr), // %2
1521 "+r"(src3_ptr), // %3
1522 "+r"(dst_ptr), // %4
1523 "+r"(dst_width) // %5
1524 : "r"(src_stepx * 8) // %6
1525 : "memory", "cc", "d0");
1526 }
1527
1528 #endif // defined(__ARM_NEON__) && !defined(__aarch64__)
1529
1530 #ifdef __cplusplus
1531 } // extern "C"
1532 } // namespace libyuv
1533 #endif
1534