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