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(__native_client__) 21 22 // Read 8 Y, 4 U and 4 V from 422 23 #define READYUV422 \ 24 "vld1.8 {d0}, [%0]! \n" \ 25 "vld1.32 {d2[0]}, [%1]! \n" \ 26 "vld1.32 {d2[1]}, [%2]! \n" 27 28 // Read 8 Y, 2 U and 2 V from 422 29 #define READYUV411 \ 30 "vld1.8 {d0}, [%0]! \n" \ 31 "vld1.16 {d2[0]}, [%1]! \n" \ 32 "vld1.16 {d2[1]}, [%2]! \n" \ 33 "vmov.u8 d3, d2 \n" \ 34 "vzip.u8 d2, d3 \n" 35 36 // Read 8 Y, 8 U and 8 V from 444 37 #define READYUV444 \ 38 "vld1.8 {d0}, [%0]! \n" \ 39 "vld1.8 {d2}, [%1]! \n" \ 40 "vld1.8 {d3}, [%2]! \n" \ 41 "vpaddl.u8 q1, q1 \n" \ 42 "vrshrn.u16 d2, q1, #1 \n" 43 44 // Read 8 Y, and set 4 U and 4 V to 128 45 #define READYUV400 \ 46 "vld1.8 {d0}, [%0]! \n" \ 47 "vmov.u8 d2, #128 \n" 48 49 // Read 8 Y and 4 UV from NV12 50 #define READNV12 \ 51 "vld1.8 {d0}, [%0]! \n" \ 52 "vld1.8 {d2}, [%1]! \n" \ 53 "vmov.u8 d3, d2 \n"/* split odd/even uv apart */\ 54 "vuzp.u8 d2, d3 \n" \ 55 "vtrn.u32 d2, d3 \n" 56 57 // Read 8 Y and 4 VU from NV21 58 #define READNV21 \ 59 "vld1.8 {d0}, [%0]! \n" \ 60 "vld1.8 {d2}, [%1]! \n" \ 61 "vmov.u8 d3, d2 \n"/* split odd/even uv apart */\ 62 "vuzp.u8 d3, d2 \n" \ 63 "vtrn.u32 d2, d3 \n" 64 65 // Read 8 YUY2 66 #define READYUY2 \ 67 "vld2.8 {d0, d2}, [%0]! \n" \ 68 "vmov.u8 d3, d2 \n" \ 69 "vuzp.u8 d2, d3 \n" \ 70 "vtrn.u32 d2, d3 \n" 71 72 // Read 8 UYVY 73 #define READUYVY \ 74 "vld2.8 {d2, d3}, [%0]! \n" \ 75 "vmov.u8 d0, d3 \n" \ 76 "vmov.u8 d3, d2 \n" \ 77 "vuzp.u8 d2, d3 \n" \ 78 "vtrn.u32 d2, d3 \n" 79 80 #define YUV422TORGB \ 81 "veor.u8 d2, d26 \n"/*subtract 128 from u and v*/\ 82 "vmull.s8 q8, d2, d24 \n"/* u/v B/R component */\ 83 "vmull.s8 q9, d2, d25 \n"/* u/v G component */\ 84 "vmov.u8 d1, #0 \n"/* split odd/even y apart */\ 85 "vtrn.u8 d0, d1 \n" \ 86 "vsub.s16 q0, q0, q15 \n"/* offset y */\ 87 "vmul.s16 q0, q0, q14 \n" \ 88 "vadd.s16 d18, d19 \n" \ 89 "vqadd.s16 d20, d0, d16 \n" /* B */ \ 90 "vqadd.s16 d21, d1, d16 \n" \ 91 "vqadd.s16 d22, d0, d17 \n" /* R */ \ 92 "vqadd.s16 d23, d1, d17 \n" \ 93 "vqadd.s16 d16, d0, d18 \n" /* G */ \ 94 "vqadd.s16 d17, d1, d18 \n" \ 95 "vqshrun.s16 d0, q10, #6 \n" /* B */ \ 96 "vqshrun.s16 d1, q11, #6 \n" /* G */ \ 97 "vqshrun.s16 d2, q8, #6 \n" /* R */ \ 98 "vmovl.u8 q10, d0 \n"/* set up for reinterleave*/\ 99 "vmovl.u8 q11, d1 \n" \ 100 "vmovl.u8 q8, d2 \n" \ 101 "vtrn.u8 d20, d21 \n" \ 102 "vtrn.u8 d22, d23 \n" \ 103 "vtrn.u8 d16, d17 \n" \ 104 "vmov.u8 d21, d16 \n" 105 106 static vec8 kUVToRB = { 127, 127, 127, 127, 102, 102, 102, 102, 107 0, 0, 0, 0, 0, 0, 0, 0 }; 108 static vec8 kUVToG = { -25, -25, -25, -25, -52, -52, -52, -52, 109 0, 0, 0, 0, 0, 0, 0, 0 }; 110 111 void I444ToARGBRow_NEON(const uint8* src_y, 112 const uint8* src_u, 113 const uint8* src_v, 114 uint8* dst_argb, 115 int width) { 116 asm volatile ( 117 "vld1.8 {d24}, [%5] \n" 118 "vld1.8 {d25}, [%6] \n" 119 "vmov.u8 d26, #128 \n" 120 "vmov.u16 q14, #74 \n" 121 "vmov.u16 q15, #16 \n" 122 ".p2align 2 \n" 123 "1: \n" 124 READYUV444 125 YUV422TORGB 126 "subs %4, %4, #8 \n" 127 "vmov.u8 d23, #255 \n" 128 "vst4.8 {d20, d21, d22, d23}, [%3]! \n" 129 "bgt 1b \n" 130 : "+r"(src_y), // %0 131 "+r"(src_u), // %1 132 "+r"(src_v), // %2 133 "+r"(dst_argb), // %3 134 "+r"(width) // %4 135 : "r"(&kUVToRB), // %5 136 "r"(&kUVToG) // %6 137 : "cc", "memory", "q0", "q1", "q2", "q3", 138 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 139 ); 140 } 141 142 void I422ToARGBRow_NEON(const uint8* src_y, 143 const uint8* src_u, 144 const uint8* src_v, 145 uint8* dst_argb, 146 int width) { 147 asm volatile ( 148 "vld1.8 {d24}, [%5] \n" 149 "vld1.8 {d25}, [%6] \n" 150 "vmov.u8 d26, #128 \n" 151 "vmov.u16 q14, #74 \n" 152 "vmov.u16 q15, #16 \n" 153 ".p2align 2 \n" 154 "1: \n" 155 READYUV422 156 YUV422TORGB 157 "subs %4, %4, #8 \n" 158 "vmov.u8 d23, #255 \n" 159 "vst4.8 {d20, d21, d22, d23}, [%3]! \n" 160 "bgt 1b \n" 161 : "+r"(src_y), // %0 162 "+r"(src_u), // %1 163 "+r"(src_v), // %2 164 "+r"(dst_argb), // %3 165 "+r"(width) // %4 166 : "r"(&kUVToRB), // %5 167 "r"(&kUVToG) // %6 168 : "cc", "memory", "q0", "q1", "q2", "q3", 169 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 170 ); 171 } 172 173 void I411ToARGBRow_NEON(const uint8* src_y, 174 const uint8* src_u, 175 const uint8* src_v, 176 uint8* dst_argb, 177 int width) { 178 asm volatile ( 179 "vld1.8 {d24}, [%5] \n" 180 "vld1.8 {d25}, [%6] \n" 181 "vmov.u8 d26, #128 \n" 182 "vmov.u16 q14, #74 \n" 183 "vmov.u16 q15, #16 \n" 184 ".p2align 2 \n" 185 "1: \n" 186 READYUV411 187 YUV422TORGB 188 "subs %4, %4, #8 \n" 189 "vmov.u8 d23, #255 \n" 190 "vst4.8 {d20, d21, d22, d23}, [%3]! \n" 191 "bgt 1b \n" 192 : "+r"(src_y), // %0 193 "+r"(src_u), // %1 194 "+r"(src_v), // %2 195 "+r"(dst_argb), // %3 196 "+r"(width) // %4 197 : "r"(&kUVToRB), // %5 198 "r"(&kUVToG) // %6 199 : "cc", "memory", "q0", "q1", "q2", "q3", 200 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 201 ); 202 } 203 204 void I422ToBGRARow_NEON(const uint8* src_y, 205 const uint8* src_u, 206 const uint8* src_v, 207 uint8* dst_bgra, 208 int width) { 209 asm volatile ( 210 "vld1.8 {d24}, [%5] \n" 211 "vld1.8 {d25}, [%6] \n" 212 "vmov.u8 d26, #128 \n" 213 "vmov.u16 q14, #74 \n" 214 "vmov.u16 q15, #16 \n" 215 ".p2align 2 \n" 216 "1: \n" 217 READYUV422 218 YUV422TORGB 219 "subs %4, %4, #8 \n" 220 "vswp.u8 d20, d22 \n" 221 "vmov.u8 d19, #255 \n" 222 "vst4.8 {d19, d20, d21, d22}, [%3]! \n" 223 "bgt 1b \n" 224 : "+r"(src_y), // %0 225 "+r"(src_u), // %1 226 "+r"(src_v), // %2 227 "+r"(dst_bgra), // %3 228 "+r"(width) // %4 229 : "r"(&kUVToRB), // %5 230 "r"(&kUVToG) // %6 231 : "cc", "memory", "q0", "q1", "q2", "q3", 232 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 233 ); 234 } 235 236 void I422ToABGRRow_NEON(const uint8* src_y, 237 const uint8* src_u, 238 const uint8* src_v, 239 uint8* dst_abgr, 240 int width) { 241 asm volatile ( 242 "vld1.8 {d24}, [%5] \n" 243 "vld1.8 {d25}, [%6] \n" 244 "vmov.u8 d26, #128 \n" 245 "vmov.u16 q14, #74 \n" 246 "vmov.u16 q15, #16 \n" 247 ".p2align 2 \n" 248 "1: \n" 249 READYUV422 250 YUV422TORGB 251 "subs %4, %4, #8 \n" 252 "vswp.u8 d20, d22 \n" 253 "vmov.u8 d23, #255 \n" 254 "vst4.8 {d20, d21, d22, d23}, [%3]! \n" 255 "bgt 1b \n" 256 : "+r"(src_y), // %0 257 "+r"(src_u), // %1 258 "+r"(src_v), // %2 259 "+r"(dst_abgr), // %3 260 "+r"(width) // %4 261 : "r"(&kUVToRB), // %5 262 "r"(&kUVToG) // %6 263 : "cc", "memory", "q0", "q1", "q2", "q3", 264 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 265 ); 266 } 267 268 void I422ToRGBARow_NEON(const uint8* src_y, 269 const uint8* src_u, 270 const uint8* src_v, 271 uint8* dst_rgba, 272 int width) { 273 asm volatile ( 274 "vld1.8 {d24}, [%5] \n" 275 "vld1.8 {d25}, [%6] \n" 276 "vmov.u8 d26, #128 \n" 277 "vmov.u16 q14, #74 \n" 278 "vmov.u16 q15, #16 \n" 279 ".p2align 2 \n" 280 "1: \n" 281 READYUV422 282 YUV422TORGB 283 "subs %4, %4, #8 \n" 284 "vmov.u8 d19, #255 \n" 285 "vst4.8 {d19, d20, d21, d22}, [%3]! \n" 286 "bgt 1b \n" 287 : "+r"(src_y), // %0 288 "+r"(src_u), // %1 289 "+r"(src_v), // %2 290 "+r"(dst_rgba), // %3 291 "+r"(width) // %4 292 : "r"(&kUVToRB), // %5 293 "r"(&kUVToG) // %6 294 : "cc", "memory", "q0", "q1", "q2", "q3", 295 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 296 ); 297 } 298 299 void I422ToRGB24Row_NEON(const uint8* src_y, 300 const uint8* src_u, 301 const uint8* src_v, 302 uint8* dst_rgb24, 303 int width) { 304 asm volatile ( 305 "vld1.8 {d24}, [%5] \n" 306 "vld1.8 {d25}, [%6] \n" 307 "vmov.u8 d26, #128 \n" 308 "vmov.u16 q14, #74 \n" 309 "vmov.u16 q15, #16 \n" 310 ".p2align 2 \n" 311 "1: \n" 312 READYUV422 313 YUV422TORGB 314 "subs %4, %4, #8 \n" 315 "vst3.8 {d20, d21, d22}, [%3]! \n" 316 "bgt 1b \n" 317 : "+r"(src_y), // %0 318 "+r"(src_u), // %1 319 "+r"(src_v), // %2 320 "+r"(dst_rgb24), // %3 321 "+r"(width) // %4 322 : "r"(&kUVToRB), // %5 323 "r"(&kUVToG) // %6 324 : "cc", "memory", "q0", "q1", "q2", "q3", 325 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 326 ); 327 } 328 329 void I422ToRAWRow_NEON(const uint8* src_y, 330 const uint8* src_u, 331 const uint8* src_v, 332 uint8* dst_raw, 333 int width) { 334 asm volatile ( 335 "vld1.8 {d24}, [%5] \n" 336 "vld1.8 {d25}, [%6] \n" 337 "vmov.u8 d26, #128 \n" 338 "vmov.u16 q14, #74 \n" 339 "vmov.u16 q15, #16 \n" 340 ".p2align 2 \n" 341 "1: \n" 342 READYUV422 343 YUV422TORGB 344 "subs %4, %4, #8 \n" 345 "vswp.u8 d20, d22 \n" 346 "vst3.8 {d20, d21, d22}, [%3]! \n" 347 "bgt 1b \n" 348 : "+r"(src_y), // %0 349 "+r"(src_u), // %1 350 "+r"(src_v), // %2 351 "+r"(dst_raw), // %3 352 "+r"(width) // %4 353 : "r"(&kUVToRB), // %5 354 "r"(&kUVToG) // %6 355 : "cc", "memory", "q0", "q1", "q2", "q3", 356 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 357 ); 358 } 359 360 #define ARGBTORGB565 \ 361 "vshr.u8 d20, d20, #3 \n" /* B */ \ 362 "vshr.u8 d21, d21, #2 \n" /* G */ \ 363 "vshr.u8 d22, d22, #3 \n" /* R */ \ 364 "vmovl.u8 q8, d20 \n" /* B */ \ 365 "vmovl.u8 q9, d21 \n" /* G */ \ 366 "vmovl.u8 q10, d22 \n" /* R */ \ 367 "vshl.u16 q9, q9, #5 \n" /* G */ \ 368 "vshl.u16 q10, q10, #11 \n" /* R */ \ 369 "vorr q0, q8, q9 \n" /* BG */ \ 370 "vorr q0, q0, q10 \n" /* BGR */ 371 372 void I422ToRGB565Row_NEON(const uint8* src_y, 373 const uint8* src_u, 374 const uint8* src_v, 375 uint8* dst_rgb565, 376 int width) { 377 asm volatile ( 378 "vld1.8 {d24}, [%5] \n" 379 "vld1.8 {d25}, [%6] \n" 380 "vmov.u8 d26, #128 \n" 381 "vmov.u16 q14, #74 \n" 382 "vmov.u16 q15, #16 \n" 383 ".p2align 2 \n" 384 "1: \n" 385 READYUV422 386 YUV422TORGB 387 "subs %4, %4, #8 \n" 388 ARGBTORGB565 389 "vst1.8 {q0}, [%3]! \n" // store 8 pixels RGB565. 390 "bgt 1b \n" 391 : "+r"(src_y), // %0 392 "+r"(src_u), // %1 393 "+r"(src_v), // %2 394 "+r"(dst_rgb565), // %3 395 "+r"(width) // %4 396 : "r"(&kUVToRB), // %5 397 "r"(&kUVToG) // %6 398 : "cc", "memory", "q0", "q1", "q2", "q3", 399 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 400 ); 401 } 402 403 #define ARGBTOARGB1555 \ 404 "vshr.u8 q10, q10, #3 \n" /* B */ \ 405 "vshr.u8 d22, d22, #3 \n" /* R */ \ 406 "vshr.u8 d23, d23, #7 \n" /* A */ \ 407 "vmovl.u8 q8, d20 \n" /* B */ \ 408 "vmovl.u8 q9, d21 \n" /* G */ \ 409 "vmovl.u8 q10, d22 \n" /* R */ \ 410 "vmovl.u8 q11, d23 \n" /* A */ \ 411 "vshl.u16 q9, q9, #5 \n" /* G */ \ 412 "vshl.u16 q10, q10, #10 \n" /* R */ \ 413 "vshl.u16 q11, q11, #15 \n" /* A */ \ 414 "vorr q0, q8, q9 \n" /* BG */ \ 415 "vorr q1, q10, q11 \n" /* RA */ \ 416 "vorr q0, q0, q1 \n" /* BGRA */ 417 418 void I422ToARGB1555Row_NEON(const uint8* src_y, 419 const uint8* src_u, 420 const uint8* src_v, 421 uint8* dst_argb1555, 422 int width) { 423 asm volatile ( 424 "vld1.8 {d24}, [%5] \n" 425 "vld1.8 {d25}, [%6] \n" 426 "vmov.u8 d26, #128 \n" 427 "vmov.u16 q14, #74 \n" 428 "vmov.u16 q15, #16 \n" 429 ".p2align 2 \n" 430 "1: \n" 431 READYUV422 432 YUV422TORGB 433 "subs %4, %4, #8 \n" 434 "vmov.u8 d23, #255 \n" 435 ARGBTOARGB1555 436 "vst1.8 {q0}, [%3]! \n" // store 8 pixels ARGB1555. 437 "bgt 1b \n" 438 : "+r"(src_y), // %0 439 "+r"(src_u), // %1 440 "+r"(src_v), // %2 441 "+r"(dst_argb1555), // %3 442 "+r"(width) // %4 443 : "r"(&kUVToRB), // %5 444 "r"(&kUVToG) // %6 445 : "cc", "memory", "q0", "q1", "q2", "q3", 446 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 447 ); 448 } 449 450 #define ARGBTOARGB4444 \ 451 "vshr.u8 d20, d20, #4 \n" /* B */ \ 452 "vbic.32 d21, d21, d4 \n" /* G */ \ 453 "vshr.u8 d22, d22, #4 \n" /* R */ \ 454 "vbic.32 d23, d23, d4 \n" /* A */ \ 455 "vorr d0, d20, d21 \n" /* BG */ \ 456 "vorr d1, d22, d23 \n" /* RA */ \ 457 "vzip.u8 d0, d1 \n" /* BGRA */ 458 459 void I422ToARGB4444Row_NEON(const uint8* src_y, 460 const uint8* src_u, 461 const uint8* src_v, 462 uint8* dst_argb4444, 463 int width) { 464 asm volatile ( 465 "vld1.8 {d24}, [%5] \n" 466 "vld1.8 {d25}, [%6] \n" 467 "vmov.u8 d26, #128 \n" 468 "vmov.u16 q14, #74 \n" 469 "vmov.u16 q15, #16 \n" 470 "vmov.u8 d4, #0x0f \n" // bits to clear with vbic. 471 ".p2align 2 \n" 472 "1: \n" 473 READYUV422 474 YUV422TORGB 475 "subs %4, %4, #8 \n" 476 "vmov.u8 d23, #255 \n" 477 ARGBTOARGB4444 478 "vst1.8 {q0}, [%3]! \n" // store 8 pixels ARGB4444. 479 "bgt 1b \n" 480 : "+r"(src_y), // %0 481 "+r"(src_u), // %1 482 "+r"(src_v), // %2 483 "+r"(dst_argb4444), // %3 484 "+r"(width) // %4 485 : "r"(&kUVToRB), // %5 486 "r"(&kUVToG) // %6 487 : "cc", "memory", "q0", "q1", "q2", "q3", 488 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 489 ); 490 } 491 492 void YToARGBRow_NEON(const uint8* src_y, 493 uint8* dst_argb, 494 int width) { 495 asm volatile ( 496 "vld1.8 {d24}, [%3] \n" 497 "vld1.8 {d25}, [%4] \n" 498 "vmov.u8 d26, #128 \n" 499 "vmov.u16 q14, #74 \n" 500 "vmov.u16 q15, #16 \n" 501 ".p2align 2 \n" 502 "1: \n" 503 READYUV400 504 YUV422TORGB 505 "subs %2, %2, #8 \n" 506 "vmov.u8 d23, #255 \n" 507 "vst4.8 {d20, d21, d22, d23}, [%1]! \n" 508 "bgt 1b \n" 509 : "+r"(src_y), // %0 510 "+r"(dst_argb), // %1 511 "+r"(width) // %2 512 : "r"(&kUVToRB), // %3 513 "r"(&kUVToG) // %4 514 : "cc", "memory", "q0", "q1", "q2", "q3", 515 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 516 ); 517 } 518 519 void I400ToARGBRow_NEON(const uint8* src_y, 520 uint8* dst_argb, 521 int width) { 522 asm volatile ( 523 ".p2align 2 \n" 524 "vmov.u8 d23, #255 \n" 525 "1: \n" 526 "vld1.8 {d20}, [%0]! \n" 527 "vmov d21, d20 \n" 528 "vmov d22, d20 \n" 529 "subs %2, %2, #8 \n" 530 "vst4.8 {d20, d21, d22, d23}, [%1]! \n" 531 "bgt 1b \n" 532 : "+r"(src_y), // %0 533 "+r"(dst_argb), // %1 534 "+r"(width) // %2 535 : 536 : "cc", "memory", "d20", "d21", "d22", "d23" 537 ); 538 } 539 540 void NV12ToARGBRow_NEON(const uint8* src_y, 541 const uint8* src_uv, 542 uint8* dst_argb, 543 int width) { 544 asm volatile ( 545 "vld1.8 {d24}, [%4] \n" 546 "vld1.8 {d25}, [%5] \n" 547 "vmov.u8 d26, #128 \n" 548 "vmov.u16 q14, #74 \n" 549 "vmov.u16 q15, #16 \n" 550 ".p2align 2 \n" 551 "1: \n" 552 READNV12 553 YUV422TORGB 554 "subs %3, %3, #8 \n" 555 "vmov.u8 d23, #255 \n" 556 "vst4.8 {d20, d21, d22, d23}, [%2]! \n" 557 "bgt 1b \n" 558 : "+r"(src_y), // %0 559 "+r"(src_uv), // %1 560 "+r"(dst_argb), // %2 561 "+r"(width) // %3 562 : "r"(&kUVToRB), // %4 563 "r"(&kUVToG) // %5 564 : "cc", "memory", "q0", "q1", "q2", "q3", 565 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 566 ); 567 } 568 569 void NV21ToARGBRow_NEON(const uint8* src_y, 570 const uint8* src_uv, 571 uint8* dst_argb, 572 int width) { 573 asm volatile ( 574 "vld1.8 {d24}, [%4] \n" 575 "vld1.8 {d25}, [%5] \n" 576 "vmov.u8 d26, #128 \n" 577 "vmov.u16 q14, #74 \n" 578 "vmov.u16 q15, #16 \n" 579 ".p2align 2 \n" 580 "1: \n" 581 READNV21 582 YUV422TORGB 583 "subs %3, %3, #8 \n" 584 "vmov.u8 d23, #255 \n" 585 "vst4.8 {d20, d21, d22, d23}, [%2]! \n" 586 "bgt 1b \n" 587 : "+r"(src_y), // %0 588 "+r"(src_uv), // %1 589 "+r"(dst_argb), // %2 590 "+r"(width) // %3 591 : "r"(&kUVToRB), // %4 592 "r"(&kUVToG) // %5 593 : "cc", "memory", "q0", "q1", "q2", "q3", 594 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 595 ); 596 } 597 598 void NV12ToRGB565Row_NEON(const uint8* src_y, 599 const uint8* src_uv, 600 uint8* dst_rgb565, 601 int width) { 602 asm volatile ( 603 "vld1.8 {d24}, [%4] \n" 604 "vld1.8 {d25}, [%5] \n" 605 "vmov.u8 d26, #128 \n" 606 "vmov.u16 q14, #74 \n" 607 "vmov.u16 q15, #16 \n" 608 ".p2align 2 \n" 609 "1: \n" 610 READNV12 611 YUV422TORGB 612 "subs %3, %3, #8 \n" 613 ARGBTORGB565 614 "vst1.8 {q0}, [%2]! \n" // store 8 pixels RGB565. 615 "bgt 1b \n" 616 : "+r"(src_y), // %0 617 "+r"(src_uv), // %1 618 "+r"(dst_rgb565), // %2 619 "+r"(width) // %3 620 : "r"(&kUVToRB), // %4 621 "r"(&kUVToG) // %5 622 : "cc", "memory", "q0", "q1", "q2", "q3", 623 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 624 ); 625 } 626 627 void NV21ToRGB565Row_NEON(const uint8* src_y, 628 const uint8* src_uv, 629 uint8* dst_rgb565, 630 int width) { 631 asm volatile ( 632 "vld1.8 {d24}, [%4] \n" 633 "vld1.8 {d25}, [%5] \n" 634 "vmov.u8 d26, #128 \n" 635 "vmov.u16 q14, #74 \n" 636 "vmov.u16 q15, #16 \n" 637 ".p2align 2 \n" 638 "1: \n" 639 READNV21 640 YUV422TORGB 641 "subs %3, %3, #8 \n" 642 ARGBTORGB565 643 "vst1.8 {q0}, [%2]! \n" // store 8 pixels RGB565. 644 "bgt 1b \n" 645 : "+r"(src_y), // %0 646 "+r"(src_uv), // %1 647 "+r"(dst_rgb565), // %2 648 "+r"(width) // %3 649 : "r"(&kUVToRB), // %4 650 "r"(&kUVToG) // %5 651 : "cc", "memory", "q0", "q1", "q2", "q3", 652 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 653 ); 654 } 655 656 void YUY2ToARGBRow_NEON(const uint8* src_yuy2, 657 uint8* dst_argb, 658 int width) { 659 asm volatile ( 660 "vld1.8 {d24}, [%3] \n" 661 "vld1.8 {d25}, [%4] \n" 662 "vmov.u8 d26, #128 \n" 663 "vmov.u16 q14, #74 \n" 664 "vmov.u16 q15, #16 \n" 665 ".p2align 2 \n" 666 "1: \n" 667 READYUY2 668 YUV422TORGB 669 "subs %2, %2, #8 \n" 670 "vmov.u8 d23, #255 \n" 671 "vst4.8 {d20, d21, d22, d23}, [%1]! \n" 672 "bgt 1b \n" 673 : "+r"(src_yuy2), // %0 674 "+r"(dst_argb), // %1 675 "+r"(width) // %2 676 : "r"(&kUVToRB), // %3 677 "r"(&kUVToG) // %4 678 : "cc", "memory", "q0", "q1", "q2", "q3", 679 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 680 ); 681 } 682 683 void UYVYToARGBRow_NEON(const uint8* src_uyvy, 684 uint8* dst_argb, 685 int width) { 686 asm volatile ( 687 "vld1.8 {d24}, [%3] \n" 688 "vld1.8 {d25}, [%4] \n" 689 "vmov.u8 d26, #128 \n" 690 "vmov.u16 q14, #74 \n" 691 "vmov.u16 q15, #16 \n" 692 ".p2align 2 \n" 693 "1: \n" 694 READUYVY 695 YUV422TORGB 696 "subs %2, %2, #8 \n" 697 "vmov.u8 d23, #255 \n" 698 "vst4.8 {d20, d21, d22, d23}, [%1]! \n" 699 "bgt 1b \n" 700 : "+r"(src_uyvy), // %0 701 "+r"(dst_argb), // %1 702 "+r"(width) // %2 703 : "r"(&kUVToRB), // %3 704 "r"(&kUVToG) // %4 705 : "cc", "memory", "q0", "q1", "q2", "q3", 706 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 707 ); 708 } 709 710 // Reads 16 pairs of UV and write even values to dst_u and odd to dst_v. 711 void SplitUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v, 712 int width) { 713 asm volatile ( 714 ".p2align 2 \n" 715 "1: \n" 716 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pairs of UV 717 "subs %3, %3, #16 \n" // 16 processed per loop 718 "vst1.8 {q0}, [%1]! \n" // store U 719 "vst1.8 {q1}, [%2]! \n" // store V 720 "bgt 1b \n" 721 : "+r"(src_uv), // %0 722 "+r"(dst_u), // %1 723 "+r"(dst_v), // %2 724 "+r"(width) // %3 // Output registers 725 : // Input registers 726 : "cc", "memory", "q0", "q1" // Clobber List 727 ); 728 } 729 730 // Reads 16 U's and V's and writes out 16 pairs of UV. 731 void MergeUVRow_NEON(const uint8* src_u, const uint8* src_v, uint8* dst_uv, 732 int width) { 733 asm volatile ( 734 ".p2align 2 \n" 735 "1: \n" 736 "vld1.8 {q0}, [%0]! \n" // load U 737 "vld1.8 {q1}, [%1]! \n" // load V 738 "subs %3, %3, #16 \n" // 16 processed per loop 739 "vst2.u8 {q0, q1}, [%2]! \n" // store 16 pairs of UV 740 "bgt 1b \n" 741 : 742 "+r"(src_u), // %0 743 "+r"(src_v), // %1 744 "+r"(dst_uv), // %2 745 "+r"(width) // %3 // Output registers 746 : // Input registers 747 : "cc", "memory", "q0", "q1" // Clobber List 748 ); 749 } 750 751 // Copy multiple of 32. vld4.8 allow unaligned and is fastest on a15. 752 void CopyRow_NEON(const uint8* src, uint8* dst, int count) { 753 asm volatile ( 754 ".p2align 2 \n" 755 "1: \n" 756 "vld1.8 {d0, d1, d2, d3}, [%0]! \n" // load 32 757 "subs %2, %2, #32 \n" // 32 processed per loop 758 "vst1.8 {d0, d1, d2, d3}, [%1]! \n" // store 32 759 "bgt 1b \n" 760 : "+r"(src), // %0 761 "+r"(dst), // %1 762 "+r"(count) // %2 // Output registers 763 : // Input registers 764 : "cc", "memory", "q0", "q1" // Clobber List 765 ); 766 } 767 768 // SetRow8 writes 'count' bytes using a 32 bit value repeated. 769 void SetRow_NEON(uint8* dst, uint32 v32, int count) { 770 asm volatile ( 771 "vdup.u32 q0, %2 \n" // duplicate 4 ints 772 "1: \n" 773 "subs %1, %1, #16 \n" // 16 bytes per loop 774 "vst1.8 {q0}, [%0]! \n" // store 775 "bgt 1b \n" 776 : "+r"(dst), // %0 777 "+r"(count) // %1 778 : "r"(v32) // %2 779 : "cc", "memory", "q0" 780 ); 781 } 782 783 // TODO(fbarchard): Make fully assembler 784 // SetRow32 writes 'count' words using a 32 bit value repeated. 785 void ARGBSetRows_NEON(uint8* dst, uint32 v32, int width, 786 int dst_stride, int height) { 787 for (int y = 0; y < height; ++y) { 788 SetRow_NEON(dst, v32, width << 2); 789 dst += dst_stride; 790 } 791 } 792 793 void MirrorRow_NEON(const uint8* src, uint8* dst, int width) { 794 asm volatile ( 795 // Start at end of source row. 796 "mov r3, #-16 \n" 797 "add %0, %0, %2 \n" 798 "sub %0, #16 \n" 799 800 ".p2align 2 \n" 801 "1: \n" 802 "vld1.8 {q0}, [%0], r3 \n" // src -= 16 803 "subs %2, #16 \n" // 16 pixels per loop. 804 "vrev64.8 q0, q0 \n" 805 "vst1.8 {d1}, [%1]! \n" // dst += 16 806 "vst1.8 {d0}, [%1]! \n" 807 "bgt 1b \n" 808 : "+r"(src), // %0 809 "+r"(dst), // %1 810 "+r"(width) // %2 811 : 812 : "cc", "memory", "r3", "q0" 813 ); 814 } 815 816 void MirrorUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v, 817 int width) { 818 asm volatile ( 819 // Start at end of source row. 820 "mov r12, #-16 \n" 821 "add %0, %0, %3, lsl #1 \n" 822 "sub %0, #16 \n" 823 824 ".p2align 2 \n" 825 "1: \n" 826 "vld2.8 {d0, d1}, [%0], r12 \n" // src -= 16 827 "subs %3, #8 \n" // 8 pixels per loop. 828 "vrev64.8 q0, q0 \n" 829 "vst1.8 {d0}, [%1]! \n" // dst += 8 830 "vst1.8 {d1}, [%2]! \n" 831 "bgt 1b \n" 832 : "+r"(src_uv), // %0 833 "+r"(dst_u), // %1 834 "+r"(dst_v), // %2 835 "+r"(width) // %3 836 : 837 : "cc", "memory", "r12", "q0" 838 ); 839 } 840 841 void ARGBMirrorRow_NEON(const uint8* src, uint8* dst, int width) { 842 asm volatile ( 843 // Start at end of source row. 844 "mov r3, #-16 \n" 845 "add %0, %0, %2, lsl #2 \n" 846 "sub %0, #16 \n" 847 848 ".p2align 2 \n" 849 "1: \n" 850 "vld1.8 {q0}, [%0], r3 \n" // src -= 16 851 "subs %2, #4 \n" // 4 pixels per loop. 852 "vrev64.32 q0, q0 \n" 853 "vst1.8 {d1}, [%1]! \n" // dst += 16 854 "vst1.8 {d0}, [%1]! \n" 855 "bgt 1b \n" 856 : "+r"(src), // %0 857 "+r"(dst), // %1 858 "+r"(width) // %2 859 : 860 : "cc", "memory", "r3", "q0" 861 ); 862 } 863 864 void RGB24ToARGBRow_NEON(const uint8* src_rgb24, uint8* dst_argb, int pix) { 865 asm volatile ( 866 "vmov.u8 d4, #255 \n" // Alpha 867 ".p2align 2 \n" 868 "1: \n" 869 "vld3.8 {d1, d2, d3}, [%0]! \n" // load 8 pixels of RGB24. 870 "subs %2, %2, #8 \n" // 8 processed per loop. 871 "vst4.8 {d1, d2, d3, d4}, [%1]! \n" // store 8 pixels of ARGB. 872 "bgt 1b \n" 873 : "+r"(src_rgb24), // %0 874 "+r"(dst_argb), // %1 875 "+r"(pix) // %2 876 : 877 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List 878 ); 879 } 880 881 void RAWToARGBRow_NEON(const uint8* src_raw, uint8* dst_argb, int pix) { 882 asm volatile ( 883 "vmov.u8 d4, #255 \n" // Alpha 884 ".p2align 2 \n" 885 "1: \n" 886 "vld3.8 {d1, d2, d3}, [%0]! \n" // load 8 pixels of RAW. 887 "subs %2, %2, #8 \n" // 8 processed per loop. 888 "vswp.u8 d1, d3 \n" // swap R, B 889 "vst4.8 {d1, d2, d3, d4}, [%1]! \n" // store 8 pixels of ARGB. 890 "bgt 1b \n" 891 : "+r"(src_raw), // %0 892 "+r"(dst_argb), // %1 893 "+r"(pix) // %2 894 : 895 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List 896 ); 897 } 898 899 #define RGB565TOARGB \ 900 "vshrn.u16 d6, q0, #5 \n" /* G xxGGGGGG */ \ 901 "vuzp.u8 d0, d1 \n" /* d0 xxxBBBBB RRRRRxxx */ \ 902 "vshl.u8 d6, d6, #2 \n" /* G GGGGGG00 upper 6 */ \ 903 "vshr.u8 d1, d1, #3 \n" /* R 000RRRRR lower 5 */ \ 904 "vshl.u8 q0, q0, #3 \n" /* B,R BBBBB000 upper 5 */ \ 905 "vshr.u8 q2, q0, #5 \n" /* B,R 00000BBB lower 3 */ \ 906 "vorr.u8 d0, d0, d4 \n" /* B */ \ 907 "vshr.u8 d4, d6, #6 \n" /* G 000000GG lower 2 */ \ 908 "vorr.u8 d2, d1, d5 \n" /* R */ \ 909 "vorr.u8 d1, d4, d6 \n" /* G */ 910 911 void RGB565ToARGBRow_NEON(const uint8* src_rgb565, uint8* dst_argb, int pix) { 912 asm volatile ( 913 "vmov.u8 d3, #255 \n" // Alpha 914 ".p2align 2 \n" 915 "1: \n" 916 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels. 917 "subs %2, %2, #8 \n" // 8 processed per loop. 918 RGB565TOARGB 919 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB. 920 "bgt 1b \n" 921 : "+r"(src_rgb565), // %0 922 "+r"(dst_argb), // %1 923 "+r"(pix) // %2 924 : 925 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List 926 ); 927 } 928 929 #define ARGB1555TOARGB \ 930 "vshrn.u16 d7, q0, #8 \n" /* A Arrrrrxx */ \ 931 "vshr.u8 d6, d7, #2 \n" /* R xxxRRRRR */ \ 932 "vshrn.u16 d5, q0, #5 \n" /* G xxxGGGGG */ \ 933 "vmovn.u16 d4, q0 \n" /* B xxxBBBBB */ \ 934 "vshr.u8 d7, d7, #7 \n" /* A 0000000A */ \ 935 "vneg.s8 d7, d7 \n" /* A AAAAAAAA upper 8 */ \ 936 "vshl.u8 d6, d6, #3 \n" /* R RRRRR000 upper 5 */ \ 937 "vshr.u8 q1, q3, #5 \n" /* R,A 00000RRR lower 3 */ \ 938 "vshl.u8 q0, q2, #3 \n" /* B,G BBBBB000 upper 5 */ \ 939 "vshr.u8 q2, q0, #5 \n" /* B,G 00000BBB lower 3 */ \ 940 "vorr.u8 q1, q1, q3 \n" /* R,A */ \ 941 "vorr.u8 q0, q0, q2 \n" /* B,G */ \ 942 943 // RGB555TOARGB is same as ARGB1555TOARGB but ignores alpha. 944 #define RGB555TOARGB \ 945 "vshrn.u16 d6, q0, #5 \n" /* G xxxGGGGG */ \ 946 "vuzp.u8 d0, d1 \n" /* d0 xxxBBBBB xRRRRRxx */ \ 947 "vshl.u8 d6, d6, #3 \n" /* G GGGGG000 upper 5 */ \ 948 "vshr.u8 d1, d1, #2 \n" /* R 00xRRRRR lower 5 */ \ 949 "vshl.u8 q0, q0, #3 \n" /* B,R BBBBB000 upper 5 */ \ 950 "vshr.u8 q2, q0, #5 \n" /* B,R 00000BBB lower 3 */ \ 951 "vorr.u8 d0, d0, d4 \n" /* B */ \ 952 "vshr.u8 d4, d6, #5 \n" /* G 00000GGG lower 3 */ \ 953 "vorr.u8 d2, d1, d5 \n" /* R */ \ 954 "vorr.u8 d1, d4, d6 \n" /* G */ 955 956 void ARGB1555ToARGBRow_NEON(const uint8* src_argb1555, uint8* dst_argb, 957 int pix) { 958 asm volatile ( 959 "vmov.u8 d3, #255 \n" // Alpha 960 ".p2align 2 \n" 961 "1: \n" 962 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels. 963 "subs %2, %2, #8 \n" // 8 processed per loop. 964 ARGB1555TOARGB 965 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB. 966 "bgt 1b \n" 967 : "+r"(src_argb1555), // %0 968 "+r"(dst_argb), // %1 969 "+r"(pix) // %2 970 : 971 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List 972 ); 973 } 974 975 #define ARGB4444TOARGB \ 976 "vuzp.u8 d0, d1 \n" /* d0 BG, d1 RA */ \ 977 "vshl.u8 q2, q0, #4 \n" /* B,R BBBB0000 */ \ 978 "vshr.u8 q1, q0, #4 \n" /* G,A 0000GGGG */ \ 979 "vshr.u8 q0, q2, #4 \n" /* B,R 0000BBBB */ \ 980 "vorr.u8 q0, q0, q2 \n" /* B,R BBBBBBBB */ \ 981 "vshl.u8 q2, q1, #4 \n" /* G,A GGGG0000 */ \ 982 "vorr.u8 q1, q1, q2 \n" /* G,A GGGGGGGG */ \ 983 "vswp.u8 d1, d2 \n" /* B,R,G,A -> B,G,R,A */ 984 985 void ARGB4444ToARGBRow_NEON(const uint8* src_argb4444, uint8* dst_argb, 986 int pix) { 987 asm volatile ( 988 "vmov.u8 d3, #255 \n" // Alpha 989 ".p2align 2 \n" 990 "1: \n" 991 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels. 992 "subs %2, %2, #8 \n" // 8 processed per loop. 993 ARGB4444TOARGB 994 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB. 995 "bgt 1b \n" 996 : "+r"(src_argb4444), // %0 997 "+r"(dst_argb), // %1 998 "+r"(pix) // %2 999 : 1000 : "cc", "memory", "q0", "q1", "q2" // Clobber List 1001 ); 1002 } 1003 1004 void ARGBToRGB24Row_NEON(const uint8* src_argb, uint8* dst_rgb24, int pix) { 1005 asm volatile ( 1006 ".p2align 2 \n" 1007 "1: \n" 1008 "vld4.8 {d1, d2, d3, d4}, [%0]! \n" // load 8 pixels of ARGB. 1009 "subs %2, %2, #8 \n" // 8 processed per loop. 1010 "vst3.8 {d1, d2, d3}, [%1]! \n" // store 8 pixels of RGB24. 1011 "bgt 1b \n" 1012 : "+r"(src_argb), // %0 1013 "+r"(dst_rgb24), // %1 1014 "+r"(pix) // %2 1015 : 1016 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List 1017 ); 1018 } 1019 1020 void ARGBToRAWRow_NEON(const uint8* src_argb, uint8* dst_raw, int pix) { 1021 asm volatile ( 1022 ".p2align 2 \n" 1023 "1: \n" 1024 "vld4.8 {d1, d2, d3, d4}, [%0]! \n" // load 8 pixels of ARGB. 1025 "subs %2, %2, #8 \n" // 8 processed per loop. 1026 "vswp.u8 d1, d3 \n" // swap R, B 1027 "vst3.8 {d1, d2, d3}, [%1]! \n" // store 8 pixels of RAW. 1028 "bgt 1b \n" 1029 : "+r"(src_argb), // %0 1030 "+r"(dst_raw), // %1 1031 "+r"(pix) // %2 1032 : 1033 : "cc", "memory", "d1", "d2", "d3", "d4" // Clobber List 1034 ); 1035 } 1036 1037 void YUY2ToYRow_NEON(const uint8* src_yuy2, uint8* dst_y, int pix) { 1038 asm volatile ( 1039 ".p2align 2 \n" 1040 "1: \n" 1041 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pixels of YUY2. 1042 "subs %2, %2, #16 \n" // 16 processed per loop. 1043 "vst1.8 {q0}, [%1]! \n" // store 16 pixels of Y. 1044 "bgt 1b \n" 1045 : "+r"(src_yuy2), // %0 1046 "+r"(dst_y), // %1 1047 "+r"(pix) // %2 1048 : 1049 : "cc", "memory", "q0", "q1" // Clobber List 1050 ); 1051 } 1052 1053 void UYVYToYRow_NEON(const uint8* src_uyvy, uint8* dst_y, int pix) { 1054 asm volatile ( 1055 ".p2align 2 \n" 1056 "1: \n" 1057 "vld2.8 {q0, q1}, [%0]! \n" // load 16 pixels of UYVY. 1058 "subs %2, %2, #16 \n" // 16 processed per loop. 1059 "vst1.8 {q1}, [%1]! \n" // store 16 pixels of Y. 1060 "bgt 1b \n" 1061 : "+r"(src_uyvy), // %0 1062 "+r"(dst_y), // %1 1063 "+r"(pix) // %2 1064 : 1065 : "cc", "memory", "q0", "q1" // Clobber List 1066 ); 1067 } 1068 1069 void YUY2ToUV422Row_NEON(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v, 1070 int pix) { 1071 asm volatile ( 1072 ".p2align 2 \n" 1073 "1: \n" 1074 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of YUY2. 1075 "subs %3, %3, #16 \n" // 16 pixels = 8 UVs. 1076 "vst1.8 {d1}, [%1]! \n" // store 8 U. 1077 "vst1.8 {d3}, [%2]! \n" // store 8 V. 1078 "bgt 1b \n" 1079 : "+r"(src_yuy2), // %0 1080 "+r"(dst_u), // %1 1081 "+r"(dst_v), // %2 1082 "+r"(pix) // %3 1083 : 1084 : "cc", "memory", "d0", "d1", "d2", "d3" // Clobber List 1085 ); 1086 } 1087 1088 void UYVYToUV422Row_NEON(const uint8* src_uyvy, uint8* dst_u, uint8* dst_v, 1089 int pix) { 1090 asm volatile ( 1091 ".p2align 2 \n" 1092 "1: \n" 1093 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of UYVY. 1094 "subs %3, %3, #16 \n" // 16 pixels = 8 UVs. 1095 "vst1.8 {d0}, [%1]! \n" // store 8 U. 1096 "vst1.8 {d2}, [%2]! \n" // store 8 V. 1097 "bgt 1b \n" 1098 : "+r"(src_uyvy), // %0 1099 "+r"(dst_u), // %1 1100 "+r"(dst_v), // %2 1101 "+r"(pix) // %3 1102 : 1103 : "cc", "memory", "d0", "d1", "d2", "d3" // Clobber List 1104 ); 1105 } 1106 1107 void YUY2ToUVRow_NEON(const uint8* src_yuy2, int stride_yuy2, 1108 uint8* dst_u, uint8* dst_v, int pix) { 1109 asm volatile ( 1110 "add %1, %0, %1 \n" // stride + src_yuy2 1111 ".p2align 2 \n" 1112 "1: \n" 1113 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of YUY2. 1114 "subs %4, %4, #16 \n" // 16 pixels = 8 UVs. 1115 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load next row YUY2. 1116 "vrhadd.u8 d1, d1, d5 \n" // average rows of U 1117 "vrhadd.u8 d3, d3, d7 \n" // average rows of V 1118 "vst1.8 {d1}, [%2]! \n" // store 8 U. 1119 "vst1.8 {d3}, [%3]! \n" // store 8 V. 1120 "bgt 1b \n" 1121 : "+r"(src_yuy2), // %0 1122 "+r"(stride_yuy2), // %1 1123 "+r"(dst_u), // %2 1124 "+r"(dst_v), // %3 1125 "+r"(pix) // %4 1126 : 1127 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7" // Clobber List 1128 ); 1129 } 1130 1131 void UYVYToUVRow_NEON(const uint8* src_uyvy, int stride_uyvy, 1132 uint8* dst_u, uint8* dst_v, int pix) { 1133 asm volatile ( 1134 "add %1, %0, %1 \n" // stride + src_uyvy 1135 ".p2align 2 \n" 1136 "1: \n" 1137 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 16 pixels of UYVY. 1138 "subs %4, %4, #16 \n" // 16 pixels = 8 UVs. 1139 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load next row UYVY. 1140 "vrhadd.u8 d0, d0, d4 \n" // average rows of U 1141 "vrhadd.u8 d2, d2, d6 \n" // average rows of V 1142 "vst1.8 {d0}, [%2]! \n" // store 8 U. 1143 "vst1.8 {d2}, [%3]! \n" // store 8 V. 1144 "bgt 1b \n" 1145 : "+r"(src_uyvy), // %0 1146 "+r"(stride_uyvy), // %1 1147 "+r"(dst_u), // %2 1148 "+r"(dst_v), // %3 1149 "+r"(pix) // %4 1150 : 1151 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7" // Clobber List 1152 ); 1153 } 1154 1155 void HalfRow_NEON(const uint8* src_uv, int src_uv_stride, 1156 uint8* dst_uv, int pix) { 1157 asm volatile ( 1158 // change the stride to row 2 pointer 1159 "add %1, %0 \n" 1160 "1: \n" 1161 "vld1.8 {q0}, [%0]! \n" // load row 1 16 pixels. 1162 "subs %3, %3, #16 \n" // 16 processed per loop 1163 "vld1.8 {q1}, [%1]! \n" // load row 2 16 pixels. 1164 "vrhadd.u8 q0, q1 \n" // average row 1 and 2 1165 "vst1.8 {q0}, [%2]! \n" 1166 "bgt 1b \n" 1167 : "+r"(src_uv), // %0 1168 "+r"(src_uv_stride), // %1 1169 "+r"(dst_uv), // %2 1170 "+r"(pix) // %3 1171 : 1172 : "cc", "memory", "q0", "q1" // Clobber List 1173 ); 1174 } 1175 1176 // Select 2 channels from ARGB on alternating pixels. e.g. BGBGBGBG 1177 void ARGBToBayerRow_NEON(const uint8* src_argb, uint8* dst_bayer, 1178 uint32 selector, int pix) { 1179 asm volatile ( 1180 "vmov.u32 d6[0], %3 \n" // selector 1181 "1: \n" 1182 "vld1.8 {q0, q1}, [%0]! \n" // load row 8 pixels. 1183 "subs %2, %2, #8 \n" // 8 processed per loop 1184 "vtbl.8 d4, {d0, d1}, d6 \n" // look up 4 pixels 1185 "vtbl.8 d5, {d2, d3}, d6 \n" // look up 4 pixels 1186 "vtrn.u32 d4, d5 \n" // combine 8 pixels 1187 "vst1.8 {d4}, [%1]! \n" // store 8. 1188 "bgt 1b \n" 1189 : "+r"(src_argb), // %0 1190 "+r"(dst_bayer), // %1 1191 "+r"(pix) // %2 1192 : "r"(selector) // %3 1193 : "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List 1194 ); 1195 } 1196 1197 // Select G channels from ARGB. e.g. GGGGGGGG 1198 void ARGBToBayerGGRow_NEON(const uint8* src_argb, uint8* dst_bayer, 1199 uint32 /*selector*/, int pix) { 1200 asm volatile ( 1201 "1: \n" 1202 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load row 8 pixels. 1203 "subs %2, %2, #8 \n" // 8 processed per loop 1204 "vst1.8 {d1}, [%1]! \n" // store 8 G's. 1205 "bgt 1b \n" 1206 : "+r"(src_argb), // %0 1207 "+r"(dst_bayer), // %1 1208 "+r"(pix) // %2 1209 : 1210 : "cc", "memory", "q0", "q1" // Clobber List 1211 ); 1212 } 1213 1214 // For BGRAToARGB, ABGRToARGB, RGBAToARGB, and ARGBToRGBA. 1215 void ARGBShuffleRow_NEON(const uint8* src_argb, uint8* dst_argb, 1216 const uint8* shuffler, int pix) { 1217 asm volatile ( 1218 "vld1.8 {q2}, [%3] \n" // shuffler 1219 "1: \n" 1220 "vld1.8 {q0}, [%0]! \n" // load 4 pixels. 1221 "subs %2, %2, #4 \n" // 4 processed per loop 1222 "vtbl.8 d2, {d0, d1}, d4 \n" // look up 2 first pixels 1223 "vtbl.8 d3, {d0, d1}, d5 \n" // look up 2 next pixels 1224 "vst1.8 {q1}, [%1]! \n" // store 4. 1225 "bgt 1b \n" 1226 : "+r"(src_argb), // %0 1227 "+r"(dst_argb), // %1 1228 "+r"(pix) // %2 1229 : "r"(shuffler) // %3 1230 : "cc", "memory", "q0", "q1", "q2" // Clobber List 1231 ); 1232 } 1233 1234 void I422ToYUY2Row_NEON(const uint8* src_y, 1235 const uint8* src_u, 1236 const uint8* src_v, 1237 uint8* dst_yuy2, int width) { 1238 asm volatile ( 1239 ".p2align 2 \n" 1240 "1: \n" 1241 "vld2.8 {d0, d2}, [%0]! \n" // load 16 Ys 1242 "vld1.8 {d1}, [%1]! \n" // load 8 Us 1243 "vld1.8 {d3}, [%2]! \n" // load 8 Vs 1244 "subs %4, %4, #16 \n" // 16 pixels 1245 "vst4.8 {d0, d1, d2, d3}, [%3]! \n" // Store 8 YUY2/16 pixels. 1246 "bgt 1b \n" 1247 : "+r"(src_y), // %0 1248 "+r"(src_u), // %1 1249 "+r"(src_v), // %2 1250 "+r"(dst_yuy2), // %3 1251 "+r"(width) // %4 1252 : 1253 : "cc", "memory", "d0", "d1", "d2", "d3" 1254 ); 1255 } 1256 1257 void I422ToUYVYRow_NEON(const uint8* src_y, 1258 const uint8* src_u, 1259 const uint8* src_v, 1260 uint8* dst_uyvy, int width) { 1261 asm volatile ( 1262 ".p2align 2 \n" 1263 "1: \n" 1264 "vld2.8 {d1, d3}, [%0]! \n" // load 16 Ys 1265 "vld1.8 {d0}, [%1]! \n" // load 8 Us 1266 "vld1.8 {d2}, [%2]! \n" // load 8 Vs 1267 "subs %4, %4, #16 \n" // 16 pixels 1268 "vst4.8 {d0, d1, d2, d3}, [%3]! \n" // Store 8 UYVY/16 pixels. 1269 "bgt 1b \n" 1270 : "+r"(src_y), // %0 1271 "+r"(src_u), // %1 1272 "+r"(src_v), // %2 1273 "+r"(dst_uyvy), // %3 1274 "+r"(width) // %4 1275 : 1276 : "cc", "memory", "d0", "d1", "d2", "d3" 1277 ); 1278 } 1279 1280 void ARGBToRGB565Row_NEON(const uint8* src_argb, uint8* dst_rgb565, int pix) { 1281 asm volatile ( 1282 ".p2align 2 \n" 1283 "1: \n" 1284 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB. 1285 "subs %2, %2, #8 \n" // 8 processed per loop. 1286 ARGBTORGB565 1287 "vst1.8 {q0}, [%1]! \n" // store 8 pixels RGB565. 1288 "bgt 1b \n" 1289 : "+r"(src_argb), // %0 1290 "+r"(dst_rgb565), // %1 1291 "+r"(pix) // %2 1292 : 1293 : "cc", "memory", "q0", "q8", "q9", "q10", "q11" 1294 ); 1295 } 1296 1297 void ARGBToARGB1555Row_NEON(const uint8* src_argb, uint8* dst_argb1555, 1298 int pix) { 1299 asm volatile ( 1300 ".p2align 2 \n" 1301 "1: \n" 1302 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB. 1303 "subs %2, %2, #8 \n" // 8 processed per loop. 1304 ARGBTOARGB1555 1305 "vst1.8 {q0}, [%1]! \n" // store 8 pixels ARGB1555. 1306 "bgt 1b \n" 1307 : "+r"(src_argb), // %0 1308 "+r"(dst_argb1555), // %1 1309 "+r"(pix) // %2 1310 : 1311 : "cc", "memory", "q0", "q8", "q9", "q10", "q11" 1312 ); 1313 } 1314 1315 void ARGBToARGB4444Row_NEON(const uint8* src_argb, uint8* dst_argb4444, 1316 int pix) { 1317 asm volatile ( 1318 "vmov.u8 d4, #0x0f \n" // bits to clear with vbic. 1319 ".p2align 2 \n" 1320 "1: \n" 1321 "vld4.8 {d20, d21, d22, d23}, [%0]! \n" // load 8 pixels of ARGB. 1322 "subs %2, %2, #8 \n" // 8 processed per loop. 1323 ARGBTOARGB4444 1324 "vst1.8 {q0}, [%1]! \n" // store 8 pixels ARGB4444. 1325 "bgt 1b \n" 1326 : "+r"(src_argb), // %0 1327 "+r"(dst_argb4444), // %1 1328 "+r"(pix) // %2 1329 : 1330 : "cc", "memory", "q0", "q8", "q9", "q10", "q11" 1331 ); 1332 } 1333 1334 void ARGBToYRow_NEON(const uint8* src_argb, uint8* dst_y, int pix) { 1335 asm volatile ( 1336 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient 1337 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient 1338 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient 1339 "vmov.u8 d27, #16 \n" // Add 16 constant 1340 ".p2align 2 \n" 1341 "1: \n" 1342 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 1343 "subs %2, %2, #8 \n" // 8 processed per loop. 1344 "vmull.u8 q2, d0, d24 \n" // B 1345 "vmlal.u8 q2, d1, d25 \n" // G 1346 "vmlal.u8 q2, d2, d26 \n" // R 1347 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y 1348 "vqadd.u8 d0, d27 \n" 1349 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 1350 "bgt 1b \n" 1351 : "+r"(src_argb), // %0 1352 "+r"(dst_y), // %1 1353 "+r"(pix) // %2 1354 : 1355 : "cc", "memory", "q0", "q1", "q2", "q12", "q13" 1356 ); 1357 } 1358 1359 void ARGBToYJRow_NEON(const uint8* src_argb, uint8* dst_y, int pix) { 1360 asm volatile ( 1361 "vmov.u8 d24, #15 \n" // B * 0.11400 coefficient 1362 "vmov.u8 d25, #75 \n" // G * 0.58700 coefficient 1363 "vmov.u8 d26, #38 \n" // R * 0.29900 coefficient 1364 ".p2align 2 \n" 1365 "1: \n" 1366 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 1367 "subs %2, %2, #8 \n" // 8 processed per loop. 1368 "vmull.u8 q2, d0, d24 \n" // B 1369 "vmlal.u8 q2, d1, d25 \n" // G 1370 "vmlal.u8 q2, d2, d26 \n" // R 1371 "vqrshrun.s16 d0, q2, #7 \n" // 15 bit to 8 bit Y 1372 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 1373 "bgt 1b \n" 1374 : "+r"(src_argb), // %0 1375 "+r"(dst_y), // %1 1376 "+r"(pix) // %2 1377 : 1378 : "cc", "memory", "q0", "q1", "q2", "q12", "q13" 1379 ); 1380 } 1381 1382 // 8x1 pixels. 1383 void ARGBToUV444Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v, 1384 int pix) { 1385 asm volatile ( 1386 "vmov.u8 d24, #112 \n" // UB / VR 0.875 coefficient 1387 "vmov.u8 d25, #74 \n" // UG -0.5781 coefficient 1388 "vmov.u8 d26, #38 \n" // UR -0.2969 coefficient 1389 "vmov.u8 d27, #18 \n" // VB -0.1406 coefficient 1390 "vmov.u8 d28, #94 \n" // VG -0.7344 coefficient 1391 "vmov.u16 q15, #0x8080 \n" // 128.5 1392 ".p2align 2 \n" 1393 "1: \n" 1394 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 1395 "subs %3, %3, #8 \n" // 8 processed per loop. 1396 "vmull.u8 q2, d0, d24 \n" // B 1397 "vmlsl.u8 q2, d1, d25 \n" // G 1398 "vmlsl.u8 q2, d2, d26 \n" // R 1399 "vadd.u16 q2, q2, q15 \n" // +128 -> unsigned 1400 1401 "vmull.u8 q3, d2, d24 \n" // R 1402 "vmlsl.u8 q3, d1, d28 \n" // G 1403 "vmlsl.u8 q3, d0, d27 \n" // B 1404 "vadd.u16 q3, q3, q15 \n" // +128 -> unsigned 1405 1406 "vqshrn.u16 d0, q2, #8 \n" // 16 bit to 8 bit U 1407 "vqshrn.u16 d1, q3, #8 \n" // 16 bit to 8 bit V 1408 1409 "vst1.8 {d0}, [%1]! \n" // store 8 pixels U. 1410 "vst1.8 {d1}, [%2]! \n" // store 8 pixels V. 1411 "bgt 1b \n" 1412 : "+r"(src_argb), // %0 1413 "+r"(dst_u), // %1 1414 "+r"(dst_v), // %2 1415 "+r"(pix) // %3 1416 : 1417 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q12", "q13", "q14", "q15" 1418 ); 1419 } 1420 1421 // 16x1 pixels -> 8x1. pix is number of argb pixels. e.g. 16. 1422 void ARGBToUV422Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v, 1423 int pix) { 1424 asm volatile ( 1425 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1426 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1427 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1428 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1429 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1430 "vmov.u16 q15, #0x8080 \n" // 128.5 1431 ".p2align 2 \n" 1432 "1: \n" 1433 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels. 1434 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels. 1435 1436 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts. 1437 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1438 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts. 1439 1440 "subs %3, %3, #16 \n" // 16 processed per loop. 1441 "vmul.s16 q8, q0, q10 \n" // B 1442 "vmls.s16 q8, q1, q11 \n" // G 1443 "vmls.s16 q8, q2, q12 \n" // R 1444 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned 1445 1446 "vmul.s16 q9, q2, q10 \n" // R 1447 "vmls.s16 q9, q1, q14 \n" // G 1448 "vmls.s16 q9, q0, q13 \n" // B 1449 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned 1450 1451 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U 1452 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V 1453 1454 "vst1.8 {d0}, [%1]! \n" // store 8 pixels U. 1455 "vst1.8 {d1}, [%2]! \n" // store 8 pixels V. 1456 "bgt 1b \n" 1457 : "+r"(src_argb), // %0 1458 "+r"(dst_u), // %1 1459 "+r"(dst_v), // %2 1460 "+r"(pix) // %3 1461 : 1462 : "cc", "memory", "q0", "q1", "q2", "q3", 1463 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1464 ); 1465 } 1466 1467 // 32x1 pixels -> 8x1. pix is number of argb pixels. e.g. 32. 1468 void ARGBToUV411Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v, 1469 int pix) { 1470 asm volatile ( 1471 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1472 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1473 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1474 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1475 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1476 "vmov.u16 q15, #0x8080 \n" // 128.5 1477 ".p2align 2 \n" 1478 "1: \n" 1479 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels. 1480 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels. 1481 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts. 1482 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1483 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts. 1484 "vld4.8 {d8, d10, d12, d14}, [%0]! \n" // load 8 more ARGB pixels. 1485 "vld4.8 {d9, d11, d13, d15}, [%0]! \n" // load last 8 ARGB pixels. 1486 "vpaddl.u8 q4, q4 \n" // B 16 bytes -> 8 shorts. 1487 "vpaddl.u8 q5, q5 \n" // G 16 bytes -> 8 shorts. 1488 "vpaddl.u8 q6, q6 \n" // R 16 bytes -> 8 shorts. 1489 1490 "vpadd.u16 d0, d0, d1 \n" // B 16 shorts -> 8 shorts. 1491 "vpadd.u16 d1, d8, d9 \n" // B 1492 "vpadd.u16 d2, d2, d3 \n" // G 16 shorts -> 8 shorts. 1493 "vpadd.u16 d3, d10, d11 \n" // G 1494 "vpadd.u16 d4, d4, d5 \n" // R 16 shorts -> 8 shorts. 1495 "vpadd.u16 d5, d12, d13 \n" // R 1496 1497 "vrshr.u16 q0, q0, #1 \n" // 2x average 1498 "vrshr.u16 q1, q1, #1 \n" 1499 "vrshr.u16 q2, q2, #1 \n" 1500 1501 "subs %3, %3, #32 \n" // 32 processed per loop. 1502 "vmul.s16 q8, q0, q10 \n" // B 1503 "vmls.s16 q8, q1, q11 \n" // G 1504 "vmls.s16 q8, q2, q12 \n" // R 1505 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned 1506 "vmul.s16 q9, q2, q10 \n" // R 1507 "vmls.s16 q9, q1, q14 \n" // G 1508 "vmls.s16 q9, q0, q13 \n" // B 1509 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned 1510 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U 1511 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V 1512 "vst1.8 {d0}, [%1]! \n" // store 8 pixels U. 1513 "vst1.8 {d1}, [%2]! \n" // store 8 pixels V. 1514 "bgt 1b \n" 1515 : "+r"(src_argb), // %0 1516 "+r"(dst_u), // %1 1517 "+r"(dst_v), // %2 1518 "+r"(pix) // %3 1519 : 1520 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1521 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1522 ); 1523 } 1524 1525 // 16x2 pixels -> 8x1. pix is number of argb pixels. e.g. 16. 1526 #define RGBTOUV(QB, QG, QR) \ 1527 "vmul.s16 q8, " #QB ", q10 \n" /* B */ \ 1528 "vmls.s16 q8, " #QG ", q11 \n" /* G */ \ 1529 "vmls.s16 q8, " #QR ", q12 \n" /* R */ \ 1530 "vadd.u16 q8, q8, q15 \n" /* +128 -> unsigned */ \ 1531 "vmul.s16 q9, " #QR ", q10 \n" /* R */ \ 1532 "vmls.s16 q9, " #QG ", q14 \n" /* G */ \ 1533 "vmls.s16 q9, " #QB ", q13 \n" /* B */ \ 1534 "vadd.u16 q9, q9, q15 \n" /* +128 -> unsigned */ \ 1535 "vqshrn.u16 d0, q8, #8 \n" /* 16 bit to 8 bit U */ \ 1536 "vqshrn.u16 d1, q9, #8 \n" /* 16 bit to 8 bit V */ 1537 1538 // TODO(fbarchard): Consider vhadd vertical, then vpaddl horizontal, avoid shr. 1539 void ARGBToUVRow_NEON(const uint8* src_argb, int src_stride_argb, 1540 uint8* dst_u, uint8* dst_v, int pix) { 1541 asm volatile ( 1542 "add %1, %0, %1 \n" // src_stride + src_argb 1543 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1544 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1545 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1546 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1547 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1548 "vmov.u16 q15, #0x8080 \n" // 128.5 1549 ".p2align 2 \n" 1550 "1: \n" 1551 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels. 1552 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels. 1553 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts. 1554 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1555 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts. 1556 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ARGB pixels. 1557 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ARGB pixels. 1558 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts. 1559 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts. 1560 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts. 1561 1562 "vrshr.u16 q0, q0, #1 \n" // 2x average 1563 "vrshr.u16 q1, q1, #1 \n" 1564 "vrshr.u16 q2, q2, #1 \n" 1565 1566 "subs %4, %4, #16 \n" // 32 processed per loop. 1567 RGBTOUV(q0, q1, q2) 1568 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1569 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1570 "bgt 1b \n" 1571 : "+r"(src_argb), // %0 1572 "+r"(src_stride_argb), // %1 1573 "+r"(dst_u), // %2 1574 "+r"(dst_v), // %3 1575 "+r"(pix) // %4 1576 : 1577 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1578 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1579 ); 1580 } 1581 1582 // TODO(fbarchard): Subsample match C code. 1583 void ARGBToUVJRow_NEON(const uint8* src_argb, int src_stride_argb, 1584 uint8* dst_u, uint8* dst_v, int pix) { 1585 asm volatile ( 1586 "add %1, %0, %1 \n" // src_stride + src_argb 1587 "vmov.s16 q10, #127 / 2 \n" // UB / VR 0.500 coefficient 1588 "vmov.s16 q11, #84 / 2 \n" // UG -0.33126 coefficient 1589 "vmov.s16 q12, #43 / 2 \n" // UR -0.16874 coefficient 1590 "vmov.s16 q13, #20 / 2 \n" // VB -0.08131 coefficient 1591 "vmov.s16 q14, #107 / 2 \n" // VG -0.41869 coefficient 1592 "vmov.u16 q15, #0x8080 \n" // 128.5 1593 ".p2align 2 \n" 1594 "1: \n" 1595 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels. 1596 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels. 1597 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts. 1598 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1599 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts. 1600 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ARGB pixels. 1601 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ARGB pixels. 1602 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts. 1603 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts. 1604 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts. 1605 1606 "vrshr.u16 q0, q0, #1 \n" // 2x average 1607 "vrshr.u16 q1, q1, #1 \n" 1608 "vrshr.u16 q2, q2, #1 \n" 1609 1610 "subs %4, %4, #16 \n" // 32 processed per loop. 1611 RGBTOUV(q0, q1, q2) 1612 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1613 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1614 "bgt 1b \n" 1615 : "+r"(src_argb), // %0 1616 "+r"(src_stride_argb), // %1 1617 "+r"(dst_u), // %2 1618 "+r"(dst_v), // %3 1619 "+r"(pix) // %4 1620 : 1621 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1622 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1623 ); 1624 } 1625 1626 void BGRAToUVRow_NEON(const uint8* src_bgra, int src_stride_bgra, 1627 uint8* dst_u, uint8* dst_v, int pix) { 1628 asm volatile ( 1629 "add %1, %0, %1 \n" // src_stride + src_bgra 1630 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1631 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1632 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1633 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1634 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1635 "vmov.u16 q15, #0x8080 \n" // 128.5 1636 ".p2align 2 \n" 1637 "1: \n" 1638 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 BGRA pixels. 1639 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 BGRA pixels. 1640 "vpaddl.u8 q3, q3 \n" // B 16 bytes -> 8 shorts. 1641 "vpaddl.u8 q2, q2 \n" // G 16 bytes -> 8 shorts. 1642 "vpaddl.u8 q1, q1 \n" // R 16 bytes -> 8 shorts. 1643 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more BGRA pixels. 1644 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 BGRA pixels. 1645 "vpadal.u8 q3, q7 \n" // B 16 bytes -> 8 shorts. 1646 "vpadal.u8 q2, q6 \n" // G 16 bytes -> 8 shorts. 1647 "vpadal.u8 q1, q5 \n" // R 16 bytes -> 8 shorts. 1648 1649 "vrshr.u16 q1, q1, #1 \n" // 2x average 1650 "vrshr.u16 q2, q2, #1 \n" 1651 "vrshr.u16 q3, q3, #1 \n" 1652 1653 "subs %4, %4, #16 \n" // 32 processed per loop. 1654 RGBTOUV(q3, q2, q1) 1655 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1656 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1657 "bgt 1b \n" 1658 : "+r"(src_bgra), // %0 1659 "+r"(src_stride_bgra), // %1 1660 "+r"(dst_u), // %2 1661 "+r"(dst_v), // %3 1662 "+r"(pix) // %4 1663 : 1664 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1665 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1666 ); 1667 } 1668 1669 void ABGRToUVRow_NEON(const uint8* src_abgr, int src_stride_abgr, 1670 uint8* dst_u, uint8* dst_v, int pix) { 1671 asm volatile ( 1672 "add %1, %0, %1 \n" // src_stride + src_abgr 1673 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1674 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1675 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1676 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1677 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1678 "vmov.u16 q15, #0x8080 \n" // 128.5 1679 ".p2align 2 \n" 1680 "1: \n" 1681 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ABGR pixels. 1682 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ABGR pixels. 1683 "vpaddl.u8 q2, q2 \n" // B 16 bytes -> 8 shorts. 1684 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1685 "vpaddl.u8 q0, q0 \n" // R 16 bytes -> 8 shorts. 1686 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more ABGR pixels. 1687 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 ABGR pixels. 1688 "vpadal.u8 q2, q6 \n" // B 16 bytes -> 8 shorts. 1689 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts. 1690 "vpadal.u8 q0, q4 \n" // R 16 bytes -> 8 shorts. 1691 1692 "vrshr.u16 q0, q0, #1 \n" // 2x average 1693 "vrshr.u16 q1, q1, #1 \n" 1694 "vrshr.u16 q2, q2, #1 \n" 1695 1696 "subs %4, %4, #16 \n" // 32 processed per loop. 1697 RGBTOUV(q2, q1, q0) 1698 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1699 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1700 "bgt 1b \n" 1701 : "+r"(src_abgr), // %0 1702 "+r"(src_stride_abgr), // %1 1703 "+r"(dst_u), // %2 1704 "+r"(dst_v), // %3 1705 "+r"(pix) // %4 1706 : 1707 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1708 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1709 ); 1710 } 1711 1712 void RGBAToUVRow_NEON(const uint8* src_rgba, int src_stride_rgba, 1713 uint8* dst_u, uint8* dst_v, int pix) { 1714 asm volatile ( 1715 "add %1, %0, %1 \n" // src_stride + src_rgba 1716 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1717 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1718 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1719 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1720 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1721 "vmov.u16 q15, #0x8080 \n" // 128.5 1722 ".p2align 2 \n" 1723 "1: \n" 1724 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 RGBA pixels. 1725 "vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 RGBA pixels. 1726 "vpaddl.u8 q0, q1 \n" // B 16 bytes -> 8 shorts. 1727 "vpaddl.u8 q1, q2 \n" // G 16 bytes -> 8 shorts. 1728 "vpaddl.u8 q2, q3 \n" // R 16 bytes -> 8 shorts. 1729 "vld4.8 {d8, d10, d12, d14}, [%1]! \n" // load 8 more RGBA pixels. 1730 "vld4.8 {d9, d11, d13, d15}, [%1]! \n" // load last 8 RGBA pixels. 1731 "vpadal.u8 q0, q5 \n" // B 16 bytes -> 8 shorts. 1732 "vpadal.u8 q1, q6 \n" // G 16 bytes -> 8 shorts. 1733 "vpadal.u8 q2, q7 \n" // R 16 bytes -> 8 shorts. 1734 1735 "vrshr.u16 q0, q0, #1 \n" // 2x average 1736 "vrshr.u16 q1, q1, #1 \n" 1737 "vrshr.u16 q2, q2, #1 \n" 1738 1739 "subs %4, %4, #16 \n" // 32 processed per loop. 1740 RGBTOUV(q0, q1, q2) 1741 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1742 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1743 "bgt 1b \n" 1744 : "+r"(src_rgba), // %0 1745 "+r"(src_stride_rgba), // %1 1746 "+r"(dst_u), // %2 1747 "+r"(dst_v), // %3 1748 "+r"(pix) // %4 1749 : 1750 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1751 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1752 ); 1753 } 1754 1755 void RGB24ToUVRow_NEON(const uint8* src_rgb24, int src_stride_rgb24, 1756 uint8* dst_u, uint8* dst_v, int pix) { 1757 asm volatile ( 1758 "add %1, %0, %1 \n" // src_stride + src_rgb24 1759 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1760 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1761 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1762 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1763 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1764 "vmov.u16 q15, #0x8080 \n" // 128.5 1765 ".p2align 2 \n" 1766 "1: \n" 1767 "vld3.8 {d0, d2, d4}, [%0]! \n" // load 8 RGB24 pixels. 1768 "vld3.8 {d1, d3, d5}, [%0]! \n" // load next 8 RGB24 pixels. 1769 "vpaddl.u8 q0, q0 \n" // B 16 bytes -> 8 shorts. 1770 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1771 "vpaddl.u8 q2, q2 \n" // R 16 bytes -> 8 shorts. 1772 "vld3.8 {d8, d10, d12}, [%1]! \n" // load 8 more RGB24 pixels. 1773 "vld3.8 {d9, d11, d13}, [%1]! \n" // load last 8 RGB24 pixels. 1774 "vpadal.u8 q0, q4 \n" // B 16 bytes -> 8 shorts. 1775 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts. 1776 "vpadal.u8 q2, q6 \n" // R 16 bytes -> 8 shorts. 1777 1778 "vrshr.u16 q0, q0, #1 \n" // 2x average 1779 "vrshr.u16 q1, q1, #1 \n" 1780 "vrshr.u16 q2, q2, #1 \n" 1781 1782 "subs %4, %4, #16 \n" // 32 processed per loop. 1783 RGBTOUV(q0, q1, q2) 1784 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1785 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1786 "bgt 1b \n" 1787 : "+r"(src_rgb24), // %0 1788 "+r"(src_stride_rgb24), // %1 1789 "+r"(dst_u), // %2 1790 "+r"(dst_v), // %3 1791 "+r"(pix) // %4 1792 : 1793 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1794 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1795 ); 1796 } 1797 1798 void RAWToUVRow_NEON(const uint8* src_raw, int src_stride_raw, 1799 uint8* dst_u, uint8* dst_v, int pix) { 1800 asm volatile ( 1801 "add %1, %0, %1 \n" // src_stride + src_raw 1802 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1803 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1804 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1805 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1806 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1807 "vmov.u16 q15, #0x8080 \n" // 128.5 1808 ".p2align 2 \n" 1809 "1: \n" 1810 "vld3.8 {d0, d2, d4}, [%0]! \n" // load 8 RAW pixels. 1811 "vld3.8 {d1, d3, d5}, [%0]! \n" // load next 8 RAW pixels. 1812 "vpaddl.u8 q2, q2 \n" // B 16 bytes -> 8 shorts. 1813 "vpaddl.u8 q1, q1 \n" // G 16 bytes -> 8 shorts. 1814 "vpaddl.u8 q0, q0 \n" // R 16 bytes -> 8 shorts. 1815 "vld3.8 {d8, d10, d12}, [%1]! \n" // load 8 more RAW pixels. 1816 "vld3.8 {d9, d11, d13}, [%1]! \n" // load last 8 RAW pixels. 1817 "vpadal.u8 q2, q6 \n" // B 16 bytes -> 8 shorts. 1818 "vpadal.u8 q1, q5 \n" // G 16 bytes -> 8 shorts. 1819 "vpadal.u8 q0, q4 \n" // R 16 bytes -> 8 shorts. 1820 1821 "vrshr.u16 q0, q0, #1 \n" // 2x average 1822 "vrshr.u16 q1, q1, #1 \n" 1823 "vrshr.u16 q2, q2, #1 \n" 1824 1825 "subs %4, %4, #16 \n" // 32 processed per loop. 1826 RGBTOUV(q2, q1, q0) 1827 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1828 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1829 "bgt 1b \n" 1830 : "+r"(src_raw), // %0 1831 "+r"(src_stride_raw), // %1 1832 "+r"(dst_u), // %2 1833 "+r"(dst_v), // %3 1834 "+r"(pix) // %4 1835 : 1836 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1837 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1838 ); 1839 } 1840 1841 // 16x2 pixels -> 8x1. pix is number of argb pixels. e.g. 16. 1842 void RGB565ToUVRow_NEON(const uint8* src_rgb565, int src_stride_rgb565, 1843 uint8* dst_u, uint8* dst_v, int pix) { 1844 asm volatile ( 1845 "add %1, %0, %1 \n" // src_stride + src_argb 1846 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1847 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1848 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1849 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1850 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1851 "vmov.u16 q15, #0x8080 \n" // 128.5 1852 ".p2align 2 \n" 1853 "1: \n" 1854 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels. 1855 RGB565TOARGB 1856 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1857 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1858 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1859 "vld1.8 {q0}, [%0]! \n" // next 8 RGB565 pixels. 1860 RGB565TOARGB 1861 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 1862 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 1863 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 1864 1865 "vld1.8 {q0}, [%1]! \n" // load 8 RGB565 pixels. 1866 RGB565TOARGB 1867 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1868 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1869 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1870 "vld1.8 {q0}, [%1]! \n" // next 8 RGB565 pixels. 1871 RGB565TOARGB 1872 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 1873 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 1874 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 1875 1876 "vrshr.u16 q4, q4, #1 \n" // 2x average 1877 "vrshr.u16 q5, q5, #1 \n" 1878 "vrshr.u16 q6, q6, #1 \n" 1879 1880 "subs %4, %4, #16 \n" // 16 processed per loop. 1881 "vmul.s16 q8, q4, q10 \n" // B 1882 "vmls.s16 q8, q5, q11 \n" // G 1883 "vmls.s16 q8, q6, q12 \n" // R 1884 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned 1885 "vmul.s16 q9, q6, q10 \n" // R 1886 "vmls.s16 q9, q5, q14 \n" // G 1887 "vmls.s16 q9, q4, q13 \n" // B 1888 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned 1889 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U 1890 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V 1891 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1892 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1893 "bgt 1b \n" 1894 : "+r"(src_rgb565), // %0 1895 "+r"(src_stride_rgb565), // %1 1896 "+r"(dst_u), // %2 1897 "+r"(dst_v), // %3 1898 "+r"(pix) // %4 1899 : 1900 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1901 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1902 ); 1903 } 1904 1905 // 16x2 pixels -> 8x1. pix is number of argb pixels. e.g. 16. 1906 void ARGB1555ToUVRow_NEON(const uint8* src_argb1555, int src_stride_argb1555, 1907 uint8* dst_u, uint8* dst_v, int pix) { 1908 asm volatile ( 1909 "add %1, %0, %1 \n" // src_stride + src_argb 1910 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1911 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1912 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1913 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1914 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1915 "vmov.u16 q15, #0x8080 \n" // 128.5 1916 ".p2align 2 \n" 1917 "1: \n" 1918 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels. 1919 RGB555TOARGB 1920 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1921 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1922 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1923 "vld1.8 {q0}, [%0]! \n" // next 8 ARGB1555 pixels. 1924 RGB555TOARGB 1925 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 1926 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 1927 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 1928 1929 "vld1.8 {q0}, [%1]! \n" // load 8 ARGB1555 pixels. 1930 RGB555TOARGB 1931 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1932 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1933 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1934 "vld1.8 {q0}, [%1]! \n" // next 8 ARGB1555 pixels. 1935 RGB555TOARGB 1936 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 1937 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 1938 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 1939 1940 "vrshr.u16 q4, q4, #1 \n" // 2x average 1941 "vrshr.u16 q5, q5, #1 \n" 1942 "vrshr.u16 q6, q6, #1 \n" 1943 1944 "subs %4, %4, #16 \n" // 16 processed per loop. 1945 "vmul.s16 q8, q4, q10 \n" // B 1946 "vmls.s16 q8, q5, q11 \n" // G 1947 "vmls.s16 q8, q6, q12 \n" // R 1948 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned 1949 "vmul.s16 q9, q6, q10 \n" // R 1950 "vmls.s16 q9, q5, q14 \n" // G 1951 "vmls.s16 q9, q4, q13 \n" // B 1952 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned 1953 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U 1954 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V 1955 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 1956 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 1957 "bgt 1b \n" 1958 : "+r"(src_argb1555), // %0 1959 "+r"(src_stride_argb1555), // %1 1960 "+r"(dst_u), // %2 1961 "+r"(dst_v), // %3 1962 "+r"(pix) // %4 1963 : 1964 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 1965 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 1966 ); 1967 } 1968 1969 // 16x2 pixels -> 8x1. pix is number of argb pixels. e.g. 16. 1970 void ARGB4444ToUVRow_NEON(const uint8* src_argb4444, int src_stride_argb4444, 1971 uint8* dst_u, uint8* dst_v, int pix) { 1972 asm volatile ( 1973 "add %1, %0, %1 \n" // src_stride + src_argb 1974 "vmov.s16 q10, #112 / 2 \n" // UB / VR 0.875 coefficient 1975 "vmov.s16 q11, #74 / 2 \n" // UG -0.5781 coefficient 1976 "vmov.s16 q12, #38 / 2 \n" // UR -0.2969 coefficient 1977 "vmov.s16 q13, #18 / 2 \n" // VB -0.1406 coefficient 1978 "vmov.s16 q14, #94 / 2 \n" // VG -0.7344 coefficient 1979 "vmov.u16 q15, #0x8080 \n" // 128.5 1980 ".p2align 2 \n" 1981 "1: \n" 1982 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels. 1983 ARGB4444TOARGB 1984 "vpaddl.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1985 "vpaddl.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1986 "vpaddl.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1987 "vld1.8 {q0}, [%0]! \n" // next 8 ARGB4444 pixels. 1988 ARGB4444TOARGB 1989 "vpaddl.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 1990 "vpaddl.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 1991 "vpaddl.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 1992 1993 "vld1.8 {q0}, [%1]! \n" // load 8 ARGB4444 pixels. 1994 ARGB4444TOARGB 1995 "vpadal.u8 d8, d0 \n" // B 8 bytes -> 4 shorts. 1996 "vpadal.u8 d10, d1 \n" // G 8 bytes -> 4 shorts. 1997 "vpadal.u8 d12, d2 \n" // R 8 bytes -> 4 shorts. 1998 "vld1.8 {q0}, [%1]! \n" // next 8 ARGB4444 pixels. 1999 ARGB4444TOARGB 2000 "vpadal.u8 d9, d0 \n" // B 8 bytes -> 4 shorts. 2001 "vpadal.u8 d11, d1 \n" // G 8 bytes -> 4 shorts. 2002 "vpadal.u8 d13, d2 \n" // R 8 bytes -> 4 shorts. 2003 2004 "vrshr.u16 q4, q4, #1 \n" // 2x average 2005 "vrshr.u16 q5, q5, #1 \n" 2006 "vrshr.u16 q6, q6, #1 \n" 2007 2008 "subs %4, %4, #16 \n" // 16 processed per loop. 2009 "vmul.s16 q8, q4, q10 \n" // B 2010 "vmls.s16 q8, q5, q11 \n" // G 2011 "vmls.s16 q8, q6, q12 \n" // R 2012 "vadd.u16 q8, q8, q15 \n" // +128 -> unsigned 2013 "vmul.s16 q9, q6, q10 \n" // R 2014 "vmls.s16 q9, q5, q14 \n" // G 2015 "vmls.s16 q9, q4, q13 \n" // B 2016 "vadd.u16 q9, q9, q15 \n" // +128 -> unsigned 2017 "vqshrn.u16 d0, q8, #8 \n" // 16 bit to 8 bit U 2018 "vqshrn.u16 d1, q9, #8 \n" // 16 bit to 8 bit V 2019 "vst1.8 {d0}, [%2]! \n" // store 8 pixels U. 2020 "vst1.8 {d1}, [%3]! \n" // store 8 pixels V. 2021 "bgt 1b \n" 2022 : "+r"(src_argb4444), // %0 2023 "+r"(src_stride_argb4444), // %1 2024 "+r"(dst_u), // %2 2025 "+r"(dst_v), // %3 2026 "+r"(pix) // %4 2027 : 2028 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", 2029 "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" 2030 ); 2031 } 2032 2033 void RGB565ToYRow_NEON(const uint8* src_rgb565, uint8* dst_y, int pix) { 2034 asm volatile ( 2035 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient 2036 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient 2037 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient 2038 "vmov.u8 d27, #16 \n" // Add 16 constant 2039 ".p2align 2 \n" 2040 "1: \n" 2041 "vld1.8 {q0}, [%0]! \n" // load 8 RGB565 pixels. 2042 "subs %2, %2, #8 \n" // 8 processed per loop. 2043 RGB565TOARGB 2044 "vmull.u8 q2, d0, d24 \n" // B 2045 "vmlal.u8 q2, d1, d25 \n" // G 2046 "vmlal.u8 q2, d2, d26 \n" // R 2047 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y 2048 "vqadd.u8 d0, d27 \n" 2049 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2050 "bgt 1b \n" 2051 : "+r"(src_rgb565), // %0 2052 "+r"(dst_y), // %1 2053 "+r"(pix) // %2 2054 : 2055 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13" 2056 ); 2057 } 2058 2059 void ARGB1555ToYRow_NEON(const uint8* src_argb1555, uint8* dst_y, int pix) { 2060 asm volatile ( 2061 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient 2062 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient 2063 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient 2064 "vmov.u8 d27, #16 \n" // Add 16 constant 2065 ".p2align 2 \n" 2066 "1: \n" 2067 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB1555 pixels. 2068 "subs %2, %2, #8 \n" // 8 processed per loop. 2069 ARGB1555TOARGB 2070 "vmull.u8 q2, d0, d24 \n" // B 2071 "vmlal.u8 q2, d1, d25 \n" // G 2072 "vmlal.u8 q2, d2, d26 \n" // R 2073 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y 2074 "vqadd.u8 d0, d27 \n" 2075 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2076 "bgt 1b \n" 2077 : "+r"(src_argb1555), // %0 2078 "+r"(dst_y), // %1 2079 "+r"(pix) // %2 2080 : 2081 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13" 2082 ); 2083 } 2084 2085 void ARGB4444ToYRow_NEON(const uint8* src_argb4444, uint8* dst_y, int pix) { 2086 asm volatile ( 2087 "vmov.u8 d24, #13 \n" // B * 0.1016 coefficient 2088 "vmov.u8 d25, #65 \n" // G * 0.5078 coefficient 2089 "vmov.u8 d26, #33 \n" // R * 0.2578 coefficient 2090 "vmov.u8 d27, #16 \n" // Add 16 constant 2091 ".p2align 2 \n" 2092 "1: \n" 2093 "vld1.8 {q0}, [%0]! \n" // load 8 ARGB4444 pixels. 2094 "subs %2, %2, #8 \n" // 8 processed per loop. 2095 ARGB4444TOARGB 2096 "vmull.u8 q2, d0, d24 \n" // B 2097 "vmlal.u8 q2, d1, d25 \n" // G 2098 "vmlal.u8 q2, d2, d26 \n" // R 2099 "vqrshrun.s16 d0, q2, #7 \n" // 16 bit to 8 bit Y 2100 "vqadd.u8 d0, d27 \n" 2101 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2102 "bgt 1b \n" 2103 : "+r"(src_argb4444), // %0 2104 "+r"(dst_y), // %1 2105 "+r"(pix) // %2 2106 : 2107 : "cc", "memory", "q0", "q1", "q2", "q3", "q12", "q13" 2108 ); 2109 } 2110 2111 void BGRAToYRow_NEON(const uint8* src_bgra, uint8* dst_y, int pix) { 2112 asm volatile ( 2113 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient 2114 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient 2115 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient 2116 "vmov.u8 d7, #16 \n" // Add 16 constant 2117 ".p2align 2 \n" 2118 "1: \n" 2119 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of BGRA. 2120 "subs %2, %2, #8 \n" // 8 processed per loop. 2121 "vmull.u8 q8, d1, d4 \n" // R 2122 "vmlal.u8 q8, d2, d5 \n" // G 2123 "vmlal.u8 q8, d3, d6 \n" // B 2124 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y 2125 "vqadd.u8 d0, d7 \n" 2126 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2127 "bgt 1b \n" 2128 : "+r"(src_bgra), // %0 2129 "+r"(dst_y), // %1 2130 "+r"(pix) // %2 2131 : 2132 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8" 2133 ); 2134 } 2135 2136 void ABGRToYRow_NEON(const uint8* src_abgr, uint8* dst_y, int pix) { 2137 asm volatile ( 2138 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient 2139 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient 2140 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient 2141 "vmov.u8 d7, #16 \n" // Add 16 constant 2142 ".p2align 2 \n" 2143 "1: \n" 2144 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ABGR. 2145 "subs %2, %2, #8 \n" // 8 processed per loop. 2146 "vmull.u8 q8, d0, d4 \n" // R 2147 "vmlal.u8 q8, d1, d5 \n" // G 2148 "vmlal.u8 q8, d2, d6 \n" // B 2149 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y 2150 "vqadd.u8 d0, d7 \n" 2151 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2152 "bgt 1b \n" 2153 : "+r"(src_abgr), // %0 2154 "+r"(dst_y), // %1 2155 "+r"(pix) // %2 2156 : 2157 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8" 2158 ); 2159 } 2160 2161 void RGBAToYRow_NEON(const uint8* src_rgba, uint8* dst_y, int pix) { 2162 asm volatile ( 2163 "vmov.u8 d4, #13 \n" // B * 0.1016 coefficient 2164 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient 2165 "vmov.u8 d6, #33 \n" // R * 0.2578 coefficient 2166 "vmov.u8 d7, #16 \n" // Add 16 constant 2167 ".p2align 2 \n" 2168 "1: \n" 2169 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of RGBA. 2170 "subs %2, %2, #8 \n" // 8 processed per loop. 2171 "vmull.u8 q8, d1, d4 \n" // B 2172 "vmlal.u8 q8, d2, d5 \n" // G 2173 "vmlal.u8 q8, d3, d6 \n" // R 2174 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y 2175 "vqadd.u8 d0, d7 \n" 2176 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2177 "bgt 1b \n" 2178 : "+r"(src_rgba), // %0 2179 "+r"(dst_y), // %1 2180 "+r"(pix) // %2 2181 : 2182 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8" 2183 ); 2184 } 2185 2186 void RGB24ToYRow_NEON(const uint8* src_rgb24, uint8* dst_y, int pix) { 2187 asm volatile ( 2188 "vmov.u8 d4, #13 \n" // B * 0.1016 coefficient 2189 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient 2190 "vmov.u8 d6, #33 \n" // R * 0.2578 coefficient 2191 "vmov.u8 d7, #16 \n" // Add 16 constant 2192 ".p2align 2 \n" 2193 "1: \n" 2194 "vld3.8 {d0, d1, d2}, [%0]! \n" // load 8 pixels of RGB24. 2195 "subs %2, %2, #8 \n" // 8 processed per loop. 2196 "vmull.u8 q8, d0, d4 \n" // B 2197 "vmlal.u8 q8, d1, d5 \n" // G 2198 "vmlal.u8 q8, d2, d6 \n" // R 2199 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y 2200 "vqadd.u8 d0, d7 \n" 2201 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2202 "bgt 1b \n" 2203 : "+r"(src_rgb24), // %0 2204 "+r"(dst_y), // %1 2205 "+r"(pix) // %2 2206 : 2207 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8" 2208 ); 2209 } 2210 2211 void RAWToYRow_NEON(const uint8* src_raw, uint8* dst_y, int pix) { 2212 asm volatile ( 2213 "vmov.u8 d4, #33 \n" // R * 0.2578 coefficient 2214 "vmov.u8 d5, #65 \n" // G * 0.5078 coefficient 2215 "vmov.u8 d6, #13 \n" // B * 0.1016 coefficient 2216 "vmov.u8 d7, #16 \n" // Add 16 constant 2217 ".p2align 2 \n" 2218 "1: \n" 2219 "vld3.8 {d0, d1, d2}, [%0]! \n" // load 8 pixels of RAW. 2220 "subs %2, %2, #8 \n" // 8 processed per loop. 2221 "vmull.u8 q8, d0, d4 \n" // B 2222 "vmlal.u8 q8, d1, d5 \n" // G 2223 "vmlal.u8 q8, d2, d6 \n" // R 2224 "vqrshrun.s16 d0, q8, #7 \n" // 16 bit to 8 bit Y 2225 "vqadd.u8 d0, d7 \n" 2226 "vst1.8 {d0}, [%1]! \n" // store 8 pixels Y. 2227 "bgt 1b \n" 2228 : "+r"(src_raw), // %0 2229 "+r"(dst_y), // %1 2230 "+r"(pix) // %2 2231 : 2232 : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "q8" 2233 ); 2234 } 2235 2236 // Bilinear filter 16x2 -> 16x1 2237 void InterpolateRow_NEON(uint8* dst_ptr, 2238 const uint8* src_ptr, ptrdiff_t src_stride, 2239 int dst_width, int source_y_fraction) { 2240 asm volatile ( 2241 "cmp %4, #0 \n" 2242 "beq 100f \n" 2243 "add %2, %1 \n" 2244 "cmp %4, #64 \n" 2245 "beq 75f \n" 2246 "cmp %4, #128 \n" 2247 "beq 50f \n" 2248 "cmp %4, #192 \n" 2249 "beq 25f \n" 2250 2251 "vdup.8 d5, %4 \n" 2252 "rsb %4, #256 \n" 2253 "vdup.8 d4, %4 \n" 2254 // General purpose row blend. 2255 "1: \n" 2256 "vld1.8 {q0}, [%1]! \n" 2257 "vld1.8 {q1}, [%2]! \n" 2258 "subs %3, %3, #16 \n" 2259 "vmull.u8 q13, d0, d4 \n" 2260 "vmull.u8 q14, d1, d4 \n" 2261 "vmlal.u8 q13, d2, d5 \n" 2262 "vmlal.u8 q14, d3, d5 \n" 2263 "vrshrn.u16 d0, q13, #8 \n" 2264 "vrshrn.u16 d1, q14, #8 \n" 2265 "vst1.8 {q0}, [%0]! \n" 2266 "bgt 1b \n" 2267 "b 99f \n" 2268 2269 // Blend 25 / 75. 2270 "25: \n" 2271 "vld1.8 {q0}, [%1]! \n" 2272 "vld1.8 {q1}, [%2]! \n" 2273 "subs %3, %3, #16 \n" 2274 "vrhadd.u8 q0, q1 \n" 2275 "vrhadd.u8 q0, q1 \n" 2276 "vst1.8 {q0}, [%0]! \n" 2277 "bgt 25b \n" 2278 "b 99f \n" 2279 2280 // Blend 50 / 50. 2281 "50: \n" 2282 "vld1.8 {q0}, [%1]! \n" 2283 "vld1.8 {q1}, [%2]! \n" 2284 "subs %3, %3, #16 \n" 2285 "vrhadd.u8 q0, q1 \n" 2286 "vst1.8 {q0}, [%0]! \n" 2287 "bgt 50b \n" 2288 "b 99f \n" 2289 2290 // Blend 75 / 25. 2291 "75: \n" 2292 "vld1.8 {q1}, [%1]! \n" 2293 "vld1.8 {q0}, [%2]! \n" 2294 "subs %3, %3, #16 \n" 2295 "vrhadd.u8 q0, q1 \n" 2296 "vrhadd.u8 q0, q1 \n" 2297 "vst1.8 {q0}, [%0]! \n" 2298 "bgt 75b \n" 2299 "b 99f \n" 2300 2301 // Blend 100 / 0 - Copy row unchanged. 2302 "100: \n" 2303 "vld1.8 {q0}, [%1]! \n" 2304 "subs %3, %3, #16 \n" 2305 "vst1.8 {q0}, [%0]! \n" 2306 "bgt 100b \n" 2307 2308 "99: \n" 2309 : "+r"(dst_ptr), // %0 2310 "+r"(src_ptr), // %1 2311 "+r"(src_stride), // %2 2312 "+r"(dst_width), // %3 2313 "+r"(source_y_fraction) // %4 2314 : 2315 : "cc", "memory", "q0", "q1", "d4", "d5", "q13", "q14" 2316 ); 2317 } 2318 2319 // dr * (256 - sa) / 256 + sr = dr - dr * sa / 256 + sr 2320 void ARGBBlendRow_NEON(const uint8* src_argb0, const uint8* src_argb1, 2321 uint8* dst_argb, int width) { 2322 asm volatile ( 2323 "subs %3, #8 \n" 2324 "blt 89f \n" 2325 // Blend 8 pixels. 2326 "8: \n" 2327 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ARGB0. 2328 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 pixels of ARGB1. 2329 "subs %3, %3, #8 \n" // 8 processed per loop. 2330 "vmull.u8 q10, d4, d3 \n" // db * a 2331 "vmull.u8 q11, d5, d3 \n" // dg * a 2332 "vmull.u8 q12, d6, d3 \n" // dr * a 2333 "vqrshrn.u16 d20, q10, #8 \n" // db >>= 8 2334 "vqrshrn.u16 d21, q11, #8 \n" // dg >>= 8 2335 "vqrshrn.u16 d22, q12, #8 \n" // dr >>= 8 2336 "vqsub.u8 q2, q2, q10 \n" // dbg - dbg * a / 256 2337 "vqsub.u8 d6, d6, d22 \n" // dr - dr * a / 256 2338 "vqadd.u8 q0, q0, q2 \n" // + sbg 2339 "vqadd.u8 d2, d2, d6 \n" // + sr 2340 "vmov.u8 d3, #255 \n" // a = 255 2341 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 pixels of ARGB. 2342 "bge 8b \n" 2343 2344 "89: \n" 2345 "adds %3, #8-1 \n" 2346 "blt 99f \n" 2347 2348 // Blend 1 pixels. 2349 "1: \n" 2350 "vld4.8 {d0[0],d1[0],d2[0],d3[0]}, [%0]! \n" // load 1 pixel ARGB0. 2351 "vld4.8 {d4[0],d5[0],d6[0],d7[0]}, [%1]! \n" // load 1 pixel ARGB1. 2352 "subs %3, %3, #1 \n" // 1 processed per loop. 2353 "vmull.u8 q10, d4, d3 \n" // db * a 2354 "vmull.u8 q11, d5, d3 \n" // dg * a 2355 "vmull.u8 q12, d6, d3 \n" // dr * a 2356 "vqrshrn.u16 d20, q10, #8 \n" // db >>= 8 2357 "vqrshrn.u16 d21, q11, #8 \n" // dg >>= 8 2358 "vqrshrn.u16 d22, q12, #8 \n" // dr >>= 8 2359 "vqsub.u8 q2, q2, q10 \n" // dbg - dbg * a / 256 2360 "vqsub.u8 d6, d6, d22 \n" // dr - dr * a / 256 2361 "vqadd.u8 q0, q0, q2 \n" // + sbg 2362 "vqadd.u8 d2, d2, d6 \n" // + sr 2363 "vmov.u8 d3, #255 \n" // a = 255 2364 "vst4.8 {d0[0],d1[0],d2[0],d3[0]}, [%2]! \n" // store 1 pixel. 2365 "bge 1b \n" 2366 2367 "99: \n" 2368 2369 : "+r"(src_argb0), // %0 2370 "+r"(src_argb1), // %1 2371 "+r"(dst_argb), // %2 2372 "+r"(width) // %3 2373 : 2374 : "cc", "memory", "q0", "q1", "q2", "q3", "q10", "q11", "q12" 2375 ); 2376 } 2377 2378 // Attenuate 8 pixels at a time. 2379 void ARGBAttenuateRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) { 2380 asm volatile ( 2381 // Attenuate 8 pixels. 2382 "1: \n" 2383 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 pixels of ARGB. 2384 "subs %2, %2, #8 \n" // 8 processed per loop. 2385 "vmull.u8 q10, d0, d3 \n" // b * a 2386 "vmull.u8 q11, d1, d3 \n" // g * a 2387 "vmull.u8 q12, d2, d3 \n" // r * a 2388 "vqrshrn.u16 d0, q10, #8 \n" // b >>= 8 2389 "vqrshrn.u16 d1, q11, #8 \n" // g >>= 8 2390 "vqrshrn.u16 d2, q12, #8 \n" // r >>= 8 2391 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 pixels of ARGB. 2392 "bgt 1b \n" 2393 : "+r"(src_argb), // %0 2394 "+r"(dst_argb), // %1 2395 "+r"(width) // %2 2396 : 2397 : "cc", "memory", "q0", "q1", "q10", "q11", "q12" 2398 ); 2399 } 2400 2401 // Quantize 8 ARGB pixels (32 bytes). 2402 // dst = (dst * scale >> 16) * interval_size + interval_offset; 2403 void ARGBQuantizeRow_NEON(uint8* dst_argb, int scale, int interval_size, 2404 int interval_offset, int width) { 2405 asm volatile ( 2406 "vdup.u16 q8, %2 \n" 2407 "vshr.u16 q8, q8, #1 \n" // scale >>= 1 2408 "vdup.u16 q9, %3 \n" // interval multiply. 2409 "vdup.u16 q10, %4 \n" // interval add 2410 2411 // 8 pixel loop. 2412 ".p2align 2 \n" 2413 "1: \n" 2414 "vld4.8 {d0, d2, d4, d6}, [%0] \n" // load 8 pixels of ARGB. 2415 "subs %1, %1, #8 \n" // 8 processed per loop. 2416 "vmovl.u8 q0, d0 \n" // b (0 .. 255) 2417 "vmovl.u8 q1, d2 \n" 2418 "vmovl.u8 q2, d4 \n" 2419 "vqdmulh.s16 q0, q0, q8 \n" // b * scale 2420 "vqdmulh.s16 q1, q1, q8 \n" // g 2421 "vqdmulh.s16 q2, q2, q8 \n" // r 2422 "vmul.u16 q0, q0, q9 \n" // b * interval_size 2423 "vmul.u16 q1, q1, q9 \n" // g 2424 "vmul.u16 q2, q2, q9 \n" // r 2425 "vadd.u16 q0, q0, q10 \n" // b + interval_offset 2426 "vadd.u16 q1, q1, q10 \n" // g 2427 "vadd.u16 q2, q2, q10 \n" // r 2428 "vqmovn.u16 d0, q0 \n" 2429 "vqmovn.u16 d2, q1 \n" 2430 "vqmovn.u16 d4, q2 \n" 2431 "vst4.8 {d0, d2, d4, d6}, [%0]! \n" // store 8 pixels of ARGB. 2432 "bgt 1b \n" 2433 : "+r"(dst_argb), // %0 2434 "+r"(width) // %1 2435 : "r"(scale), // %2 2436 "r"(interval_size), // %3 2437 "r"(interval_offset) // %4 2438 : "cc", "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10" 2439 ); 2440 } 2441 2442 // Shade 8 pixels at a time by specified value. 2443 // NOTE vqrdmulh.s16 q10, q10, d0[0] must use a scaler register from 0 to 8. 2444 // Rounding in vqrdmulh does +1 to high if high bit of low s16 is set. 2445 void ARGBShadeRow_NEON(const uint8* src_argb, uint8* dst_argb, int width, 2446 uint32 value) { 2447 asm volatile ( 2448 "vdup.u32 q0, %3 \n" // duplicate scale value. 2449 "vzip.u8 d0, d1 \n" // d0 aarrggbb. 2450 "vshr.u16 q0, q0, #1 \n" // scale / 2. 2451 2452 // 8 pixel loop. 2453 ".p2align 2 \n" 2454 "1: \n" 2455 "vld4.8 {d20, d22, d24, d26}, [%0]! \n" // load 8 pixels of ARGB. 2456 "subs %2, %2, #8 \n" // 8 processed per loop. 2457 "vmovl.u8 q10, d20 \n" // b (0 .. 255) 2458 "vmovl.u8 q11, d22 \n" 2459 "vmovl.u8 q12, d24 \n" 2460 "vmovl.u8 q13, d26 \n" 2461 "vqrdmulh.s16 q10, q10, d0[0] \n" // b * scale * 2 2462 "vqrdmulh.s16 q11, q11, d0[1] \n" // g 2463 "vqrdmulh.s16 q12, q12, d0[2] \n" // r 2464 "vqrdmulh.s16 q13, q13, d0[3] \n" // a 2465 "vqmovn.u16 d20, q10 \n" 2466 "vqmovn.u16 d22, q11 \n" 2467 "vqmovn.u16 d24, q12 \n" 2468 "vqmovn.u16 d26, q13 \n" 2469 "vst4.8 {d20, d22, d24, d26}, [%1]! \n" // store 8 pixels of ARGB. 2470 "bgt 1b \n" 2471 : "+r"(src_argb), // %0 2472 "+r"(dst_argb), // %1 2473 "+r"(width) // %2 2474 : "r"(value) // %3 2475 : "cc", "memory", "q0", "q10", "q11", "q12", "q13" 2476 ); 2477 } 2478 2479 // Convert 8 ARGB pixels (64 bytes) to 8 Gray ARGB pixels 2480 // Similar to ARGBToYJ but stores ARGB. 2481 // C code is (15 * b + 75 * g + 38 * r + 64) >> 7; 2482 void ARGBGrayRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) { 2483 asm volatile ( 2484 "vmov.u8 d24, #15 \n" // B * 0.11400 coefficient 2485 "vmov.u8 d25, #75 \n" // G * 0.58700 coefficient 2486 "vmov.u8 d26, #38 \n" // R * 0.29900 coefficient 2487 ".p2align 2 \n" 2488 "1: \n" 2489 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 2490 "subs %2, %2, #8 \n" // 8 processed per loop. 2491 "vmull.u8 q2, d0, d24 \n" // B 2492 "vmlal.u8 q2, d1, d25 \n" // G 2493 "vmlal.u8 q2, d2, d26 \n" // R 2494 "vqrshrun.s16 d0, q2, #7 \n" // 15 bit to 8 bit B 2495 "vmov d1, d0 \n" // G 2496 "vmov d2, d0 \n" // R 2497 "vst4.8 {d0, d1, d2, d3}, [%1]! \n" // store 8 ARGB pixels. 2498 "bgt 1b \n" 2499 : "+r"(src_argb), // %0 2500 "+r"(dst_argb), // %1 2501 "+r"(width) // %2 2502 : 2503 : "cc", "memory", "q0", "q1", "q2", "q12", "q13" 2504 ); 2505 } 2506 2507 // Convert 8 ARGB pixels (32 bytes) to 8 Sepia ARGB pixels. 2508 // b = (r * 35 + g * 68 + b * 17) >> 7 2509 // g = (r * 45 + g * 88 + b * 22) >> 7 2510 // r = (r * 50 + g * 98 + b * 24) >> 7 2511 void ARGBSepiaRow_NEON(uint8* dst_argb, int width) { 2512 asm volatile ( 2513 "vmov.u8 d20, #17 \n" // BB coefficient 2514 "vmov.u8 d21, #68 \n" // BG coefficient 2515 "vmov.u8 d22, #35 \n" // BR coefficient 2516 "vmov.u8 d24, #22 \n" // GB coefficient 2517 "vmov.u8 d25, #88 \n" // GG coefficient 2518 "vmov.u8 d26, #45 \n" // GR coefficient 2519 "vmov.u8 d28, #24 \n" // BB coefficient 2520 "vmov.u8 d29, #98 \n" // BG coefficient 2521 "vmov.u8 d30, #50 \n" // BR coefficient 2522 ".p2align 2 \n" 2523 "1: \n" 2524 "vld4.8 {d0, d1, d2, d3}, [%0] \n" // load 8 ARGB pixels. 2525 "subs %1, %1, #8 \n" // 8 processed per loop. 2526 "vmull.u8 q2, d0, d20 \n" // B to Sepia B 2527 "vmlal.u8 q2, d1, d21 \n" // G 2528 "vmlal.u8 q2, d2, d22 \n" // R 2529 "vmull.u8 q3, d0, d24 \n" // B to Sepia G 2530 "vmlal.u8 q3, d1, d25 \n" // G 2531 "vmlal.u8 q3, d2, d26 \n" // R 2532 "vmull.u8 q8, d0, d28 \n" // B to Sepia R 2533 "vmlal.u8 q8, d1, d29 \n" // G 2534 "vmlal.u8 q8, d2, d30 \n" // R 2535 "vqshrn.u16 d0, q2, #7 \n" // 16 bit to 8 bit B 2536 "vqshrn.u16 d1, q3, #7 \n" // 16 bit to 8 bit G 2537 "vqshrn.u16 d2, q8, #7 \n" // 16 bit to 8 bit R 2538 "vst4.8 {d0, d1, d2, d3}, [%0]! \n" // store 8 ARGB pixels. 2539 "bgt 1b \n" 2540 : "+r"(dst_argb), // %0 2541 "+r"(width) // %1 2542 : 2543 : "cc", "memory", "q0", "q1", "q2", "q3", 2544 "q10", "q11", "q12", "q13", "q14", "q15" 2545 ); 2546 } 2547 2548 // Tranform 8 ARGB pixels (32 bytes) with color matrix. 2549 // TODO(fbarchard): Was same as Sepia except matrix is provided. This function 2550 // needs to saturate. Consider doing a non-saturating version. 2551 void ARGBColorMatrixRow_NEON(const uint8* src_argb, uint8* dst_argb, 2552 const int8* matrix_argb, int width) { 2553 asm volatile ( 2554 "vld1.8 {q2}, [%3] \n" // load 3 ARGB vectors. 2555 "vmovl.s8 q0, d4 \n" // B,G coefficients s16. 2556 "vmovl.s8 q1, d5 \n" // R,A coefficients s16. 2557 2558 ".p2align 2 \n" 2559 "1: \n" 2560 "vld4.8 {d16, d18, d20, d22}, [%0]! \n" // load 8 ARGB pixels. 2561 "subs %2, %2, #8 \n" // 8 processed per loop. 2562 "vmovl.u8 q8, d16 \n" // b (0 .. 255) 16 bit 2563 "vmovl.u8 q9, d18 \n" // g 2564 "vmovl.u8 q10, d20 \n" // r 2565 "vmovl.u8 q15, d22 \n" // a 2566 "vmul.s16 q12, q8, d0[0] \n" // B = B * Matrix B 2567 "vmul.s16 q13, q8, d1[0] \n" // G = B * Matrix G 2568 "vmul.s16 q14, q8, d2[0] \n" // R = B * Matrix R 2569 "vmul.s16 q15, q8, d3[0] \n" // A = B * Matrix A 2570 "vmul.s16 q4, q9, d0[1] \n" // B += G * Matrix B 2571 "vmul.s16 q5, q9, d1[1] \n" // G += G * Matrix G 2572 "vmul.s16 q6, q9, d2[1] \n" // R += G * Matrix R 2573 "vmul.s16 q7, q9, d3[1] \n" // A += G * Matrix A 2574 "vqadd.s16 q12, q12, q4 \n" // Accumulate B 2575 "vqadd.s16 q13, q13, q5 \n" // Accumulate G 2576 "vqadd.s16 q14, q14, q6 \n" // Accumulate R 2577 "vqadd.s16 q15, q15, q7 \n" // Accumulate A 2578 "vmul.s16 q4, q10, d0[2] \n" // B += R * Matrix B 2579 "vmul.s16 q5, q10, d1[2] \n" // G += R * Matrix G 2580 "vmul.s16 q6, q10, d2[2] \n" // R += R * Matrix R 2581 "vmul.s16 q7, q10, d3[2] \n" // A += R * Matrix A 2582 "vqadd.s16 q12, q12, q4 \n" // Accumulate B 2583 "vqadd.s16 q13, q13, q5 \n" // Accumulate G 2584 "vqadd.s16 q14, q14, q6 \n" // Accumulate R 2585 "vqadd.s16 q15, q15, q7 \n" // Accumulate A 2586 "vmul.s16 q4, q15, d0[3] \n" // B += A * Matrix B 2587 "vmul.s16 q5, q15, d1[3] \n" // G += A * Matrix G 2588 "vmul.s16 q6, q15, d2[3] \n" // R += A * Matrix R 2589 "vmul.s16 q7, q15, d3[3] \n" // A += A * Matrix A 2590 "vqadd.s16 q12, q12, q4 \n" // Accumulate B 2591 "vqadd.s16 q13, q13, q5 \n" // Accumulate G 2592 "vqadd.s16 q14, q14, q6 \n" // Accumulate R 2593 "vqadd.s16 q15, q15, q7 \n" // Accumulate A 2594 "vqshrun.s16 d16, q12, #6 \n" // 16 bit to 8 bit B 2595 "vqshrun.s16 d18, q13, #6 \n" // 16 bit to 8 bit G 2596 "vqshrun.s16 d20, q14, #6 \n" // 16 bit to 8 bit R 2597 "vqshrun.s16 d22, q15, #6 \n" // 16 bit to 8 bit A 2598 "vst4.8 {d16, d18, d20, d22}, [%1]! \n" // store 8 ARGB pixels. 2599 "bgt 1b \n" 2600 : "+r"(src_argb), // %0 2601 "+r"(dst_argb), // %1 2602 "+r"(width) // %2 2603 : "r"(matrix_argb) // %3 2604 : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", 2605 "q10", "q11", "q12", "q13", "q14", "q15" 2606 ); 2607 } 2608 2609 // TODO(fbarchard): fix vqshrun in ARGBMultiplyRow_NEON and reenable. 2610 #ifdef HAS_ARGBMULTIPLYROW_NEON 2611 // Multiply 2 rows of ARGB pixels together, 8 pixels at a time. 2612 void ARGBMultiplyRow_NEON(const uint8* src_argb0, const uint8* src_argb1, 2613 uint8* dst_argb, int width) { 2614 asm volatile ( 2615 // 8 pixel loop. 2616 ".p2align 2 \n" 2617 "1: \n" 2618 "vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels. 2619 "vld4.8 {d1, d3, d5, d7}, [%1]! \n" // load 8 more ARGB pixels. 2620 "subs %3, %3, #8 \n" // 8 processed per loop. 2621 "vmull.u8 q0, d0, d1 \n" // multiply B 2622 "vmull.u8 q1, d2, d3 \n" // multiply G 2623 "vmull.u8 q2, d4, d5 \n" // multiply R 2624 "vmull.u8 q3, d6, d7 \n" // multiply A 2625 "vrshrn.u16 d0, q0, #8 \n" // 16 bit to 8 bit B 2626 "vrshrn.u16 d1, q1, #8 \n" // 16 bit to 8 bit G 2627 "vrshrn.u16 d2, q2, #8 \n" // 16 bit to 8 bit R 2628 "vrshrn.u16 d3, q3, #8 \n" // 16 bit to 8 bit A 2629 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels. 2630 "bgt 1b \n" 2631 2632 : "+r"(src_argb0), // %0 2633 "+r"(src_argb1), // %1 2634 "+r"(dst_argb), // %2 2635 "+r"(width) // %3 2636 : 2637 : "cc", "memory", "q0", "q1", "q2", "q3" 2638 ); 2639 } 2640 #endif // HAS_ARGBMULTIPLYROW_NEON 2641 2642 // Add 2 rows of ARGB pixels together, 8 pixels at a time. 2643 void ARGBAddRow_NEON(const uint8* src_argb0, const uint8* src_argb1, 2644 uint8* dst_argb, int width) { 2645 asm volatile ( 2646 // 8 pixel loop. 2647 ".p2align 2 \n" 2648 "1: \n" 2649 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 2650 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 more ARGB pixels. 2651 "subs %3, %3, #8 \n" // 8 processed per loop. 2652 "vqadd.u8 q0, q0, q2 \n" // add B, G 2653 "vqadd.u8 q1, q1, q3 \n" // add R, A 2654 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels. 2655 "bgt 1b \n" 2656 2657 : "+r"(src_argb0), // %0 2658 "+r"(src_argb1), // %1 2659 "+r"(dst_argb), // %2 2660 "+r"(width) // %3 2661 : 2662 : "cc", "memory", "q0", "q1", "q2", "q3" 2663 ); 2664 } 2665 2666 // Subtract 2 rows of ARGB pixels, 8 pixels at a time. 2667 void ARGBSubtractRow_NEON(const uint8* src_argb0, const uint8* src_argb1, 2668 uint8* dst_argb, int width) { 2669 asm volatile ( 2670 // 8 pixel loop. 2671 ".p2align 2 \n" 2672 "1: \n" 2673 "vld4.8 {d0, d1, d2, d3}, [%0]! \n" // load 8 ARGB pixels. 2674 "vld4.8 {d4, d5, d6, d7}, [%1]! \n" // load 8 more ARGB pixels. 2675 "subs %3, %3, #8 \n" // 8 processed per loop. 2676 "vqsub.u8 q0, q0, q2 \n" // subtract B, G 2677 "vqsub.u8 q1, q1, q3 \n" // subtract R, A 2678 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels. 2679 "bgt 1b \n" 2680 2681 : "+r"(src_argb0), // %0 2682 "+r"(src_argb1), // %1 2683 "+r"(dst_argb), // %2 2684 "+r"(width) // %3 2685 : 2686 : "cc", "memory", "q0", "q1", "q2", "q3" 2687 ); 2688 } 2689 2690 // Adds Sobel X and Sobel Y and stores Sobel into ARGB. 2691 // A = 255 2692 // R = Sobel 2693 // G = Sobel 2694 // B = Sobel 2695 void SobelRow_NEON(const uint8* src_sobelx, const uint8* src_sobely, 2696 uint8* dst_argb, int width) { 2697 asm volatile ( 2698 "vmov.u8 d3, #255 \n" // alpha 2699 // 8 pixel loop. 2700 ".p2align 2 \n" 2701 "1: \n" 2702 "vld1.8 {d0}, [%0]! \n" // load 8 sobelx. 2703 "vld1.8 {d1}, [%1]! \n" // load 8 sobely. 2704 "subs %3, %3, #8 \n" // 8 processed per loop. 2705 "vqadd.u8 d0, d0, d1 \n" // add 2706 "vmov.u8 d1, d0 \n" 2707 "vmov.u8 d2, d0 \n" 2708 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels. 2709 "bgt 1b \n" 2710 : "+r"(src_sobelx), // %0 2711 "+r"(src_sobely), // %1 2712 "+r"(dst_argb), // %2 2713 "+r"(width) // %3 2714 : 2715 : "cc", "memory", "q0", "q1" 2716 ); 2717 } 2718 2719 // Adds Sobel X and Sobel Y and stores Sobel into plane. 2720 void SobelToPlaneRow_NEON(const uint8* src_sobelx, const uint8* src_sobely, 2721 uint8* dst_y, int width) { 2722 asm volatile ( 2723 // 16 pixel loop. 2724 ".p2align 2 \n" 2725 "1: \n" 2726 "vld1.8 {q0}, [%0]! \n" // load 16 sobelx. 2727 "vld1.8 {q1}, [%1]! \n" // load 16 sobely. 2728 "subs %3, %3, #16 \n" // 16 processed per loop. 2729 "vqadd.u8 q0, q0, q1 \n" // add 2730 "vst1.8 {q0}, [%2]! \n" // store 16 pixels. 2731 "bgt 1b \n" 2732 : "+r"(src_sobelx), // %0 2733 "+r"(src_sobely), // %1 2734 "+r"(dst_y), // %2 2735 "+r"(width) // %3 2736 : 2737 : "cc", "memory", "q0", "q1" 2738 ); 2739 } 2740 2741 // Mixes Sobel X, Sobel Y and Sobel into ARGB. 2742 // A = 255 2743 // R = Sobel X 2744 // G = Sobel 2745 // B = Sobel Y 2746 void SobelXYRow_NEON(const uint8* src_sobelx, const uint8* src_sobely, 2747 uint8* dst_argb, int width) { 2748 asm volatile ( 2749 "vmov.u8 d3, #255 \n" // alpha 2750 // 8 pixel loop. 2751 ".p2align 2 \n" 2752 "1: \n" 2753 "vld1.8 {d2}, [%0]! \n" // load 8 sobelx. 2754 "vld1.8 {d0}, [%1]! \n" // load 8 sobely. 2755 "subs %3, %3, #8 \n" // 8 processed per loop. 2756 "vqadd.u8 d1, d0, d2 \n" // add 2757 "vst4.8 {d0, d1, d2, d3}, [%2]! \n" // store 8 ARGB pixels. 2758 "bgt 1b \n" 2759 : "+r"(src_sobelx), // %0 2760 "+r"(src_sobely), // %1 2761 "+r"(dst_argb), // %2 2762 "+r"(width) // %3 2763 : 2764 : "cc", "memory", "q0", "q1" 2765 ); 2766 } 2767 2768 // SobelX as a matrix is 2769 // -1 0 1 2770 // -2 0 2 2771 // -1 0 1 2772 void SobelXRow_NEON(const uint8* src_y0, const uint8* src_y1, 2773 const uint8* src_y2, uint8* dst_sobelx, int width) { 2774 asm volatile ( 2775 ".p2align 2 \n" 2776 "1: \n" 2777 "vld1.8 {d0}, [%0],%5 \n" // top 2778 "vld1.8 {d1}, [%0],%6 \n" 2779 "vsubl.u8 q0, d0, d1 \n" 2780 "vld1.8 {d2}, [%1],%5 \n" // center * 2 2781 "vld1.8 {d3}, [%1],%6 \n" 2782 "vsubl.u8 q1, d2, d3 \n" 2783 "vadd.s16 q0, q0, q1 \n" 2784 "vadd.s16 q0, q0, q1 \n" 2785 "vld1.8 {d2}, [%2],%5 \n" // bottom 2786 "vld1.8 {d3}, [%2],%6 \n" 2787 "subs %4, %4, #8 \n" // 8 pixels 2788 "vsubl.u8 q1, d2, d3 \n" 2789 "vadd.s16 q0, q0, q1 \n" 2790 "vabs.s16 q0, q0 \n" 2791 "vqmovn.u16 d0, q0 \n" 2792 "vst1.8 {d0}, [%3]! \n" // store 8 sobelx 2793 "bgt 1b \n" 2794 : "+r"(src_y0), // %0 2795 "+r"(src_y1), // %1 2796 "+r"(src_y2), // %2 2797 "+r"(dst_sobelx), // %3 2798 "+r"(width) // %4 2799 : "r"(2), // %5 2800 "r"(6) // %6 2801 : "cc", "memory", "q0", "q1" // Clobber List 2802 ); 2803 } 2804 2805 // SobelY as a matrix is 2806 // -1 -2 -1 2807 // 0 0 0 2808 // 1 2 1 2809 void SobelYRow_NEON(const uint8* src_y0, const uint8* src_y1, 2810 uint8* dst_sobely, int width) { 2811 asm volatile ( 2812 ".p2align 2 \n" 2813 "1: \n" 2814 "vld1.8 {d0}, [%0],%4 \n" // left 2815 "vld1.8 {d1}, [%1],%4 \n" 2816 "vsubl.u8 q0, d0, d1 \n" 2817 "vld1.8 {d2}, [%0],%4 \n" // center * 2 2818 "vld1.8 {d3}, [%1],%4 \n" 2819 "vsubl.u8 q1, d2, d3 \n" 2820 "vadd.s16 q0, q0, q1 \n" 2821 "vadd.s16 q0, q0, q1 \n" 2822 "vld1.8 {d2}, [%0],%5 \n" // right 2823 "vld1.8 {d3}, [%1],%5 \n" 2824 "subs %3, %3, #8 \n" // 8 pixels 2825 "vsubl.u8 q1, d2, d3 \n" 2826 "vadd.s16 q0, q0, q1 \n" 2827 "vabs.s16 q0, q0 \n" 2828 "vqmovn.u16 d0, q0 \n" 2829 "vst1.8 {d0}, [%2]! \n" // store 8 sobely 2830 "bgt 1b \n" 2831 : "+r"(src_y0), // %0 2832 "+r"(src_y1), // %1 2833 "+r"(dst_sobely), // %2 2834 "+r"(width) // %3 2835 : "r"(1), // %4 2836 "r"(6) // %5 2837 : "cc", "memory", "q0", "q1" // Clobber List 2838 ); 2839 } 2840 #endif // __ARM_NEON__ 2841 2842 #ifdef __cplusplus 2843 } // extern "C" 2844 } // namespace libyuv 2845 #endif 2846