xref: /aosp_15_r20/external/libyuv/source/scale_neon.cc (revision 4e366538070a3a6c5c163c31b791eab742e1657a)
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