2 * Copyright 2011 The LibYuv Project Authors. All rights reserved.
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.
11 #include "libyuv/row.h"
18 // This module is for GCC Neon.
19 #if !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__) && \
22 // NEON downscalers with interpolation.
23 // Provided by Fritz Koenig
25 // Read 32x1 throw away even pixels, and write 16x1.
26 void ScaleRowDown2_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
27 uint8* dst, int dst_width) {
31 // load even pixels into q0, odd into q1
33 "vld2.8 {q0, q1}, [%0]! \n"
34 "subs %2, %2, #16 \n" // 16 processed per loop
36 "vst1.8 {q1}, [%1]! \n" // store odd pixels
38 : "+r"(src_ptr), // %0
42 : "q0", "q1" // Clobber List
46 // Read 32x2 average down and write 16x1.
47 void ScaleRowDown2Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
48 uint8* dst, int dst_width) {
50 // change the stride to row 2 pointer
55 "vld1.8 {q0, q1}, [%0]! \n" // load row 1 and post inc
57 "vld1.8 {q2, q3}, [%1]! \n" // load row 2 and post inc
58 "subs %3, %3, #16 \n" // 16 processed per loop
59 "vpaddl.u8 q0, q0 \n" // row 1 add adjacent
61 "vpadal.u8 q0, q2 \n" // row 2 add adjacent + row1
63 "vrshrn.u16 d0, q0, #2 \n" // downshift, round and pack
64 "vrshrn.u16 d1, q1, #2 \n"
66 "vst1.8 {q0}, [%2]! \n"
68 : "+r"(src_ptr), // %0
69 "+r"(src_stride), // %1
73 : "q0", "q1", "q2", "q3" // Clobber List
77 void ScaleRowDown4_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
78 uint8* dst_ptr, int dst_width) {
83 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
84 "subs %2, %2, #8 \n" // 8 processed per loop
86 "vst1.8 {d2}, [%1]! \n"
88 : "+r"(src_ptr), // %0
92 : "q0", "q1", "memory", "cc"
96 void ScaleRowDown4Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
97 uint8* dst_ptr, int dst_width) {
98 const uint8* src_ptr1 = src_ptr + src_stride;
99 const uint8* src_ptr2 = src_ptr + src_stride * 2;
100 const uint8* src_ptr3 = src_ptr + src_stride * 3;
105 "vld1.8 {q0}, [%0]! \n" // load up 16x4
107 "vld1.8 {q1}, [%3]! \n"
109 "vld1.8 {q2}, [%4]! \n"
111 "vld1.8 {q3}, [%5]! \n"
113 "vpaddl.u8 q0, q0 \n"
114 "vpadal.u8 q0, q1 \n"
115 "vpadal.u8 q0, q2 \n"
116 "vpadal.u8 q0, q3 \n"
117 "vpaddl.u16 q0, q0 \n"
118 "vrshrn.u32 d0, q0, #4 \n" // divide by 16 w/rounding
119 "vmovn.u16 d0, q0 \n"
121 "vst1.32 {d0[0]}, [%1]! \n"
123 : "+r"(src_ptr), // %0
125 "+r"(dst_width), // %2
126 "+r"(src_ptr1), // %3
127 "+r"(src_ptr2), // %4
130 : "q0", "q1", "q2", "q3", "memory", "cc"
134 // Down scale from 4 to 3 pixels. Use the neon multilane read/write
135 // to load up the every 4th pixel into a 4 different registers.
136 // Point samples 32 pixels to 24 pixels.
137 void ScaleRowDown34_NEON(const uint8* src_ptr,
138 ptrdiff_t src_stride,
139 uint8* dst_ptr, int dst_width) {
144 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
145 "subs %2, %2, #24 \n"
146 "vmov d2, d3 \n" // order d0, d1, d2
148 "vst3.8 {d0, d1, d2}, [%1]! \n"
150 : "+r"(src_ptr), // %0
152 "+r"(dst_width) // %2
154 : "d0", "d1", "d2", "d3", "memory", "cc"
158 void ScaleRowDown34_0_Box_NEON(const uint8* src_ptr,
159 ptrdiff_t src_stride,
160 uint8* dst_ptr, int dst_width) {
167 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
169 "vld4.8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
170 "subs %2, %2, #24 \n"
172 // filter src line 0 with src line 1
173 // expand chars to shorts to allow for room
174 // when adding lines together
177 "vmovl.u8 q10, d6 \n"
178 "vmovl.u8 q11, d7 \n"
180 // 3 * line_0 + line_1
181 "vmlal.u8 q8, d0, d24 \n"
182 "vmlal.u8 q9, d1, d24 \n"
183 "vmlal.u8 q10, d2, d24 \n"
184 "vmlal.u8 q11, d3, d24 \n"
186 // (3 * line_0 + line_1) >> 2
187 "vqrshrn.u16 d0, q8, #2 \n"
188 "vqrshrn.u16 d1, q9, #2 \n"
189 "vqrshrn.u16 d2, q10, #2 \n"
190 "vqrshrn.u16 d3, q11, #2 \n"
192 // a0 = (src[0] * 3 + s[1] * 1) >> 2
194 "vmlal.u8 q8, d0, d24 \n"
195 "vqrshrn.u16 d0, q8, #2 \n"
197 // a1 = (src[1] * 1 + s[2] * 1) >> 1
198 "vrhadd.u8 d1, d1, d2 \n"
200 // a2 = (src[2] * 1 + s[3] * 3) >> 2
202 "vmlal.u8 q8, d3, d24 \n"
203 "vqrshrn.u16 d2, q8, #2 \n"
206 "vst3.8 {d0, d1, d2}, [%1]! \n"
209 : "+r"(src_ptr), // %0
211 "+r"(dst_width), // %2
212 "+r"(src_stride) // %3
214 : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "d24", "memory", "cc"
218 void ScaleRowDown34_1_Box_NEON(const uint8* src_ptr,
219 ptrdiff_t src_stride,
220 uint8* dst_ptr, int dst_width) {
227 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
229 "vld4.8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
230 "subs %2, %2, #24 \n"
231 // average src line 0 with src line 1
232 "vrhadd.u8 q0, q0, q2 \n"
233 "vrhadd.u8 q1, q1, q3 \n"
235 // a0 = (src[0] * 3 + s[1] * 1) >> 2
237 "vmlal.u8 q3, d0, d24 \n"
238 "vqrshrn.u16 d0, q3, #2 \n"
240 // a1 = (src[1] * 1 + s[2] * 1) >> 1
241 "vrhadd.u8 d1, d1, d2 \n"
243 // a2 = (src[2] * 1 + s[3] * 3) >> 2
245 "vmlal.u8 q3, d3, d24 \n"
246 "vqrshrn.u16 d2, q3, #2 \n"
249 "vst3.8 {d0, d1, d2}, [%1]! \n"
251 : "+r"(src_ptr), // %0
253 "+r"(dst_width), // %2
254 "+r"(src_stride) // %3
256 : "r4", "q0", "q1", "q2", "q3", "d24", "memory", "cc"
260 #define HAS_SCALEROWDOWN38_NEON
261 static uvec8 kShuf38 =
262 { 0, 3, 6, 8, 11, 14, 16, 19, 22, 24, 27, 30, 0, 0, 0, 0 };
263 static uvec8 kShuf38_2 =
264 { 0, 8, 16, 2, 10, 17, 4, 12, 18, 6, 14, 19, 0, 0, 0, 0 };
265 static vec16 kMult38_Div6 =
266 { 65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12,
267 65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12 };
268 static vec16 kMult38_Div9 =
269 { 65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18,
270 65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18 };
273 void ScaleRowDown38_NEON(const uint8* src_ptr,
274 ptrdiff_t src_stride,
275 uint8* dst_ptr, int dst_width) {
278 "vld1.8 {q3}, [%3] \n"
282 "vld1.8 {d0, d1, d2, d3}, [%0]! \n"
283 "subs %2, %2, #12 \n"
284 "vtbl.u8 d4, {d0, d1, d2, d3}, d6 \n"
285 "vtbl.u8 d5, {d0, d1, d2, d3}, d7 \n"
287 "vst1.8 {d4}, [%1]! \n"
289 "vst1.32 {d5[0]}, [%1]! \n"
291 : "+r"(src_ptr), // %0
293 "+r"(dst_width) // %2
294 : "r"(&kShuf38) // %3
295 : "d0", "d1", "d2", "d3", "d4", "d5", "memory", "cc"
300 void OMITFP ScaleRowDown38_3_Box_NEON(const uint8* src_ptr,
301 ptrdiff_t src_stride,
302 uint8* dst_ptr, int dst_width) {
303 const uint8* src_ptr1 = src_ptr + src_stride * 2;
307 "vld1.16 {q13}, [%5] \n"
309 "vld1.8 {q14}, [%6] \n"
311 "vld1.8 {q15}, [%7] \n"
316 // d0 = 00 40 01 41 02 42 03 43
317 // d1 = 10 50 11 51 12 52 13 53
318 // d2 = 20 60 21 61 22 62 23 63
319 // d3 = 30 70 31 71 32 72 33 73
321 "vld4.8 {d0, d1, d2, d3}, [%0]! \n"
323 "vld4.8 {d4, d5, d6, d7}, [%3]! \n"
325 "vld4.8 {d16, d17, d18, d19}, [%4]! \n"
326 "subs %2, %2, #12 \n"
328 // Shuffle the input data around to get align the data
329 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
330 // d0 = 00 10 01 11 02 12 03 13
331 // d1 = 40 50 41 51 42 52 43 53
334 "vtrn.u8 d16, d17 \n"
336 // d2 = 20 30 21 31 22 32 23 33
337 // d3 = 60 70 61 71 62 72 63 73
340 "vtrn.u8 d18, d19 \n"
342 // d0 = 00+10 01+11 02+12 03+13
343 // d2 = 40+50 41+51 42+52 43+53
344 "vpaddl.u8 q0, q0 \n"
345 "vpaddl.u8 q2, q2 \n"
346 "vpaddl.u8 q8, q8 \n"
348 // d3 = 60+70 61+71 62+72 63+73
349 "vpaddl.u8 d3, d3 \n"
350 "vpaddl.u8 d7, d7 \n"
351 "vpaddl.u8 d19, d19 \n"
353 // combine source lines
356 "vadd.u16 d4, d3, d7 \n"
357 "vadd.u16 d4, d19 \n"
359 // dst_ptr[3] = (s[6 + st * 0] + s[7 + st * 0]
360 // + s[6 + st * 1] + s[7 + st * 1]
361 // + s[6 + st * 2] + s[7 + st * 2]) / 6
362 "vqrdmulh.s16 q2, q2, q13 \n"
363 "vmovn.u16 d4, q2 \n"
365 // Shuffle 2,3 reg around so that 2 can be added to the
366 // 0,1 reg and 3 can be added to the 4,5 reg. This
367 // requires expanding from u8 to u16 as the 0,1 and 4,5
368 // registers are already expanded. Then do transposes
370 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
373 "vmovl.u8 q9, d18 \n"
375 // combine source lines
379 // d4 = xx 20 xx 30 xx 22 xx 32
380 // d5 = xx 21 xx 31 xx 23 xx 33
383 // d4 = xx 20 xx 21 xx 22 xx 23
384 // d5 = xx 30 xx 31 xx 32 xx 33
390 // Need to divide, but can't downshift as the the value
391 // isn't a power of 2. So multiply by 65536 / n
392 // and take the upper 16 bits.
393 "vqrdmulh.s16 q0, q0, q15 \n"
395 // Align for table lookup, vtbl requires registers to
399 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
400 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
403 "vst1.8 {d3}, [%1]! \n"
405 "vst1.32 {d4[0]}, [%1]! \n"
407 : "+r"(src_ptr), // %0
409 "+r"(dst_width), // %2
410 "+r"(src_stride), // %3
412 : "r"(&kMult38_Div6), // %5
413 "r"(&kShuf38_2), // %6
414 "r"(&kMult38_Div9) // %7
415 : "q0", "q1", "q2", "q3", "q8", "q9", "q13", "q14", "q15", "memory", "cc"
420 void ScaleRowDown38_2_Box_NEON(const uint8* src_ptr,
421 ptrdiff_t src_stride,
422 uint8* dst_ptr, int dst_width) {
425 "vld1.16 {q13}, [%4] \n"
427 "vld1.8 {q14}, [%5] \n"
432 // d0 = 00 40 01 41 02 42 03 43
433 // d1 = 10 50 11 51 12 52 13 53
434 // d2 = 20 60 21 61 22 62 23 63
435 // d3 = 30 70 31 71 32 72 33 73
437 "vld4.8 {d0, d1, d2, d3}, [%0]! \n"
439 "vld4.8 {d4, d5, d6, d7}, [%3]! \n"
440 "subs %2, %2, #12 \n"
442 // Shuffle the input data around to get align the data
443 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
444 // d0 = 00 10 01 11 02 12 03 13
445 // d1 = 40 50 41 51 42 52 43 53
449 // d2 = 20 30 21 31 22 32 23 33
450 // d3 = 60 70 61 71 62 72 63 73
454 // d0 = 00+10 01+11 02+12 03+13
455 // d2 = 40+50 41+51 42+52 43+53
456 "vpaddl.u8 q0, q0 \n"
457 "vpaddl.u8 q2, q2 \n"
459 // d3 = 60+70 61+71 62+72 63+73
460 "vpaddl.u8 d3, d3 \n"
461 "vpaddl.u8 d7, d7 \n"
463 // combine source lines
465 "vadd.u16 d4, d3, d7 \n"
467 // dst_ptr[3] = (s[6] + s[7] + s[6+st] + s[7+st]) / 4
468 "vqrshrn.u16 d4, q2, #2 \n"
470 // Shuffle 2,3 reg around so that 2 can be added to the
471 // 0,1 reg and 3 can be added to the 4,5 reg. This
472 // requires expanding from u8 to u16 as the 0,1 and 4,5
473 // registers are already expanded. Then do transposes
475 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
479 // combine source lines
482 // d4 = xx 20 xx 30 xx 22 xx 32
483 // d5 = xx 21 xx 31 xx 23 xx 33
486 // d4 = xx 20 xx 21 xx 22 xx 23
487 // d5 = xx 30 xx 31 xx 32 xx 33
493 // Need to divide, but can't downshift as the the value
494 // isn't a power of 2. So multiply by 65536 / n
495 // and take the upper 16 bits.
496 "vqrdmulh.s16 q0, q0, q13 \n"
498 // Align for table lookup, vtbl requires registers to
502 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
503 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
506 "vst1.8 {d3}, [%1]! \n"
508 "vst1.32 {d4[0]}, [%1]! \n"
510 : "+r"(src_ptr), // %0
512 "+r"(dst_width), // %2
513 "+r"(src_stride) // %3
514 : "r"(&kMult38_Div6), // %4
515 "r"(&kShuf38_2) // %5
516 : "q0", "q1", "q2", "q3", "q13", "q14", "memory", "cc"
521 void ScaleFilterRows_NEON(uint8* dst_ptr,
522 const uint8* src_ptr, ptrdiff_t src_stride,
523 int dst_width, int source_y_fraction) {
538 // General purpose row blend.
541 "vld1.8 {q0}, [%1]! \n"
543 "vld1.8 {q1}, [%2]! \n"
544 "subs %3, %3, #16 \n"
545 "vmull.u8 q13, d0, d4 \n"
546 "vmull.u8 q14, d1, d4 \n"
547 "vmlal.u8 q13, d2, d5 \n"
548 "vmlal.u8 q14, d3, d5 \n"
549 "vrshrn.u16 d0, q13, #8 \n"
550 "vrshrn.u16 d1, q14, #8 \n"
552 "vst1.8 {q0}, [%0]! \n"
559 "vld1.8 {q0}, [%1]! \n"
561 "vld1.8 {q1}, [%2]! \n"
562 "subs %3, %3, #16 \n"
563 "vrhadd.u8 q0, q1 \n"
564 "vrhadd.u8 q0, q1 \n"
566 "vst1.8 {q0}, [%0]! \n"
573 "vld1.8 {q0}, [%1]! \n"
575 "vld1.8 {q1}, [%2]! \n"
576 "subs %3, %3, #16 \n"
577 "vrhadd.u8 q0, q1 \n"
579 "vst1.8 {q0}, [%0]! \n"
586 "vld1.8 {q1}, [%1]! \n"
588 "vld1.8 {q0}, [%2]! \n"
589 "subs %3, %3, #16 \n"
590 "vrhadd.u8 q0, q1 \n"
591 "vrhadd.u8 q0, q1 \n"
593 "vst1.8 {q0}, [%0]! \n"
597 // Blend 100 / 0 - Copy row unchanged.
600 "vld1.8 {q0}, [%1]! \n"
601 "subs %3, %3, #16 \n"
603 "vst1.8 {q0}, [%0]! \n"
608 "vst1.8 {d1[7]}, [%0] \n"
609 : "+r"(dst_ptr), // %0
611 "+r"(src_stride), // %2
612 "+r"(dst_width), // %3
613 "+r"(source_y_fraction) // %4
615 : "q0", "q1", "d4", "d5", "q13", "q14", "memory", "cc"
619 void ScaleARGBRowDown2_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
620 uint8* dst, int dst_width) {
624 // load even pixels into q0, odd into q1
626 "vld2.32 {q0, q1}, [%0]! \n"
628 "vld2.32 {q2, q3}, [%0]! \n"
629 "subs %2, %2, #8 \n" // 8 processed per loop
631 "vst1.8 {q1}, [%1]! \n" // store odd pixels
633 "vst1.8 {q3}, [%1]! \n"
635 : "+r"(src_ptr), // %0
637 "+r"(dst_width) // %2
639 : "memory", "cc", "q0", "q1", "q2", "q3" // Clobber List
643 void ScaleARGBRowDown2Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
644 uint8* dst, int dst_width) {
646 // change the stride to row 2 pointer
651 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels.
653 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels.
654 "subs %3, %3, #8 \n" // 8 processed per loop.
655 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts.
656 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts.
657 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts.
658 "vpaddl.u8 q3, q3 \n" // A 16 bytes -> 8 shorts.
660 "vld4.8 {d16, d18, d20, d22}, [%1]! \n" // load 8 more ARGB pixels.
662 "vld4.8 {d17, d19, d21, d23}, [%1]! \n" // load last 8 ARGB pixels.
663 "vpadal.u8 q0, q8 \n" // B 16 bytes -> 8 shorts.
664 "vpadal.u8 q1, q9 \n" // G 16 bytes -> 8 shorts.
665 "vpadal.u8 q2, q10 \n" // R 16 bytes -> 8 shorts.
666 "vpadal.u8 q3, q11 \n" // A 16 bytes -> 8 shorts.
667 "vrshrn.u16 d0, q0, #2 \n" // downshift, round and pack
668 "vrshrn.u16 d1, q1, #2 \n"
669 "vrshrn.u16 d2, q2, #2 \n"
670 "vrshrn.u16 d3, q3, #2 \n"
672 "vst4.8 {d0, d1, d2, d3}, [%2]! \n"
674 : "+r"(src_ptr), // %0
675 "+r"(src_stride), // %1
677 "+r"(dst_width) // %3
679 : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11"
683 // Reads 4 pixels at a time.
684 // Alignment requirement: src_argb 4 byte aligned.
685 void ScaleARGBRowDownEven_NEON(const uint8* src_argb, ptrdiff_t src_stride,
686 int src_stepx, uint8* dst_argb, int dst_width) {
688 "mov r12, %3, lsl #2 \n"
692 "vld1.32 {d0[0]}, [%0], r12 \n"
694 "vld1.32 {d0[1]}, [%0], r12 \n"
696 "vld1.32 {d1[0]}, [%0], r12 \n"
698 "vld1.32 {d1[1]}, [%0], r12 \n"
699 "subs %2, %2, #4 \n" // 4 pixels per loop.
701 "vst1.8 {q0}, [%1]! \n"
703 : "+r"(src_argb), // %0
704 "+r"(dst_argb), // %1
705 "+r"(dst_width) // %2
706 : "r"(src_stepx) // %3
707 : "memory", "cc", "r12", "q0"
711 // Reads 4 pixels at a time.
712 // Alignment requirement: src_argb 4 byte aligned.
713 void ScaleARGBRowDownEvenBox_NEON(const uint8* src_argb, ptrdiff_t src_stride,
715 uint8* dst_argb, int dst_width) {
717 "mov r12, %4, lsl #2 \n"
722 "vld1.8 {d0}, [%0], r12 \n" // Read 4 2x2 blocks -> 2x1
724 "vld1.8 {d1}, [%1], r12 \n"
726 "vld1.8 {d2}, [%0], r12 \n"
728 "vld1.8 {d3}, [%1], r12 \n"
730 "vld1.8 {d4}, [%0], r12 \n"
732 "vld1.8 {d5}, [%1], r12 \n"
734 "vld1.8 {d6}, [%0], r12 \n"
736 "vld1.8 {d7}, [%1], r12 \n"
737 "vaddl.u8 q0, d0, d1 \n"
738 "vaddl.u8 q1, d2, d3 \n"
739 "vaddl.u8 q2, d4, d5 \n"
740 "vaddl.u8 q3, d6, d7 \n"
741 "vswp.8 d1, d2 \n" // ab_cd -> ac_bd
742 "vswp.8 d5, d6 \n" // ef_gh -> eg_fh
743 "vadd.u16 q0, q0, q1 \n" // (a+b)_(c+d)
744 "vadd.u16 q2, q2, q3 \n" // (e+f)_(g+h)
745 "vrshrn.u16 d0, q0, #2 \n" // first 2 pixels.
746 "vrshrn.u16 d1, q2, #2 \n" // next 2 pixels.
747 "subs %3, %3, #4 \n" // 4 pixels per loop.
749 "vst1.8 {q0}, [%2]! \n"
751 : "+r"(src_argb), // %0
752 "+r"(src_stride), // %1
753 "+r"(dst_argb), // %2
754 "+r"(dst_width) // %3
755 : "r"(src_stepx) // %4
756 : "memory", "cc", "r12", "q0", "q1", "q2", "q3"
760 #endif // defined(__ARM_NEON__) && !defined(__aarch64__)
764 } // namespace libyuv