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