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
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
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
209 "vrhadd.u8 d1, d1, d2 \n"
210
211 // a2 = (src[2] * 1 + s[3] * 3) >> 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
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
249 "vrhadd.u8 d1, d1, d2 \n"
250
251 // a2 = (src[2] * 1 + s[3] * 3) >> 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
ScaleAddRows_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint16_t * dst_ptr,int src_width,int src_height)507 void ScaleAddRows_NEON(const uint8_t* src_ptr,
508 ptrdiff_t src_stride,
509 uint16_t* dst_ptr,
510 int src_width,
511 int src_height) {
512 const uint8_t* src_tmp;
513 asm volatile(
514 "1: \n"
515 "mov %0, %1 \n"
516 "mov r12, %5 \n"
517 "veor q2, q2, q2 \n"
518 "veor q3, q3, q3 \n"
519 "2: \n"
520 // load 16 pixels into q0
521 "vld1.8 {q0}, [%0], %3 \n"
522 "vaddw.u8 q3, q3, d1 \n"
523 "vaddw.u8 q2, q2, d0 \n"
524 "subs r12, r12, #1 \n"
525 "bgt 2b \n"
526 "vst1.16 {q2, q3}, [%2]! \n" // store pixels
527 "add %1, %1, #16 \n"
528 "subs %4, %4, #16 \n" // 16 processed per loop
529 "bgt 1b \n"
530 : "=&r"(src_tmp), // %0
531 "+r"(src_ptr), // %1
532 "+r"(dst_ptr), // %2
533 "+r"(src_stride), // %3
534 "+r"(src_width), // %4
535 "+r"(src_height) // %5
536 :
537 : "memory", "cc", "r12", "q0", "q1", "q2", "q3" // Clobber List
538 );
539 }
540
541 // TODO(Yang Zhang): Investigate less load instructions for
542 // the x/dx stepping
543 #define LOAD2_DATA8_LANE(n) \
544 "lsr %5, %3, #16 \n" \
545 "add %6, %1, %5 \n" \
546 "add %3, %3, %4 \n" \
547 "vld2.8 {d6[" #n "], d7[" #n "]}, [%6] \n"
548
549 // The NEON version mimics this formula (from row_common.cc):
550 // #define BLENDER(a, b, f) (uint8_t)((int)(a) +
551 // ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16))
552
ScaleFilterCols_NEON(uint8_t * dst_ptr,const uint8_t * src_ptr,int dst_width,int x,int dx)553 void ScaleFilterCols_NEON(uint8_t* dst_ptr,
554 const uint8_t* src_ptr,
555 int dst_width,
556 int x,
557 int dx) {
558 int dx_offset[4] = {0, 1, 2, 3};
559 int* tmp = dx_offset;
560 const uint8_t* src_tmp = src_ptr;
561 asm volatile (
562 "vdup.32 q0, %3 \n" // x
563 "vdup.32 q1, %4 \n" // dx
564 "vld1.32 {q2}, [%5] \n" // 0 1 2 3
565 "vshl.i32 q3, q1, #2 \n" // 4 * dx
566 "vmul.s32 q1, q1, q2 \n"
567 // x , x + 1 * dx, x + 2 * dx, x + 3 * dx
568 "vadd.s32 q1, q1, q0 \n"
569 // x + 4 * dx, x + 5 * dx, x + 6 * dx, x + 7 * dx
570 "vadd.s32 q2, q1, q3 \n"
571 "vshl.i32 q0, q3, #1 \n" // 8 * dx
572 "1: \n"
573 LOAD2_DATA8_LANE(0)
574 LOAD2_DATA8_LANE(1)
575 LOAD2_DATA8_LANE(2)
576 LOAD2_DATA8_LANE(3)
577 LOAD2_DATA8_LANE(4)
578 LOAD2_DATA8_LANE(5)
579 LOAD2_DATA8_LANE(6)
580 LOAD2_DATA8_LANE(7)
581 "vmov q10, q1 \n"
582 "vmov q11, q2 \n"
583 "vuzp.16 q10, q11 \n"
584 "vmovl.u8 q8, d6 \n"
585 "vmovl.u8 q9, d7 \n"
586 "vsubl.s16 q11, d18, d16 \n"
587 "vsubl.s16 q12, d19, d17 \n"
588 "vmovl.u16 q13, d20 \n"
589 "vmovl.u16 q10, d21 \n"
590 "vmul.s32 q11, q11, q13 \n"
591 "vmul.s32 q12, q12, q10 \n"
592 "vrshrn.s32 d18, q11, #16 \n"
593 "vrshrn.s32 d19, q12, #16 \n"
594 "vadd.s16 q8, q8, q9 \n"
595 "vmovn.s16 d6, q8 \n"
596
597 "vst1.8 {d6}, [%0]! \n" // store pixels
598 "vadd.s32 q1, q1, q0 \n"
599 "vadd.s32 q2, q2, q0 \n"
600 "subs %2, %2, #8 \n" // 8 processed per loop
601 "bgt 1b \n"
602 : "+r"(dst_ptr), // %0
603 "+r"(src_ptr), // %1
604 "+r"(dst_width), // %2
605 "+r"(x), // %3
606 "+r"(dx), // %4
607 "+r"(tmp), // %5
608 "+r"(src_tmp) // %6
609 :
610 : "memory", "cc", "q0", "q1", "q2", "q3",
611 "q8", "q9", "q10", "q11", "q12", "q13"
612 );
613 }
614
615 #undef LOAD2_DATA8_LANE
616
617 // 16x2 -> 16x1
ScaleFilterRows_NEON(uint8_t * dst_ptr,const uint8_t * src_ptr,ptrdiff_t src_stride,int dst_width,int source_y_fraction)618 void ScaleFilterRows_NEON(uint8_t* dst_ptr,
619 const uint8_t* src_ptr,
620 ptrdiff_t src_stride,
621 int dst_width,
622 int source_y_fraction) {
623 asm volatile(
624 "cmp %4, #0 \n"
625 "beq 100f \n"
626 "add %2, %1 \n"
627 "cmp %4, #64 \n"
628 "beq 75f \n"
629 "cmp %4, #128 \n"
630 "beq 50f \n"
631 "cmp %4, #192 \n"
632 "beq 25f \n"
633
634 "vdup.8 d5, %4 \n"
635 "rsb %4, #256 \n"
636 "vdup.8 d4, %4 \n"
637 // General purpose row blend.
638 "1: \n"
639 "vld1.8 {q0}, [%1]! \n"
640 "vld1.8 {q1}, [%2]! \n"
641 "subs %3, %3, #16 \n"
642 "vmull.u8 q13, d0, d4 \n"
643 "vmull.u8 q14, d1, d4 \n"
644 "vmlal.u8 q13, d2, d5 \n"
645 "vmlal.u8 q14, d3, d5 \n"
646 "vrshrn.u16 d0, q13, #8 \n"
647 "vrshrn.u16 d1, q14, #8 \n"
648 "vst1.8 {q0}, [%0]! \n"
649 "bgt 1b \n"
650 "b 99f \n"
651
652 // Blend 25 / 75.
653 "25: \n"
654 "vld1.8 {q0}, [%1]! \n"
655 "vld1.8 {q1}, [%2]! \n"
656 "subs %3, %3, #16 \n"
657 "vrhadd.u8 q0, q1 \n"
658 "vrhadd.u8 q0, q1 \n"
659 "vst1.8 {q0}, [%0]! \n"
660 "bgt 25b \n"
661 "b 99f \n"
662
663 // Blend 50 / 50.
664 "50: \n"
665 "vld1.8 {q0}, [%1]! \n"
666 "vld1.8 {q1}, [%2]! \n"
667 "subs %3, %3, #16 \n"
668 "vrhadd.u8 q0, q1 \n"
669 "vst1.8 {q0}, [%0]! \n"
670 "bgt 50b \n"
671 "b 99f \n"
672
673 // Blend 75 / 25.
674 "75: \n"
675 "vld1.8 {q1}, [%1]! \n"
676 "vld1.8 {q0}, [%2]! \n"
677 "subs %3, %3, #16 \n"
678 "vrhadd.u8 q0, q1 \n"
679 "vrhadd.u8 q0, q1 \n"
680 "vst1.8 {q0}, [%0]! \n"
681 "bgt 75b \n"
682 "b 99f \n"
683
684 // Blend 100 / 0 - Copy row unchanged.
685 "100: \n"
686 "vld1.8 {q0}, [%1]! \n"
687 "subs %3, %3, #16 \n"
688 "vst1.8 {q0}, [%0]! \n"
689 "bgt 100b \n"
690
691 "99: \n"
692 "vst1.8 {d1[7]}, [%0] \n"
693 : "+r"(dst_ptr), // %0
694 "+r"(src_ptr), // %1
695 "+r"(src_stride), // %2
696 "+r"(dst_width), // %3
697 "+r"(source_y_fraction) // %4
698 :
699 : "q0", "q1", "d4", "d5", "q13", "q14", "memory", "cc");
700 }
701
ScaleARGBRowDown2_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)702 void ScaleARGBRowDown2_NEON(const uint8_t* src_ptr,
703 ptrdiff_t src_stride,
704 uint8_t* dst,
705 int dst_width) {
706 (void)src_stride;
707 asm volatile(
708 "1: \n"
709 "vld4.32 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
710 "vld4.32 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
711 "subs %2, %2, #8 \n" // 8 processed per loop
712 "vmov q2, q1 \n" // load next 8 ARGB
713 "vst2.32 {q2, q3}, [%1]! \n" // store odd pixels
714 "bgt 1b \n"
715 : "+r"(src_ptr), // %0
716 "+r"(dst), // %1
717 "+r"(dst_width) // %2
718 :
719 : "memory", "cc", "q0", "q1", "q2", "q3" // Clobber List
720 );
721 }
722
723 // 46: f964 018d vld4.32 {d16,d18,d20,d22}, [r4]!
724 // 4a: 3e04 subs r6, #4
725 // 4c: f964 118d vld4.32 {d17,d19,d21,d23}, [r4]!
726 // 50: ef64 21f4 vorr q9, q10, q10
727 // 54: f942 038d vst2.32 {d16-d19}, [r2]!
728 // 58: d1f5 bne.n 46 <ScaleARGBRowDown2_C+0x46>
729
ScaleARGBRowDown2Linear_NEON(const uint8_t * src_argb,ptrdiff_t src_stride,uint8_t * dst_argb,int dst_width)730 void ScaleARGBRowDown2Linear_NEON(const uint8_t* src_argb,
731 ptrdiff_t src_stride,
732 uint8_t* dst_argb,
733 int dst_width) {
734 (void)src_stride;
735 asm volatile(
736 "1: \n"
737 "vld4.32 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
738 "vld4.32 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
739 "subs %2, %2, #8 \n" // 8 processed per loop
740 "vrhadd.u8 q0, q0, q1 \n" // rounding half add
741 "vrhadd.u8 q1, q2, q3 \n" // rounding half add
742 "vst2.32 {q0, q1}, [%1]! \n"
743 "bgt 1b \n"
744 : "+r"(src_argb), // %0
745 "+r"(dst_argb), // %1
746 "+r"(dst_width) // %2
747 :
748 : "memory", "cc", "q0", "q1", "q2", "q3" // Clobber List
749 );
750 }
751
ScaleARGBRowDown2Box_NEON(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst,int dst_width)752 void ScaleARGBRowDown2Box_NEON(const uint8_t* src_ptr,
753 ptrdiff_t src_stride,
754 uint8_t* dst,
755 int dst_width) {
756 asm volatile(
757 // change the stride to row 2 pointer
758 "add %1, %1, %0 \n"
759 "1: \n"
760 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
761 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB
762 "subs %3, %3, #8 \n" // 8 processed per loop.
763 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
764 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
765 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
766 "vpaddl.u8 q3, q3 \n" // A 16 bytes -> 8 shorts.
767 "vld4.8 {d16, d18, d20, d22}, [%1]! \n" // load 8 more ARGB
768 "vld4.8 {d17, d19, d21, d23}, [%1]! \n" // load last 8 ARGB
769 "vpadal.u8 q0, q8 \n" // B 16 bytes -> 8 shorts.
770 "vpadal.u8 q1, q9 \n" // G 16 bytes -> 8 shorts.
771 "vpadal.u8 q2, q10 \n" // R 16 bytes -> 8 shorts.
772 "vpadal.u8 q3, q11 \n" // A 16 bytes -> 8 shorts.
773 "vrshrn.u16 d0, q0, #2 \n" // round and pack to bytes
774 "vrshrn.u16 d1, q1, #2 \n"
775 "vrshrn.u16 d2, q2, #2 \n"
776 "vrshrn.u16 d3, q3, #2 \n"
777 "vst4.8 {d0, d1, d2, d3}, [%2]! \n"
778 "bgt 1b \n"
779 : "+r"(src_ptr), // %0
780 "+r"(src_stride), // %1
781 "+r"(dst), // %2
782 "+r"(dst_width) // %3
783 :
784 : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11");
785 }
786
787 // Reads 4 pixels at a time.
788 // 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)789 void ScaleARGBRowDownEven_NEON(const uint8_t* src_argb,
790 ptrdiff_t src_stride,
791 int src_stepx,
792 uint8_t* dst_argb,
793 int dst_width) {
794 (void)src_stride;
795 asm volatile(
796 "mov r12, %3, lsl #2 \n"
797 "1: \n"
798 "vld1.32 {d0[0]}, [%0], r12 \n"
799 "vld1.32 {d0[1]}, [%0], r12 \n"
800 "vld1.32 {d1[0]}, [%0], r12 \n"
801 "vld1.32 {d1[1]}, [%0], r12 \n"
802 "subs %2, %2, #4 \n" // 4 pixels per loop.
803 "vst1.8 {q0}, [%1]! \n"
804 "bgt 1b \n"
805 : "+r"(src_argb), // %0
806 "+r"(dst_argb), // %1
807 "+r"(dst_width) // %2
808 : "r"(src_stepx) // %3
809 : "memory", "cc", "r12", "q0");
810 }
811
812 // Reads 4 pixels at a time.
813 // 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)814 void ScaleARGBRowDownEvenBox_NEON(const uint8_t* src_argb,
815 ptrdiff_t src_stride,
816 int src_stepx,
817 uint8_t* dst_argb,
818 int dst_width) {
819 asm volatile(
820 "mov r12, %4, lsl #2 \n"
821 "add %1, %1, %0 \n"
822 "1: \n"
823 "vld1.8 {d0}, [%0], r12 \n" // 4 2x2 blocks -> 2x1
824 "vld1.8 {d1}, [%1], r12 \n"
825 "vld1.8 {d2}, [%0], r12 \n"
826 "vld1.8 {d3}, [%1], r12 \n"
827 "vld1.8 {d4}, [%0], r12 \n"
828 "vld1.8 {d5}, [%1], r12 \n"
829 "vld1.8 {d6}, [%0], r12 \n"
830 "vld1.8 {d7}, [%1], r12 \n"
831 "vaddl.u8 q0, d0, d1 \n"
832 "vaddl.u8 q1, d2, d3 \n"
833 "vaddl.u8 q2, d4, d5 \n"
834 "vaddl.u8 q3, d6, d7 \n"
835 "vswp.8 d1, d2 \n" // ab_cd -> ac_bd
836 "vswp.8 d5, d6 \n" // ef_gh -> eg_fh
837 "vadd.u16 q0, q0, q1 \n" // (a+b)_(c+d)
838 "vadd.u16 q2, q2, q3 \n" // (e+f)_(g+h)
839 "vrshrn.u16 d0, q0, #2 \n" // first 2 pixels.
840 "vrshrn.u16 d1, q2, #2 \n" // next 2 pixels.
841 "subs %3, %3, #4 \n" // 4 pixels per loop.
842 "vst1.8 {q0}, [%2]! \n"
843 "bgt 1b \n"
844 : "+r"(src_argb), // %0
845 "+r"(src_stride), // %1
846 "+r"(dst_argb), // %2
847 "+r"(dst_width) // %3
848 : "r"(src_stepx) // %4
849 : "memory", "cc", "r12", "q0", "q1", "q2", "q3");
850 }
851
852 // TODO(Yang Zhang): Investigate less load instructions for
853 // the x/dx stepping
854 #define LOAD1_DATA32_LANE(dn, n) \
855 "lsr %5, %3, #16 \n" \
856 "add %6, %1, %5, lsl #2 \n" \
857 "add %3, %3, %4 \n" \
858 "vld1.32 {" #dn "[" #n "]}, [%6] \n"
859
ScaleARGBCols_NEON(uint8_t * dst_argb,const uint8_t * src_argb,int dst_width,int x,int dx)860 void ScaleARGBCols_NEON(uint8_t* dst_argb,
861 const uint8_t* src_argb,
862 int dst_width,
863 int x,
864 int dx) {
865 int tmp;
866 const uint8_t* src_tmp = src_argb;
867 asm volatile(
868 "1: \n"
869 // clang-format off
870 LOAD1_DATA32_LANE(d0, 0)
871 LOAD1_DATA32_LANE(d0, 1)
872 LOAD1_DATA32_LANE(d1, 0)
873 LOAD1_DATA32_LANE(d1, 1)
874 LOAD1_DATA32_LANE(d2, 0)
875 LOAD1_DATA32_LANE(d2, 1)
876 LOAD1_DATA32_LANE(d3, 0)
877 LOAD1_DATA32_LANE(d3, 1)
878 // clang-format on
879 "vst1.32 {q0, q1}, [%0]! \n" // store pixels
880 "subs %2, %2, #8 \n" // 8 processed per loop
881 "bgt 1b \n"
882 : "+r"(dst_argb), // %0
883 "+r"(src_argb), // %1
884 "+r"(dst_width), // %2
885 "+r"(x), // %3
886 "+r"(dx), // %4
887 "=&r"(tmp), // %5
888 "+r"(src_tmp) // %6
889 :
890 : "memory", "cc", "q0", "q1");
891 }
892
893 #undef LOAD1_DATA32_LANE
894
895 // TODO(Yang Zhang): Investigate less load instructions for
896 // the x/dx stepping
897 #define LOAD2_DATA32_LANE(dn1, dn2, n) \
898 "lsr %5, %3, #16 \n" \
899 "add %6, %1, %5, lsl #2 \n" \
900 "add %3, %3, %4 \n" \
901 "vld2.32 {" #dn1 "[" #n "], " #dn2 "[" #n "]}, [%6] \n"
902
ScaleARGBFilterCols_NEON(uint8_t * dst_argb,const uint8_t * src_argb,int dst_width,int x,int dx)903 void ScaleARGBFilterCols_NEON(uint8_t* dst_argb,
904 const uint8_t* src_argb,
905 int dst_width,
906 int x,
907 int dx) {
908 int dx_offset[4] = {0, 1, 2, 3};
909 int* tmp = dx_offset;
910 const uint8_t* src_tmp = src_argb;
911 asm volatile (
912 "vdup.32 q0, %3 \n" // x
913 "vdup.32 q1, %4 \n" // dx
914 "vld1.32 {q2}, [%5] \n" // 0 1 2 3
915 "vshl.i32 q9, q1, #2 \n" // 4 * dx
916 "vmul.s32 q1, q1, q2 \n"
917 "vmov.i8 q3, #0x7f \n" // 0x7F
918 "vmov.i16 q15, #0x7f \n" // 0x7F
919 // x , x + 1 * dx, x + 2 * dx, x + 3 * dx
920 "vadd.s32 q8, q1, q0 \n"
921 "1: \n"
922 // d0, d1: a
923 // d2, d3: b
924 LOAD2_DATA32_LANE(d0, d2, 0)
925 LOAD2_DATA32_LANE(d0, d2, 1)
926 LOAD2_DATA32_LANE(d1, d3, 0)
927 LOAD2_DATA32_LANE(d1, d3, 1)
928 "vshrn.i32 d22, q8, #9 \n"
929 "vand.16 d22, d22, d30 \n"
930 "vdup.8 d24, d22[0] \n"
931 "vdup.8 d25, d22[2] \n"
932 "vdup.8 d26, d22[4] \n"
933 "vdup.8 d27, d22[6] \n"
934 "vext.8 d4, d24, d25, #4 \n"
935 "vext.8 d5, d26, d27, #4 \n" // f
936 "veor.8 q10, q2, q3 \n" // 0x7f ^ f
937 "vmull.u8 q11, d0, d20 \n"
938 "vmull.u8 q12, d1, d21 \n"
939 "vmull.u8 q13, d2, d4 \n"
940 "vmull.u8 q14, d3, d5 \n"
941 "vadd.i16 q11, q11, q13 \n"
942 "vadd.i16 q12, q12, q14 \n"
943 "vshrn.i16 d0, q11, #7 \n"
944 "vshrn.i16 d1, q12, #7 \n"
945
946 "vst1.32 {d0, d1}, [%0]! \n" // store pixels
947 "vadd.s32 q8, q8, q9 \n"
948 "subs %2, %2, #4 \n" // 4 processed per loop
949 "bgt 1b \n"
950 : "+r"(dst_argb), // %0
951 "+r"(src_argb), // %1
952 "+r"(dst_width), // %2
953 "+r"(x), // %3
954 "+r"(dx), // %4
955 "+r"(tmp), // %5
956 "+r"(src_tmp) // %6
957 :
958 : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9",
959 "q10", "q11", "q12", "q13", "q14", "q15"
960 );
961 }
962
963 #undef LOAD2_DATA32_LANE
964
965 #endif // defined(__ARM_NEON__) && !defined(__aarch64__)
966
967 #ifdef __cplusplus
968 } // extern "C"
969 } // namespace libyuv
970 #endif
971