blob: a1946f051bc1cb3f0c1c8241597065a56f3a06f9 [file] [log] [blame]
fbarchard@google.com64ce0ab2012-10-09 00:05:29 +00001/*
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/basic_types.h"
12#include "libyuv/row.h"
13
14#ifdef __cplusplus
15namespace libyuv {
16extern "C" {
17#endif
18
19// This module is for GCC Neon
20#if !defined(YUV_DISABLE_ASM) && defined(__ARM_NEON__)
21
22/**
23 * NEON downscalers with interpolation.
24 *
25 * Provided by Fritz Koenig
26 *
27 */
28
29void ScaleRowDown2_NEON(const uint8* src_ptr, ptrdiff_t /* src_stride */,
30 uint8* dst, int dst_width) {
31 asm volatile (
32 "1: \n"
33 // load even pixels into q0, odd into q1
34 "vld2.u8 {q0,q1}, [%0]! \n"
35 "vst1.u8 {q0}, [%1]! \n" // store even pixels
36 "subs %2, %2, #16 \n" // 16 processed per loop
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
46void ScaleRowDown2Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
47 uint8* dst, int dst_width) {
48 asm volatile (
49 // change the stride to row 2 pointer
50 "add %1, %0 \n"
51 "1: \n"
52 "vld1.u8 {q0,q1}, [%0]! \n" // load row 1 and post inc
53 "vld1.u8 {q2,q3}, [%1]! \n" // load row 2 and post inc
54 "vpaddl.u8 q0, q0 \n" // row 1 add adjacent
55 "vpaddl.u8 q1, q1 \n"
56 "vpadal.u8 q0, q2 \n" // row 2 add adjacent + row1
57 "vpadal.u8 q1, q3 \n"
58 "vrshrn.u16 d0, q0, #2 \n" // downshift, round and pack
59 "vrshrn.u16 d1, q1, #2 \n"
60 "vst1.u8 {q0}, [%2]! \n"
61 "subs %3, %3, #16 \n" // 16 processed per loop
62 "bgt 1b \n"
63 : "+r"(src_ptr), // %0
64 "+r"(src_stride), // %1
65 "+r"(dst), // %2
66 "+r"(dst_width) // %3
67 :
68 : "q0", "q1", "q2", "q3" // Clobber List
69 );
70}
71
72void ScaleRowDown4_NEON(const uint8* src_ptr, ptrdiff_t /* src_stride */,
73 uint8* dst_ptr, int dst_width) {
74 asm volatile (
75 "1: \n"
76 "vld2.u8 {d0, d1}, [%0]! \n"
77 "vtrn.u8 d1, d0 \n"
78 "vshrn.u16 d0, q0, #8 \n"
79 "vst1.u32 {d0[1]}, [%1]! \n"
80 "subs %2, #4 \n"
81 "bgt 1b \n"
82 : "+r"(src_ptr), // %0
83 "+r"(dst_ptr), // %1
84 "+r"(dst_width) // %2
85 :
86 : "q0", "q1", "memory", "cc"
87 );
88}
89
90void ScaleRowDown4Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
91 uint8* dst_ptr, int dst_width) {
92 asm volatile (
93 "add r4, %0, %3 \n"
94 "add r5, r4, %3 \n"
95 "add %3, r5, %3 \n"
96 "1: \n"
97 "vld1.u8 {q0}, [%0]! \n" // load up 16x4
98 "vld1.u8 {q1}, [r4]! \n"
99 "vld1.u8 {q2}, [r5]! \n"
100 "vld1.u8 {q3}, [%3]! \n"
101 "vpaddl.u8 q0, q0 \n"
102 "vpadal.u8 q0, q1 \n"
103 "vpadal.u8 q0, q2 \n"
104 "vpadal.u8 q0, q3 \n"
105 "vpaddl.u16 q0, q0 \n"
106 "vrshrn.u32 d0, q0, #4 \n" // divide by 16 w/rounding
107 "vmovn.u16 d0, q0 \n"
108 "vst1.u32 {d0[0]}, [%1]! \n"
109 "subs %2, #4 \n"
110 "bgt 1b \n"
111 : "+r"(src_ptr), // %0
112 "+r"(dst_ptr), // %1
113 "+r"(dst_width) // %2
114 : "r"(src_stride) // %3
115 : "r4", "r5", "q0", "q1", "q2", "q3", "memory", "cc"
116 );
117}
118
119// Down scale from 4 to 3 pixels. Use the neon multilane read/write
120// to load up the every 4th pixel into a 4 different registers.
121// Point samples 32 pixels to 24 pixels.
122void ScaleRowDown34_NEON(const uint8* src_ptr,
123 ptrdiff_t /* src_stride */,
124 uint8* dst_ptr, int dst_width) {
125 asm volatile (
126 "1: \n"
127 "vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
128 "vmov d2, d3 \n" // order d0, d1, d2
129 "vst3.u8 {d0, d1, d2}, [%1]! \n"
130 "subs %2, #24 \n"
131 "bgt 1b \n"
132 : "+r"(src_ptr), // %0
133 "+r"(dst_ptr), // %1
134 "+r"(dst_width) // %2
135 :
136 : "d0", "d1", "d2", "d3", "memory", "cc"
137 );
138}
139
140void ScaleRowDown34_0_Int_NEON(const uint8* src_ptr,
141 ptrdiff_t src_stride,
142 uint8* dst_ptr, int dst_width) {
143 asm volatile (
144 "vmov.u8 d24, #3 \n"
145 "add %3, %0 \n"
146 "1: \n"
147 "vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
148 "vld4.u8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
149
150 // filter src line 0 with src line 1
151 // expand chars to shorts to allow for room
152 // when adding lines together
153 "vmovl.u8 q8, d4 \n"
154 "vmovl.u8 q9, d5 \n"
155 "vmovl.u8 q10, d6 \n"
156 "vmovl.u8 q11, d7 \n"
157
158 // 3 * line_0 + line_1
159 "vmlal.u8 q8, d0, d24 \n"
160 "vmlal.u8 q9, d1, d24 \n"
161 "vmlal.u8 q10, d2, d24 \n"
162 "vmlal.u8 q11, d3, d24 \n"
163
164 // (3 * line_0 + line_1) >> 2
165 "vqrshrn.u16 d0, q8, #2 \n"
166 "vqrshrn.u16 d1, q9, #2 \n"
167 "vqrshrn.u16 d2, q10, #2 \n"
168 "vqrshrn.u16 d3, q11, #2 \n"
169
170 // a0 = (src[0] * 3 + s[1] * 1) >> 2
171 "vmovl.u8 q8, d1 \n"
172 "vmlal.u8 q8, d0, d24 \n"
173 "vqrshrn.u16 d0, q8, #2 \n"
174
175 // a1 = (src[1] * 1 + s[2] * 1) >> 1
176 "vrhadd.u8 d1, d1, d2 \n"
177
178 // a2 = (src[2] * 1 + s[3] * 3) >> 2
179 "vmovl.u8 q8, d2 \n"
180 "vmlal.u8 q8, d3, d24 \n"
181 "vqrshrn.u16 d2, q8, #2 \n"
182
183 "vst3.u8 {d0, d1, d2}, [%1]! \n"
184
185 "subs %2, #24 \n"
186 "bgt 1b \n"
187 : "+r"(src_ptr), // %0
188 "+r"(dst_ptr), // %1
189 "+r"(dst_width), // %2
190 "+r"(src_stride) // %3
191 :
192 : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "d24", "memory", "cc"
193 );
194}
195
196void ScaleRowDown34_1_Int_NEON(const uint8* src_ptr,
197 ptrdiff_t src_stride,
198 uint8* dst_ptr, int dst_width) {
199 asm volatile (
200 "vmov.u8 d24, #3 \n"
201 "add %3, %0 \n"
202 "1: \n"
203 "vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
204 "vld4.u8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
205
206 // average src line 0 with src line 1
207 "vrhadd.u8 q0, q0, q2 \n"
208 "vrhadd.u8 q1, q1, q3 \n"
209
210 // a0 = (src[0] * 3 + s[1] * 1) >> 2
211 "vmovl.u8 q3, d1 \n"
212 "vmlal.u8 q3, d0, d24 \n"
213 "vqrshrn.u16 d0, q3, #2 \n"
214
215 // a1 = (src[1] * 1 + s[2] * 1) >> 1
216 "vrhadd.u8 d1, d1, d2 \n"
217
218 // a2 = (src[2] * 1 + s[3] * 3) >> 2
219 "vmovl.u8 q3, d2 \n"
220 "vmlal.u8 q3, d3, d24 \n"
221 "vqrshrn.u16 d2, q3, #2 \n"
222
223 "vst3.u8 {d0, d1, d2}, [%1]! \n"
224
225 "subs %2, #24 \n"
226 "bgt 1b \n"
227 : "+r"(src_ptr), // %0
228 "+r"(dst_ptr), // %1
229 "+r"(dst_width), // %2
230 "+r"(src_stride) // %3
231 :
232 : "r4", "q0", "q1", "q2", "q3", "d24", "memory", "cc"
233 );
234}
235
236#define HAS_SCALEROWDOWN38_NEON
237const uvec8 kShuf38 =
238 { 0, 3, 6, 8, 11, 14, 16, 19, 22, 24, 27, 30, 0, 0, 0, 0 };
239const uvec8 kShuf38_2 =
240 { 0, 8, 16, 2, 10, 17, 4, 12, 18, 6, 14, 19, 0, 0, 0, 0 };
241const vec16 kMult38_Div6 =
242 { 65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12,
243 65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12 };
244const vec16 kMult38_Div9 =
245 { 65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18,
246 65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18 };
247
248// 32 -> 12
249void ScaleRowDown38_NEON(const uint8* src_ptr,
250 ptrdiff_t /* src_stride */,
251 uint8* dst_ptr, int dst_width) {
252 asm volatile (
253 "vld1.u8 {q3}, [%3] \n"
254 "1: \n"
255 "vld1.u8 {d0, d1, d2, d3}, [%0]! \n"
256 "vtbl.u8 d4, {d0, d1, d2, d3}, d6 \n"
257 "vtbl.u8 d5, {d0, d1, d2, d3}, d7 \n"
258 "vst1.u8 {d4}, [%1]! \n"
259 "vst1.u32 {d5[0]}, [%1]! \n"
260 "subs %2, #12 \n"
261 "bgt 1b \n"
262 : "+r"(src_ptr), // %0
263 "+r"(dst_ptr), // %1
264 "+r"(dst_width) // %2
265 : "r"(&kShuf38) // %3
266 : "d0", "d1", "d2", "d3", "d4", "d5", "memory", "cc"
267 );
268}
269
270// 32x3 -> 12x1
271void OMITFP ScaleRowDown38_3_Int_NEON(const uint8* src_ptr,
272 ptrdiff_t src_stride,
273 uint8* dst_ptr, int dst_width) {
274 asm volatile (
275 "vld1.u16 {q13}, [%4] \n"
276 "vld1.u8 {q14}, [%5] \n"
277 "vld1.u8 {q15}, [%6] \n"
278 "add r4, %0, %3, lsl #1 \n"
279 "add %3, %0 \n"
280 "1: \n"
281
282 // d0 = 00 40 01 41 02 42 03 43
283 // d1 = 10 50 11 51 12 52 13 53
284 // d2 = 20 60 21 61 22 62 23 63
285 // d3 = 30 70 31 71 32 72 33 73
286 "vld4.u8 {d0, d1, d2, d3}, [%0]! \n"
287 "vld4.u8 {d4, d5, d6, d7}, [%3]! \n"
288 "vld4.u8 {d16, d17, d18, d19}, [r4]! \n"
289
290 // Shuffle the input data around to get align the data
291 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
292 // d0 = 00 10 01 11 02 12 03 13
293 // d1 = 40 50 41 51 42 52 43 53
294 "vtrn.u8 d0, d1 \n"
295 "vtrn.u8 d4, d5 \n"
296 "vtrn.u8 d16, d17 \n"
297
298 // d2 = 20 30 21 31 22 32 23 33
299 // d3 = 60 70 61 71 62 72 63 73
300 "vtrn.u8 d2, d3 \n"
301 "vtrn.u8 d6, d7 \n"
302 "vtrn.u8 d18, d19 \n"
303
304 // d0 = 00+10 01+11 02+12 03+13
305 // d2 = 40+50 41+51 42+52 43+53
306 "vpaddl.u8 q0, q0 \n"
307 "vpaddl.u8 q2, q2 \n"
308 "vpaddl.u8 q8, q8 \n"
309
310 // d3 = 60+70 61+71 62+72 63+73
311 "vpaddl.u8 d3, d3 \n"
312 "vpaddl.u8 d7, d7 \n"
313 "vpaddl.u8 d19, d19 \n"
314
315 // combine source lines
316 "vadd.u16 q0, q2 \n"
317 "vadd.u16 q0, q8 \n"
318 "vadd.u16 d4, d3, d7 \n"
319 "vadd.u16 d4, d19 \n"
320
321 // dst_ptr[3] = (s[6 + st * 0] + s[7 + st * 0]
322 // + s[6 + st * 1] + s[7 + st * 1]
323 // + s[6 + st * 2] + s[7 + st * 2]) / 6
324 "vqrdmulh.s16 q2, q2, q13 \n"
325 "vmovn.u16 d4, q2 \n"
326
327 // Shuffle 2,3 reg around so that 2 can be added to the
328 // 0,1 reg and 3 can be added to the 4,5 reg. This
329 // requires expanding from u8 to u16 as the 0,1 and 4,5
330 // registers are already expanded. Then do transposes
331 // to get aligned.
332 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
333 "vmovl.u8 q1, d2 \n"
334 "vmovl.u8 q3, d6 \n"
335 "vmovl.u8 q9, d18 \n"
336
337 // combine source lines
338 "vadd.u16 q1, q3 \n"
339 "vadd.u16 q1, q9 \n"
340
341 // d4 = xx 20 xx 30 xx 22 xx 32
342 // d5 = xx 21 xx 31 xx 23 xx 33
343 "vtrn.u32 d2, d3 \n"
344
345 // d4 = xx 20 xx 21 xx 22 xx 23
346 // d5 = xx 30 xx 31 xx 32 xx 33
347 "vtrn.u16 d2, d3 \n"
348
349 // 0+1+2, 3+4+5
350 "vadd.u16 q0, q1 \n"
351
352 // Need to divide, but can't downshift as the the value
353 // isn't a power of 2. So multiply by 65536 / n
354 // and take the upper 16 bits.
355 "vqrdmulh.s16 q0, q0, q15 \n"
356
357 // Align for table lookup, vtbl requires registers to
358 // be adjacent
359 "vmov.u8 d2, d4 \n"
360
361 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
362 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
363
364 "vst1.u8 {d3}, [%1]! \n"
365 "vst1.u32 {d4[0]}, [%1]! \n"
366 "subs %2, #12 \n"
367 "bgt 1b \n"
368 : "+r"(src_ptr), // %0
369 "+r"(dst_ptr), // %1
370 "+r"(dst_width), // %2
371 "+r"(src_stride) // %3
372 : "r"(&kMult38_Div6), // %4
373 "r"(&kShuf38_2), // %5
374 "r"(&kMult38_Div9) // %6
375 : "r4", "q0", "q1", "q2", "q3", "q8", "q9",
376 "q13", "q14", "q15", "memory", "cc"
377 );
378}
379
380// 32x2 -> 12x1
381void ScaleRowDown38_2_Int_NEON(const uint8* src_ptr,
382 ptrdiff_t src_stride,
383 uint8* dst_ptr, int dst_width) {
384 asm volatile (
385 "vld1.u16 {q13}, [%4] \n"
386 "vld1.u8 {q14}, [%5] \n"
387 "add %3, %0 \n"
388 "1: \n"
389
390 // d0 = 00 40 01 41 02 42 03 43
391 // d1 = 10 50 11 51 12 52 13 53
392 // d2 = 20 60 21 61 22 62 23 63
393 // d3 = 30 70 31 71 32 72 33 73
394 "vld4.u8 {d0, d1, d2, d3}, [%0]! \n"
395 "vld4.u8 {d4, d5, d6, d7}, [%3]! \n"
396
397 // Shuffle the input data around to get align the data
398 // so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
399 // d0 = 00 10 01 11 02 12 03 13
400 // d1 = 40 50 41 51 42 52 43 53
401 "vtrn.u8 d0, d1 \n"
402 "vtrn.u8 d4, d5 \n"
403
404 // d2 = 20 30 21 31 22 32 23 33
405 // d3 = 60 70 61 71 62 72 63 73
406 "vtrn.u8 d2, d3 \n"
407 "vtrn.u8 d6, d7 \n"
408
409 // d0 = 00+10 01+11 02+12 03+13
410 // d2 = 40+50 41+51 42+52 43+53
411 "vpaddl.u8 q0, q0 \n"
412 "vpaddl.u8 q2, q2 \n"
413
414 // d3 = 60+70 61+71 62+72 63+73
415 "vpaddl.u8 d3, d3 \n"
416 "vpaddl.u8 d7, d7 \n"
417
418 // combine source lines
419 "vadd.u16 q0, q2 \n"
420 "vadd.u16 d4, d3, d7 \n"
421
422 // dst_ptr[3] = (s[6] + s[7] + s[6+st] + s[7+st]) / 4
423 "vqrshrn.u16 d4, q2, #2 \n"
424
425 // Shuffle 2,3 reg around so that 2 can be added to the
426 // 0,1 reg and 3 can be added to the 4,5 reg. This
427 // requires expanding from u8 to u16 as the 0,1 and 4,5
428 // registers are already expanded. Then do transposes
429 // to get aligned.
430 // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
431 "vmovl.u8 q1, d2 \n"
432 "vmovl.u8 q3, d6 \n"
433
434 // combine source lines
435 "vadd.u16 q1, q3 \n"
436
437 // d4 = xx 20 xx 30 xx 22 xx 32
438 // d5 = xx 21 xx 31 xx 23 xx 33
439 "vtrn.u32 d2, d3 \n"
440
441 // d4 = xx 20 xx 21 xx 22 xx 23
442 // d5 = xx 30 xx 31 xx 32 xx 33
443 "vtrn.u16 d2, d3 \n"
444
445 // 0+1+2, 3+4+5
446 "vadd.u16 q0, q1 \n"
447
448 // Need to divide, but can't downshift as the the value
449 // isn't a power of 2. So multiply by 65536 / n
450 // and take the upper 16 bits.
451 "vqrdmulh.s16 q0, q0, q13 \n"
452
453 // Align for table lookup, vtbl requires registers to
454 // be adjacent
455 "vmov.u8 d2, d4 \n"
456
457 "vtbl.u8 d3, {d0, d1, d2}, d28 \n"
458 "vtbl.u8 d4, {d0, d1, d2}, d29 \n"
459
460 "vst1.u8 {d3}, [%1]! \n"
461 "vst1.u32 {d4[0]}, [%1]! \n"
462 "subs %2, #12 \n"
463 "bgt 1b \n"
464 : "+r"(src_ptr), // %0
465 "+r"(dst_ptr), // %1
466 "+r"(dst_width), // %2
467 "+r"(src_stride) // %3
468 : "r"(&kMult38_Div6), // %4
469 "r"(&kShuf38_2) // %5
470 : "q0", "q1", "q2", "q3", "q13", "q14", "memory", "cc"
471 );
472}
473
474// 16x2 -> 16x1
475void ScaleFilterRows_NEON(uint8* dst_ptr,
476 const uint8* src_ptr, ptrdiff_t src_stride,
477 int dst_width, int source_y_fraction) {
478 asm volatile (
479 "cmp %4, #0 \n"
480 "beq 2f \n"
481 "add %2, %1 \n"
482 "cmp %4, #128 \n"
483 "beq 3f \n"
484
485 "vdup.8 d5, %4 \n"
486 "rsb %4, #256 \n"
487 "vdup.8 d4, %4 \n"
488 "1: \n"
489 "vld1.u8 {q0}, [%1]! \n"
490 "vld1.u8 {q1}, [%2]! \n"
491 "subs %3, #16 \n"
492 "vmull.u8 q13, d0, d4 \n"
493 "vmull.u8 q14, d1, d4 \n"
494 "vmlal.u8 q13, d2, d5 \n"
495 "vmlal.u8 q14, d3, d5 \n"
496 "vrshrn.u16 d0, q13, #8 \n"
497 "vrshrn.u16 d1, q14, #8 \n"
498 "vst1.u8 {q0}, [%0]! \n"
499 "bgt 1b \n"
500 "b 4f \n"
501
502 "2: \n"
503 "vld1.u8 {q0}, [%1]! \n"
504 "subs %3, #16 \n"
505 "vst1.u8 {q0}, [%0]! \n"
506 "bgt 2b \n"
507 "b 4f \n"
508
509 "3: \n"
510 "vld1.u8 {q0}, [%1]! \n"
511 "vld1.u8 {q1}, [%2]! \n"
512 "subs %3, #16 \n"
513 "vrhadd.u8 q0, q1 \n"
514 "vst1.u8 {q0}, [%0]! \n"
515 "bgt 3b \n"
516 "4: \n"
517 "vst1.u8 {d1[7]}, [%0] \n"
518 : "+r"(dst_ptr), // %0
519 "+r"(src_ptr), // %1
520 "+r"(src_stride), // %2
521 "+r"(dst_width), // %3
522 "+r"(source_y_fraction) // %4
523 :
524 : "q0", "q1", "d4", "d5", "q13", "q14", "memory", "cc"
525 );
526}
527
528#endif // __ARM_NEON__
529
530#ifdef __cplusplus
531} // extern "C"
532} // namespace libyuv
533#endif
534