blob: df9e3917a4158b64d2c880bb2e38ff06cdc392ed [file]
/*
* Copyright 2026 The LibYuv Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "libyuv/row.h"
#if !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
#include <wasm_simd128.h>
#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif
#ifdef __wasm_relaxed_simd__
#define LIBYUV_WASM_SWIZZLE wasm_i8x16_relaxed_swizzle
#else
#define LIBYUV_WASM_SWIZZLE wasm_i8x16_swizzle
#endif
// V8 materializes v128.const values and arbitrary i8x16.shuffle masks with
// several instructions at every use, including inside loops. These helpers
// make constants opaque to clang so they are built once, before the loop,
// and kept in registers. Use swizzle with a LoadConst() table instead of
// i8x16.shuffle unless the pattern maps to a native instruction (unpack,
// 32-bit lane shuffle, concat/palignr, even/odd byte unzip).
// Loops that access ptr + 16 etc. use asm("" : "+r"(ptr)) so LLVM keeps the
// constant offsets in the load/store instructions instead of creating extra
// pointer induction variables.
static inline v128_t LoadConst(const void* p) {
asm("" : "+r"(p));
return wasm_v128_load(p);
}
static inline v128_t SplatConst8(int v) {
asm("" : "+r"(v));
return wasm_i8x16_splat(v);
}
static inline v128_t SplatConst16(int v) {
asm("" : "+r"(v));
return wasm_i16x8_splat(v);
}
static inline v128_t SplatConst32(int v) {
asm("" : "+r"(v));
return wasm_i32x4_splat(v);
}
#ifdef HAS_RAWTORGB24ROW_WASMSIMD
static const uint8_t kRawToRGB24SwizzleTable[96] = {
2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 1, 0, 128, 4, 3, 2, 7, 6, 5, 10, 9, 8, 13, 12,
11, 128, 15, 128, 1, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 2, 128, 128, 3, 2, 1, 6, 5, 4, 9, 8, 7, 12,
11, 10, 15, 14, 13, 14, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128};
void RAWToRGB24Row_WASMSIMD(const uint8_t* src_raw,
uint8_t* dst_rgb24,
int width) {
const uint8_t* shuf = kRawToRGB24SwizzleTable;
// Load shuffle masks into Wasm locals outside the loop so Binaryen's
// precompute-propagate pass does not sink v128.const into the loop.
asm("" : "+r"(shuf));
v128_t kShuf0_0 = wasm_v128_load(shuf);
v128_t kShuf0_1 = wasm_v128_load(shuf + 16);
v128_t kShuf1_1 = wasm_v128_load(shuf + 32);
v128_t kShuf1_02 = wasm_v128_load(shuf + 48);
v128_t kShuf2_2 = wasm_v128_load(shuf + 64);
v128_t kShuf2_1 = wasm_v128_load(shuf + 80);
while (width >= 32) {
// Prevent LLVM LSR from splitting constant load/store offsets into separate
// pointer induction variables.
asm("" : "+r"(src_raw), "+r"(dst_rgb24));
v128_t s0 = wasm_v128_load(src_raw);
v128_t s1 = wasm_v128_load(src_raw + 16);
v128_t s2 = wasm_v128_load(src_raw + 32);
v128_t s3 = wasm_v128_load(src_raw + 48);
v128_t s4 = wasm_v128_load(src_raw + 64);
v128_t s5 = wasm_v128_load(src_raw + 80);
v128_t s02 = wasm_i8x16_shuffle(s0, s2, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29);
v128_t s35 = wasm_i8x16_shuffle(s3, s5, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29);
v128_t d0 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s0, kShuf0_0),
LIBYUV_WASM_SWIZZLE(s1, kShuf0_1));
v128_t d1 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s1, kShuf1_1),
LIBYUV_WASM_SWIZZLE(s02, kShuf1_02));
v128_t d2 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s2, kShuf2_2),
LIBYUV_WASM_SWIZZLE(s1, kShuf2_1));
v128_t d3 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s3, kShuf0_0),
LIBYUV_WASM_SWIZZLE(s4, kShuf0_1));
v128_t d4 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s4, kShuf1_1),
LIBYUV_WASM_SWIZZLE(s35, kShuf1_02));
v128_t d5 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s5, kShuf2_2),
LIBYUV_WASM_SWIZZLE(s4, kShuf2_1));
wasm_v128_store(dst_rgb24, d0);
wasm_v128_store(dst_rgb24 + 16, d1);
wasm_v128_store(dst_rgb24 + 32, d2);
wasm_v128_store(dst_rgb24 + 48, d3);
wasm_v128_store(dst_rgb24 + 64, d4);
wasm_v128_store(dst_rgb24 + 80, d5);
src_raw += 96;
dst_rgb24 += 96;
width -= 32;
}
if (width > 0) {
asm("" : "+r"(src_raw), "+r"(dst_rgb24));
v128_t s0 = wasm_v128_load(src_raw);
v128_t s1 = wasm_v128_load(src_raw + 16);
v128_t s2 = wasm_v128_load(src_raw + 32);
v128_t s02 = wasm_i8x16_shuffle(s0, s2, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29);
v128_t d0 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s0, kShuf0_0),
LIBYUV_WASM_SWIZZLE(s1, kShuf0_1));
v128_t d1 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s1, kShuf1_1),
LIBYUV_WASM_SWIZZLE(s02, kShuf1_02));
v128_t d2 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s2, kShuf2_2),
LIBYUV_WASM_SWIZZLE(s1, kShuf2_1));
wasm_v128_store(dst_rgb24, d0);
wasm_v128_store(dst_rgb24 + 16, d1);
wasm_v128_store(dst_rgb24 + 32, d2);
}
}
#endif // HAS_RAWTORGB24ROW_WASMSIMD
#if defined(HAS_RAWTOARGBROW_WASMSIMD) || defined(HAS_RGB24TOARGBROW_WASMSIMD)
// 16 pixels of 3 bytes to 4 bytes. shuf holds 2 masks: one for loads at
// offsets 0, 12, 24 and one for the load at offset 32 (avoids over-read).
static void RGB3ToARGBRow_WASMSIMD(const uint8_t* src,
uint8_t* dst_argb,
int width,
const uint8_t* shuf) {
v128_t m0 = LoadConst(shuf);
v128_t m1 = LoadConst(shuf + 16);
v128_t alpha = SplatConst32((int)0xff000000);
do {
asm("" : "+r"(src), "+r"(dst_argb));
v128_t d0 = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src), m0);
v128_t d1 = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src + 12), m0);
v128_t d2 = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src + 24), m0);
v128_t d3 = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src + 32), m1);
wasm_v128_store(dst_argb, wasm_v128_or(d0, alpha));
wasm_v128_store(dst_argb + 16, wasm_v128_or(d1, alpha));
wasm_v128_store(dst_argb + 32, wasm_v128_or(d2, alpha));
wasm_v128_store(dst_argb + 48, wasm_v128_or(d3, alpha));
src += 48;
dst_argb += 64;
width -= 16;
} while (width > 0);
}
#endif
#ifdef HAS_RAWTOARGBROW_WASMSIMD
static const uint8_t kShuffleRAWToARGB[32] = {
2, 1, 0, 128, 5, 4, 3, 128, 8, 7, 6, 128, 11, 10, 9, 128,
6, 5, 4, 128, 9, 8, 7, 128, 12, 11, 10, 128, 15, 14, 13, 128};
void RAWToARGBRow_WASMSIMD(const uint8_t* src_raw,
uint8_t* dst_argb,
int width) {
RGB3ToARGBRow_WASMSIMD(src_raw, dst_argb, width, kShuffleRAWToARGB);
}
#endif // HAS_RAWTOARGBROW_WASMSIMD
#ifdef HAS_RGB24TOARGBROW_WASMSIMD
static const uint8_t kShuffleRGB24ToARGB[32] = {
0, 1, 2, 128, 3, 4, 5, 128, 6, 7, 8, 128, 9, 10, 11, 128,
4, 5, 6, 128, 7, 8, 9, 128, 10, 11, 12, 128, 13, 14, 15, 128};
void RGB24ToARGBRow_WASMSIMD(const uint8_t* src_rgb24,
uint8_t* dst_argb,
int width) {
RGB3ToARGBRow_WASMSIMD(src_rgb24, dst_argb, width, kShuffleRGB24ToARGB);
}
#endif // HAS_RGB24TOARGBROW_WASMSIMD
#if defined(HAS_ARGBTOYMATRIXROW_WASMSIMD) || \
defined(HAS_ARGBTOUV444MATRIXROW_WASMSIMD) || \
defined(HAS_ARGBTOUVMATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOYMATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOUV444MATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOUVMATRIXROW_WASMSIMD) || \
defined(HAS_RGB565TOYMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB1555TOYMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB4444TOYMATRIXROW_WASMSIMD) || \
defined(HAS_RGB565TOUVMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB1555TOUVMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB4444TOUVMATRIXROW_WASMSIMD)
// RGB to YUV. Math is in 16 bit lanes, which wrap, but sum + bias fits in
// 16 bits so the results match the C code. i32x4.dot_i16x8_s would be best
// on x86 but is 3 instructions on Arm, so i16x8.mul (mul + mla on Arm) and
// horizontal adds that are cheap on both are used instead.
// lo * k0 + hi * k1.
static inline v128_t MulAdd16(v128_t lo, v128_t hi, v128_t k0, v128_t k1) {
return wasm_i16x8_add(wasm_i16x8_mul(lo, k0), wasm_i16x8_mul(hi, k1));
}
// Sums adjacent 16 bit lanes of t0 and t1.
static inline v128_t HAddPairs16(v128_t t0, v128_t t1) {
return wasm_i16x8_add(wasm_i16x8_shuffle(t0, t1, 0, 2, 4, 6, 8, 10, 12, 14),
wasm_i16x8_shuffle(t0, t1, 1, 3, 5, 7, 9, 11, 13, 15));
}
// Splats 16 bit {lo, hi} into each 32 bit lane.
static inline v128_t SplatPair16(int lo, int hi) {
return SplatConst32((lo & 0xffff) | (hi << 16));
}
// (bias - sum) >> 8 for U and V.
static inline v128_t SubShr8(v128_t bias, v128_t sum) {
return wasm_u16x8_shr(wasm_i16x8_sub(bias, sum), 8);
}
#endif
#if defined(HAS_ARGBTOUV444MATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOYMATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOUV444MATRIXROW_WASMSIMD)
// Swizzles 4 pixels to 16 bit {c0 x4, c2 x4} and {c1 x4, c3 x4}, so the 2
// halves of the products are summed with a 64 bit interleave.
static const uint8_t kShuffleARGBToPlanar[32] = {
0, 128, 4, 128, 8, 128, 12, 128, 2, 128, 6, 128, 10, 128, 14, 128,
1, 128, 5, 128, 9, 128, 13, 128, 3, 128, 7, 128, 11, 128, 15, 128};
// RGB24 to {b x4, r x4} and {g x4, 0 x4}. The second pair of masks is for
// the load at offset 32 (avoids over-read).
static const uint8_t kShuffleRGBToPlanar[64] = {
0, 128, 3, 128, 6, 128, 9, 128, 2, 128, 5, 128, 8, 128, 11, 128,
1, 128, 4, 128, 7, 128, 10, 128, 128, 128, 128, 128, 128, 128, 128, 128,
4, 128, 7, 128, 10, 128, 13, 128, 6, 128, 9, 128, 12, 128, 15, 128,
5, 128, 8, 128, 11, 128, 14, 128, 128, 128, 128, 128, 128, 128, 128, 128};
// {a x4, b x4} as 16 bit lanes.
static inline v128_t Splat4x2(int a, int b) {
asm("" : "+r"(a), "+r"(b));
return wasm_i16x8_make(a, a, a, a, b, b, b, b);
}
// Sums the 64 bit halves of t0 and t1.
static inline v128_t HAddHalves64(v128_t t0, v128_t t1) {
return wasm_i16x8_add(wasm_i64x2_shuffle(t0, t1, 0, 2),
wasm_i64x2_shuffle(t0, t1, 1, 3));
}
// 16 U and V from 16 pixels swizzled by m (m[0], m[1] for the first 3
// vectors, m[2], m[3] for the last).
static inline void PlanarToUV16(const v128_t* s,
const v128_t* m,
v128_t ku02,
v128_t ku13,
v128_t kv02,
v128_t kv13,
v128_t bias_u,
v128_t bias_v,
uint8_t* dst_u,
uint8_t* dst_v) {
v128_t u[4], v[4];
for (int i = 0; i < 4; ++i) {
v128_t lo = LIBYUV_WASM_SWIZZLE(s[i], m[i == 3 ? 2 : 0]);
v128_t hi = LIBYUV_WASM_SWIZZLE(s[i], m[i == 3 ? 3 : 1]);
u[i] = MulAdd16(lo, hi, ku02, ku13);
v[i] = MulAdd16(lo, hi, kv02, kv13);
}
wasm_v128_store(dst_u, wasm_u8x16_narrow_i16x8(
SubShr8(bias_u, HAddHalves64(u[0], u[1])),
SubShr8(bias_u, HAddHalves64(u[2], u[3]))));
wasm_v128_store(dst_v, wasm_u8x16_narrow_i16x8(
SubShr8(bias_v, HAddHalves64(v[0], v[1])),
SubShr8(bias_v, HAddHalves64(v[2], v[3]))));
}
#endif
#if defined(HAS_ARGBTOUVMATRIXROW_WASMSIMD) || \
defined(HAS_RGBTOUVMATRIXROW_WASMSIMD)
// Rounded 2x2 average of 8 pixels held as 16 bit {c0, c2} or {c1, c3}
// pairs. a = row 0, b = row 1, 4 pixels per vector. Returns 4 pixels.
static inline v128_t Avg2x2Pairs(v128_t a0,
v128_t a1,
v128_t b0,
v128_t b1,
v128_t two) {
v128_t s0 = wasm_i16x8_add(a0, b0);
v128_t s1 = wasm_i16x8_add(a1, b1);
v128_t s = wasm_i16x8_add(wasm_i32x4_shuffle(s0, s1, 0, 2, 4, 6),
wasm_i32x4_shuffle(s0, s1, 1, 3, 5, 7));
return wasm_u16x8_shr(wasm_i16x8_add(s, two), 2);
}
// Stores 8 U and 8 V from 2 vectors each of {c0, c2} and {c1, c3} pairs.
static inline void PairsToUV8(const v128_t* lo,
const v128_t* hi,
v128_t ku02,
v128_t ku13,
v128_t kv02,
v128_t kv13,
v128_t bias_u,
v128_t bias_v,
uint8_t* dst_u,
uint8_t* dst_v) {
v128_t u = HAddPairs16(MulAdd16(lo[0], hi[0], ku02, ku13),
MulAdd16(lo[1], hi[1], ku02, ku13));
v128_t v = HAddPairs16(MulAdd16(lo[0], hi[0], kv02, kv13),
MulAdd16(lo[1], hi[1], kv02, kv13));
v128_t uv = wasm_u8x16_narrow_i16x8(SubShr8(bias_u, u), SubShr8(bias_v, v));
wasm_v128_store64_lane(dst_u, uv, 0);
wasm_v128_store64_lane(dst_v, uv, 1);
}
#endif
#ifdef HAS_ARGBTOYMATRIXROW_WASMSIMD
void ARGBToYMatrixRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
v128_t mask = SplatConst16(0xff);
v128_t k02 = SplatPair16(c->kRGBToY[0], c->kRGBToY[2]);
v128_t k13 = SplatPair16(c->kRGBToY[1], c->kRGBToY[3]);
v128_t bias = SplatConst16(c->kAddY[0]);
do {
v128_t t[4];
for (int i = 0; i < 4; ++i) {
v128_t s = wasm_v128_load(src_argb + i * 16);
t[i] = MulAdd16(wasm_v128_and(s, mask), wasm_u16x8_shr(s, 8), k02, k13);
}
v128_t y0 = wasm_i16x8_add(HAddPairs16(t[0], t[1]), bias);
v128_t y1 = wasm_i16x8_add(HAddPairs16(t[2], t[3]), bias);
wasm_v128_store(dst_y, wasm_u8x16_narrow_i16x8(wasm_u16x8_shr(y0, 8),
wasm_u16x8_shr(y1, 8)));
src_argb += 64;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGBTOYMATRIXROW_WASMSIMD
#ifdef HAS_RGBTOYMATRIXROW_WASMSIMD
// RGB24 is alpha 255 in the C version, so 255 * kRGBToY[3] is in the bias.
void RGBToYMatrixRow_WASMSIMD(const uint8_t* src_rgb,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
v128_t m[4];
for (int i = 0; i < 4; ++i) {
m[i] = LoadConst(kShuffleRGBToPlanar + i * 16);
}
v128_t k02 = Splat4x2(c->kRGBToY[0], c->kRGBToY[2]);
v128_t k13 = Splat4x2(c->kRGBToY[1], 0);
v128_t bias = SplatConst16(c->kAddY[0] + 255 * c->kRGBToY[3]);
do {
v128_t t[4];
for (int i = 0; i < 4; ++i) {
v128_t s = wasm_v128_load(src_rgb + (i == 3 ? 32 : i * 12));
t[i] = MulAdd16(LIBYUV_WASM_SWIZZLE(s, m[i == 3 ? 2 : 0]),
LIBYUV_WASM_SWIZZLE(s, m[i == 3 ? 3 : 1]), k02, k13);
}
v128_t y0 = wasm_i16x8_add(HAddHalves64(t[0], t[1]), bias);
v128_t y1 = wasm_i16x8_add(HAddHalves64(t[2], t[3]), bias);
wasm_v128_store(dst_y, wasm_u8x16_narrow_i16x8(wasm_u16x8_shr(y0, 8),
wasm_u16x8_shr(y1, 8)));
src_rgb += 48;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGBTOYMATRIXROW_WASMSIMD
#ifdef HAS_ARGBTOUV444MATRIXROW_WASMSIMD
void ARGBToUV444MatrixRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
v128_t m[4];
m[0] = m[2] = LoadConst(kShuffleARGBToPlanar);
m[1] = m[3] = LoadConst(kShuffleARGBToPlanar + 16);
v128_t ku02 = Splat4x2(c->kRGBToU[0], c->kRGBToU[2]);
v128_t ku13 = Splat4x2(c->kRGBToU[1], c->kRGBToU[3]);
v128_t kv02 = Splat4x2(c->kRGBToV[0], c->kRGBToV[2]);
v128_t kv13 = Splat4x2(c->kRGBToV[1], c->kRGBToV[3]);
v128_t bias = SplatConst16(c->kAddUV[0]);
do {
v128_t s[4];
for (int i = 0; i < 4; ++i) {
s[i] = wasm_v128_load(src_argb + i * 16);
}
PlanarToUV16(s, m, ku02, ku13, kv02, kv13, bias, bias, dst_u, dst_v);
src_argb += 64;
dst_u += 16;
dst_v += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGBTOUV444MATRIXROW_WASMSIMD
#ifdef HAS_RGBTOUV444MATRIXROW_WASMSIMD
void RGBToUV444MatrixRow_WASMSIMD(const uint8_t* src_rgb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
v128_t m[4];
for (int i = 0; i < 4; ++i) {
m[i] = LoadConst(kShuffleRGBToPlanar + i * 16);
}
v128_t ku02 = Splat4x2(c->kRGBToU[0], c->kRGBToU[2]);
v128_t ku13 = Splat4x2(c->kRGBToU[1], 0);
v128_t kv02 = Splat4x2(c->kRGBToV[0], c->kRGBToV[2]);
v128_t kv13 = Splat4x2(c->kRGBToV[1], 0);
v128_t bias_u = SplatConst16(c->kAddUV[0] - 255 * c->kRGBToU[3]);
v128_t bias_v = SplatConst16(c->kAddUV[0] - 255 * c->kRGBToV[3]);
do {
v128_t s[4];
for (int i = 0; i < 4; ++i) {
s[i] = wasm_v128_load(src_rgb + (i == 3 ? 32 : i * 12));
}
PlanarToUV16(s, m, ku02, ku13, kv02, kv13, bias_u, bias_v, dst_u, dst_v);
src_rgb += 48;
dst_u += 16;
dst_v += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGBTOUV444MATRIXROW_WASMSIMD
#ifdef HAS_ARGBTOUVMATRIXROW_WASMSIMD
void ARGBToUVMatrixRow_WASMSIMD(const uint8_t* src_argb,
int src_stride_argb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_argb1 = src_argb + src_stride_argb;
v128_t mask = SplatConst16(0xff);
v128_t two = SplatConst16(2);
v128_t ku02 = SplatPair16(c->kRGBToU[0], c->kRGBToU[2]);
v128_t ku13 = SplatPair16(c->kRGBToU[1], c->kRGBToU[3]);
v128_t kv02 = SplatPair16(c->kRGBToV[0], c->kRGBToV[2]);
v128_t kv13 = SplatPair16(c->kRGBToV[1], c->kRGBToV[3]);
v128_t bias = SplatConst16(c->kAddUV[0]);
do {
v128_t lo[2], hi[2];
for (int i = 0; i < 2; ++i) {
v128_t a0 = wasm_v128_load(src_argb + i * 32);
v128_t a1 = wasm_v128_load(src_argb + i * 32 + 16);
v128_t b0 = wasm_v128_load(src_argb1 + i * 32);
v128_t b1 = wasm_v128_load(src_argb1 + i * 32 + 16);
lo[i] =
Avg2x2Pairs(wasm_v128_and(a0, mask), wasm_v128_and(a1, mask),
wasm_v128_and(b0, mask), wasm_v128_and(b1, mask), two);
hi[i] = Avg2x2Pairs(wasm_u16x8_shr(a0, 8), wasm_u16x8_shr(a1, 8),
wasm_u16x8_shr(b0, 8), wasm_u16x8_shr(b1, 8), two);
}
PairsToUV8(lo, hi, ku02, ku13, kv02, kv13, bias, bias, dst_u, dst_v);
src_argb += 64;
src_argb1 += 64;
dst_u += 8;
dst_v += 8;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGBTOUVMATRIXROW_WASMSIMD
#ifdef HAS_RGBTOUVMATRIXROW_WASMSIMD
// Swizzles 4 RGB24 pixels to 16 bit {b, r} and {g, 0} pairs. The second
// pair of masks is for the load at offset 32 (avoids over-read).
static const uint8_t kShuffleRGBToPairs[64] = {
0, 128, 2, 128, 3, 128, 5, 128, 6, 128, 8, 128, 9, 128, 11, 128,
1, 128, 128, 128, 4, 128, 128, 128, 7, 128, 128, 128, 10, 128, 128, 128,
4, 128, 6, 128, 7, 128, 9, 128, 10, 128, 12, 128, 13, 128, 15, 128,
5, 128, 128, 128, 8, 128, 128, 128, 11, 128, 128, 128, 14, 128, 128, 128};
void RGBToUVMatrixRow_WASMSIMD(const uint8_t* src_rgb,
int src_stride_rgb,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb;
v128_t m[4];
for (int i = 0; i < 4; ++i) {
m[i] = LoadConst(kShuffleRGBToPairs + i * 16);
}
v128_t two = SplatConst16(2);
v128_t ku02 = SplatPair16(c->kRGBToU[0], c->kRGBToU[2]);
v128_t ku13 = SplatPair16(c->kRGBToU[1], 0);
v128_t kv02 = SplatPair16(c->kRGBToV[0], c->kRGBToV[2]);
v128_t kv13 = SplatPair16(c->kRGBToV[1], 0);
v128_t bias_u = SplatConst16(c->kAddUV[0] - 255 * c->kRGBToU[3]);
v128_t bias_v = SplatConst16(c->kAddUV[0] - 255 * c->kRGBToV[3]);
do {
v128_t lo[2], hi[2];
for (int i = 0; i < 2; ++i) {
// Pixels 8 * i to 8 * i + 3 at offset 24 * i, the rest at 12 or 32.
int off = i ? 32 : 12;
v128_t a0 = wasm_v128_load(src_rgb + i * 24);
v128_t a1 = wasm_v128_load(src_rgb + off);
v128_t b0 = wasm_v128_load(src_rgb1 + i * 24);
v128_t b1 = wasm_v128_load(src_rgb1 + off);
v128_t ml = m[i * 2];
v128_t mh = m[i * 2 + 1];
lo[i] = Avg2x2Pairs(
LIBYUV_WASM_SWIZZLE(a0, m[0]), LIBYUV_WASM_SWIZZLE(a1, ml),
LIBYUV_WASM_SWIZZLE(b0, m[0]), LIBYUV_WASM_SWIZZLE(b1, ml), two);
hi[i] = Avg2x2Pairs(
LIBYUV_WASM_SWIZZLE(a0, m[1]), LIBYUV_WASM_SWIZZLE(a1, mh),
LIBYUV_WASM_SWIZZLE(b0, m[1]), LIBYUV_WASM_SWIZZLE(b1, mh), two);
}
PairsToUV8(lo, hi, ku02, ku13, kv02, kv13, bias_u, bias_v, dst_u, dst_v);
src_rgb += 48;
src_rgb1 += 48;
dst_u += 8;
dst_v += 8;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGBTOUVMATRIXROW_WASMSIMD
// 16 bit pixel formats keep each channel in its own 16 bit lane.
// 5 and 6 bit channels expand as (v * 33) >> 2 and (v * 65) >> 4, and 4 bit
// channels as v * 17, which match the C bit replication.
#if defined(HAS_RGB565TOUVMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB1555TOUVMATRIXROW_WASMSIMD) || \
defined(HAS_ARGB4444TOUVMATRIXROW_WASMSIMD)
// Rounded 2x2 average of 16 pixels of 1 channel. a = row 0, b = row 1.
static inline v128_t Avg2x2Lanes(v128_t a0,
v128_t a1,
v128_t b0,
v128_t b1,
v128_t two) {
v128_t s = HAddPairs16(wasm_i16x8_add(a0, b0), wasm_i16x8_add(a1, b1));
return wasm_u16x8_shr(wasm_i16x8_add(s, two), 2);
}
// Stores 8 U and 8 V from 8 pixels of 16 bit channels. k holds U
// coefficients for b, g, r, a, then V coefficients, then bias U and V.
static inline void ChannelsToUV8(v128_t b,
v128_t g,
v128_t r,
v128_t a,
const v128_t* k,
uint8_t* dst_u,
uint8_t* dst_v) {
v128_t u =
wasm_i16x8_add(MulAdd16(b, g, k[0], k[1]), MulAdd16(r, a, k[2], k[3]));
v128_t v =
wasm_i16x8_add(MulAdd16(b, g, k[4], k[5]), MulAdd16(r, a, k[6], k[7]));
v128_t uv = wasm_u8x16_narrow_i16x8(SubShr8(k[8], u), SubShr8(k[9], v));
wasm_v128_store64_lane(dst_u, uv, 0);
wasm_v128_store64_lane(dst_v, uv, 1);
}
// U and V coefficients for ChannelsToUV8. The alpha term is folded into the
// bias if alpha is always 255 (opaque).
static inline void LoadUVChannelConstants(const struct ArgbConstants* c,
int opaque,
v128_t* k) {
for (int i = 0; i < 4; ++i) {
k[i] = SplatConst16(c->kRGBToU[i]);
k[i + 4] = SplatConst16(c->kRGBToV[i]);
}
k[8] = SplatConst16(c->kAddUV[0] - opaque * 255 * c->kRGBToU[3]);
k[9] = SplatConst16(c->kAddUV[0] - opaque * 255 * c->kRGBToV[3]);
}
#endif
#ifdef HAS_RGB565TOYMATRIXROW_WASMSIMD
void RGB565ToYMatrixRow_WASMSIMD(const uint8_t* src_rgb565,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
v128_t m5 = SplatConst16(0x1f);
v128_t m6 = SplatConst16(0x3f);
v128_t k33 = SplatConst16(33);
v128_t k65 = SplatConst16(65);
v128_t kb = SplatConst16(c->kRGBToY[0]);
v128_t kg = SplatConst16(c->kRGBToY[1]);
v128_t kr = SplatConst16(c->kRGBToY[2]);
v128_t bias = SplatConst16(c->kAddY[0] + 255 * c->kRGBToY[3]);
do {
v128_t y[2];
for (int i = 0; i < 2; ++i) {
v128_t x = wasm_v128_load(src_rgb565 + i * 16);
v128_t b = wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(x, m5), k33), 2);
v128_t g = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 5), m6), k65), 4);
v128_t r = wasm_u16x8_shr(wasm_i16x8_mul(wasm_u16x8_shr(x, 11), k33), 2);
v128_t s = wasm_i16x8_add(wasm_i16x8_mul(b, kb), bias);
y[i] = wasm_u16x8_shr(wasm_i16x8_add(s, MulAdd16(g, r, kg, kr)), 8);
}
wasm_v128_store(dst_y, wasm_u8x16_narrow_i16x8(y[0], y[1]));
src_rgb565 += 32;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGB565TOYMATRIXROW_WASMSIMD
#ifdef HAS_ARGB1555TOYMATRIXROW_WASMSIMD
void ARGB1555ToYMatrixRow_WASMSIMD(const uint8_t* src_argb1555,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
v128_t m5 = SplatConst16(0x1f);
v128_t k33 = SplatConst16(33);
v128_t kb = SplatConst16(c->kRGBToY[0]);
v128_t kg = SplatConst16(c->kRGBToY[1]);
v128_t kr = SplatConst16(c->kRGBToY[2]);
v128_t ka = SplatConst16(255 * c->kRGBToY[3]);
v128_t bias = SplatConst16(c->kAddY[0]);
do {
v128_t y[2];
for (int i = 0; i < 2; ++i) {
v128_t x = wasm_v128_load(src_argb1555 + i * 16);
v128_t b = wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(x, m5), k33), 2);
v128_t g = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 5), m5), k33), 2);
v128_t r = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 10), m5), k33), 2);
// Alpha is 0 or 255.
v128_t s = wasm_i16x8_add(wasm_v128_and(wasm_i16x8_shr(x, 15), ka), bias);
s = wasm_i16x8_add(s, wasm_i16x8_mul(b, kb));
y[i] = wasm_u16x8_shr(wasm_i16x8_add(s, MulAdd16(g, r, kg, kr)), 8);
}
wasm_v128_store(dst_y, wasm_u8x16_narrow_i16x8(y[0], y[1]));
src_argb1555 += 32;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGB1555TOYMATRIXROW_WASMSIMD
#ifdef HAS_ARGB4444TOYMATRIXROW_WASMSIMD
// 4 bit channels expand as v * 17, so the coefficients are scaled by 17.
void ARGB4444ToYMatrixRow_WASMSIMD(const uint8_t* src_argb4444,
uint8_t* dst_y,
int width,
const struct ArgbConstants* c) {
v128_t m4 = SplatConst16(0x0f);
v128_t kb = SplatConst16(c->kRGBToY[0] * 17);
v128_t kg = SplatConst16(c->kRGBToY[1] * 17);
v128_t kr = SplatConst16(c->kRGBToY[2] * 17);
v128_t ka = SplatConst16(c->kRGBToY[3] * 17);
v128_t bias = SplatConst16(c->kAddY[0]);
do {
v128_t y[2];
for (int i = 0; i < 2; ++i) {
v128_t x = wasm_v128_load(src_argb4444 + i * 16);
v128_t s = wasm_i16x8_add(
MulAdd16(wasm_v128_and(x, m4), wasm_u16x8_shr(x, 12), kb, ka), bias);
s = wasm_i16x8_add(
s, MulAdd16(wasm_v128_and(wasm_u16x8_shr(x, 4), m4),
wasm_v128_and(wasm_u16x8_shr(x, 8), m4), kg, kr));
y[i] = wasm_u16x8_shr(s, 8);
}
wasm_v128_store(dst_y, wasm_u8x16_narrow_i16x8(y[0], y[1]));
src_argb4444 += 32;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGB4444TOYMATRIXROW_WASMSIMD
#ifdef HAS_RGB565TOUVMATRIXROW_WASMSIMD
void RGB565ToUVMatrixRow_WASMSIMD(const uint8_t* src_rgb565,
int src_stride_rgb565,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_rgb565_1 = src_rgb565 + src_stride_rgb565;
v128_t m5 = SplatConst16(0x1f);
v128_t m6 = SplatConst16(0x3f);
v128_t k33 = SplatConst16(33);
v128_t k65 = SplatConst16(65);
v128_t two = SplatConst16(2);
v128_t k[10];
LoadUVChannelConstants(c, 1, k);
do {
v128_t b[4], g[4], r[4];
for (int i = 0; i < 4; ++i) {
v128_t x =
wasm_v128_load((i & 2 ? src_rgb565_1 : src_rgb565) + (i & 1) * 16);
b[i] = wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(x, m5), k33), 2);
g[i] = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 5), m6), k65), 4);
r[i] = wasm_u16x8_shr(wasm_i16x8_mul(wasm_u16x8_shr(x, 11), k33), 2);
}
ChannelsToUV8(Avg2x2Lanes(b[0], b[1], b[2], b[3], two),
Avg2x2Lanes(g[0], g[1], g[2], g[3], two),
Avg2x2Lanes(r[0], r[1], r[2], r[3], two),
wasm_i16x8_const_splat(0), k, dst_u, dst_v);
src_rgb565 += 32;
src_rgb565_1 += 32;
dst_u += 8;
dst_v += 8;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGB565TOUVMATRIXROW_WASMSIMD
#ifdef HAS_ARGB1555TOUVMATRIXROW_WASMSIMD
void ARGB1555ToUVMatrixRow_WASMSIMD(const uint8_t* src_argb1555,
int src_stride_argb1555,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_argb1555_1 = src_argb1555 + src_stride_argb1555;
v128_t m5 = SplatConst16(0x1f);
v128_t k33 = SplatConst16(33);
v128_t two = SplatConst16(2);
v128_t k[10];
LoadUVChannelConstants(c, 0, k);
do {
v128_t b[4], g[4], r[4], a[4];
for (int i = 0; i < 4; ++i) {
v128_t x = wasm_v128_load((i & 2 ? src_argb1555_1 : src_argb1555) +
(i & 1) * 16);
b[i] = wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(x, m5), k33), 2);
g[i] = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 5), m5), k33), 2);
r[i] = wasm_u16x8_shr(
wasm_i16x8_mul(wasm_v128_and(wasm_u16x8_shr(x, 10), m5), k33), 2);
a[i] = wasm_u16x8_shr(wasm_i16x8_shr(x, 15), 8);
}
ChannelsToUV8(Avg2x2Lanes(b[0], b[1], b[2], b[3], two),
Avg2x2Lanes(g[0], g[1], g[2], g[3], two),
Avg2x2Lanes(r[0], r[1], r[2], r[3], two),
Avg2x2Lanes(a[0], a[1], a[2], a[3], two), k, dst_u, dst_v);
src_argb1555 += 32;
src_argb1555_1 += 32;
dst_u += 8;
dst_v += 8;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGB1555TOUVMATRIXROW_WASMSIMD
#ifdef HAS_ARGB4444TOUVMATRIXROW_WASMSIMD
// Sums the nibble pairs {b, r} and {g, a} of 2x2 pixels in bytes, then
// expands the sums by 17 before rounding.
void ARGB4444ToUVMatrixRow_WASMSIMD(const uint8_t* src_argb4444,
int src_stride_argb4444,
uint8_t* dst_u,
uint8_t* dst_v,
int width,
const struct ArgbConstants* c) {
const uint8_t* src_argb4444_1 = src_argb4444 + src_stride_argb4444;
v128_t m4 = SplatConst16(0x0f0f);
v128_t m8 = SplatConst16(0xff);
v128_t k17 = SplatConst16(17);
v128_t two = SplatConst16(2);
v128_t k[10];
LoadUVChannelConstants(c, 0, k);
do {
v128_t s[2], t[2];
for (int i = 0; i < 2; ++i) {
v128_t a = wasm_v128_load(src_argb4444 + i * 16);
v128_t b = wasm_v128_load(src_argb4444_1 + i * 16);
s[i] = wasm_i16x8_add(wasm_v128_and(a, m4), wasm_v128_and(b, m4));
t[i] = wasm_i16x8_add(wasm_v128_and(wasm_u16x8_shr(a, 4), m4),
wasm_v128_and(wasm_u16x8_shr(b, 4), m4));
}
v128_t br = HAddPairs16(s[0], s[1]);
v128_t ga = HAddPairs16(t[0], t[1]);
v128_t ch[4] = {wasm_v128_and(br, m8), wasm_v128_and(ga, m8),
wasm_u16x8_shr(br, 8), wasm_u16x8_shr(ga, 8)};
for (int i = 0; i < 4; ++i) {
ch[i] =
wasm_u16x8_shr(wasm_i16x8_add(wasm_i16x8_mul(ch[i], k17), two), 2);
}
ChannelsToUV8(ch[0], ch[1], ch[2], ch[3], k, dst_u, dst_v);
src_argb4444 += 32;
src_argb4444_1 += 32;
dst_u += 8;
dst_v += 8;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGB4444TOUVMATRIXROW_WASMSIMD
#ifdef HAS_CONVERT8TO16ROW_WASMSIMD
// For bits 8 to 16, (y * 0x0101) >> (16 - bits) == p + (p >> 8) where
// p = y << (bits - 8). This avoids a variable shift count.
void Convert8To16Row_WASMSIMD(const uint8_t* src_y,
uint16_t* dst_y,
int bits,
int width) {
if (bits < 8) {
Convert8To16Row_C(src_y, dst_y, bits, width);
return;
}
v128_t scale = SplatConst16(1 << (bits - 8));
do {
asm("" : "+r"(dst_y));
v128_t y = wasm_v128_load(src_y);
v128_t y0 = wasm_i16x8_mul(wasm_u16x8_extend_low_u8x16(y), scale);
v128_t y1 = wasm_i16x8_mul(wasm_u16x8_extend_high_u8x16(y), scale);
wasm_v128_store(dst_y, wasm_i16x8_add(y0, wasm_u16x8_shr(y0, 8)));
wasm_v128_store(dst_y + 8, wasm_i16x8_add(y1, wasm_u16x8_shr(y1, 8)));
src_y += 16;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_CONVERT8TO16ROW_WASMSIMD
#ifdef HAS_MULTIPLYROW_16_WASMSIMD
void MultiplyRow_16_WASMSIMD(const uint16_t* src_y,
uint16_t* dst_y,
int scale,
int width) {
v128_t vscale = wasm_i16x8_splat(scale);
do {
asm("" : "+r"(src_y), "+r"(dst_y));
v128_t y0 = wasm_v128_load(src_y);
v128_t y1 = wasm_v128_load(src_y + 8);
y0 = wasm_i16x8_mul(y0, vscale);
y1 = wasm_i16x8_mul(y1, vscale);
wasm_v128_store(dst_y, y0);
wasm_v128_store(dst_y + 8, y1);
src_y += 16;
dst_y += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_MULTIPLYROW_16_WASMSIMD
#ifdef HAS_MERGEUVROW_WASMSIMD
void MergeUVRow_WASMSIMD(const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_uv,
int width) {
do {
asm("" : "+r"(dst_uv));
v128_t u = wasm_v128_load(src_u);
v128_t v = wasm_v128_load(src_v);
v128_t uv0 = wasm_i8x16_shuffle(u, v, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5,
21, 6, 22, 7, 23);
v128_t uv1 = wasm_i8x16_shuffle(u, v, 8, 24, 9, 25, 10, 26, 11, 27, 12, 28,
13, 29, 14, 30, 15, 31);
wasm_v128_store(dst_uv, uv0);
wasm_v128_store(dst_uv + 16, uv1);
src_u += 16;
src_v += 16;
dst_uv += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_MERGEUVROW_WASMSIMD
#ifdef HAS_MIRRORROW_WASMSIMD
static const uint8_t kShuffleMirror[16] = {15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0};
void MirrorRow_WASMSIMD(const uint8_t* src, uint8_t* dst, int width) {
v128_t shuf = LoadConst(kShuffleMirror);
src += width - 16;
do {
wasm_v128_store(dst, LIBYUV_WASM_SWIZZLE(wasm_v128_load(src), shuf));
src -= 16;
dst += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_MIRRORROW_WASMSIMD
#ifdef HAS_MIRRORUVROW_WASMSIMD
static const uint8_t kShuffleMirrorUV[16] = {14, 15, 12, 13, 10, 11, 8, 9,
6, 7, 4, 5, 2, 3, 0, 1};
void MirrorUVRow_WASMSIMD(const uint8_t* src_uv, uint8_t* dst_uv, int width) {
v128_t shuf = LoadConst(kShuffleMirrorUV);
src_uv += (width - 8) * 2;
do {
wasm_v128_store(dst_uv, LIBYUV_WASM_SWIZZLE(wasm_v128_load(src_uv), shuf));
src_uv -= 16;
dst_uv += 16;
width -= 8;
} while (width > 0);
}
#endif // HAS_MIRRORUVROW_WASMSIMD
#ifdef HAS_SWAPUVROW_WASMSIMD
void SwapUVRow_WASMSIMD(const uint8_t* src_uv, uint8_t* dst_vu, int width) {
do {
// Keep constant offsets in the load/store instructions.
asm("" : "+r"(src_uv), "+r"(dst_vu));
v128_t uv0 = wasm_v128_load(src_uv);
v128_t uv1 = wasm_v128_load(src_uv + 16);
// 16 bit byte swap: rev16 on Arm, shifts on x86.
wasm_v128_store(
dst_vu, wasm_v128_or(wasm_i16x8_shl(uv0, 8), wasm_u16x8_shr(uv0, 8)));
wasm_v128_store(dst_vu + 16, wasm_v128_or(wasm_i16x8_shl(uv1, 8),
wasm_u16x8_shr(uv1, 8)));
src_uv += 32;
dst_vu += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_SWAPUVROW_WASMSIMD
#ifdef HAS_MIRRORSPLITUVROW_WASMSIMD
// Reverses 8 UV pairs into 8 U then 8 V.
static const uint8_t kShuffleMirrorSplitUV[16] = {14, 12, 10, 8, 6, 4, 2, 0,
15, 13, 11, 9, 7, 5, 3, 1};
void MirrorSplitUVRow_WASMSIMD(const uint8_t* src_uv,
uint8_t* dst_u,
uint8_t* dst_v,
int width) {
v128_t shuf = LoadConst(kShuffleMirrorSplitUV);
src_uv += (width - 16) * 2;
do {
asm("" : "+r"(src_uv));
v128_t a = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src_uv), shuf);
v128_t b = LIBYUV_WASM_SWIZZLE(wasm_v128_load(src_uv + 16), shuf);
wasm_v128_store(dst_u, wasm_i64x2_shuffle(b, a, 0, 2));
wasm_v128_store(dst_v, wasm_i64x2_shuffle(b, a, 1, 3));
src_uv -= 32;
dst_u += 16;
dst_v += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_MIRRORSPLITUVROW_WASMSIMD
#ifdef HAS_RGB24MIRRORROW_WASMSIMD
// Each 16 byte output needs 18 source bytes, so it is the OR of 2 swizzles.
static const uint8_t kShuffleRGB24Mirror[96] = {
13, 14, 15, 10, 11, 12, 7, 8, 9, 4, 5, 6, 1, 2,
3, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 14, 15, 128, 11, 12, 13, 8, 9, 10, 5, 6,
7, 2, 3, 4, 128, 0, 128, 1, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 0, 128, 128, 12, 13, 14, 9, 10,
11, 6, 7, 8, 3, 4, 5, 0, 1, 2, 1, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128};
void RGB24MirrorRow_WASMSIMD(const uint8_t* src_rgb24,
uint8_t* dst_rgb24,
int width) {
const uint8_t* shuf = kShuffleRGB24Mirror;
v128_t m0a = LoadConst(shuf);
v128_t m0b = LoadConst(shuf + 16);
v128_t m1a = LoadConst(shuf + 32);
v128_t m1b = LoadConst(shuf + 48);
v128_t m2a = LoadConst(shuf + 64);
v128_t m2b = LoadConst(shuf + 80);
src_rgb24 += (width - 16) * 3;
do {
asm("" : "+r"(src_rgb24), "+r"(dst_rgb24));
v128_t s0 = wasm_v128_load(src_rgb24);
v128_t s1 = wasm_v128_load(src_rgb24 + 16);
v128_t s2 = wasm_v128_load(src_rgb24 + 32);
// Bytes 15 and 32 of the 48 source bytes.
v128_t s02 = wasm_i8x16_shuffle(s0, s2, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30);
wasm_v128_store(dst_rgb24, wasm_v128_or(LIBYUV_WASM_SWIZZLE(s2, m0a),
LIBYUV_WASM_SWIZZLE(s1, m0b)));
wasm_v128_store(dst_rgb24 + 16,
wasm_v128_or(LIBYUV_WASM_SWIZZLE(s1, m1a),
LIBYUV_WASM_SWIZZLE(s02, m1b)));
wasm_v128_store(dst_rgb24 + 32, wasm_v128_or(LIBYUV_WASM_SWIZZLE(s0, m2a),
LIBYUV_WASM_SWIZZLE(s1, m2b)));
src_rgb24 -= 48;
dst_rgb24 += 48;
width -= 16;
} while (width > 0);
}
#endif // HAS_RGB24MIRRORROW_WASMSIMD
#ifdef HAS_ARGBMIRRORROW_WASMSIMD
void ARGBMirrorRow_WASMSIMD(const uint8_t* src, uint8_t* dst, int width) {
src += (width - 4) * 4;
do {
v128_t v = wasm_v128_load(src);
v = wasm_i32x4_shuffle(v, v, 3, 2, 1, 0);
wasm_v128_store(dst, v);
src -= 16;
dst += 16;
width -= 4;
} while (width > 0);
}
#endif // HAS_ARGBMIRRORROW_WASMSIMD
#ifdef HAS_INTERPOLATEROW_WASMSIMD
void InterpolateRow_WASMSIMD(uint8_t* dst_ptr,
const uint8_t* src_ptr,
ptrdiff_t src_stride,
int width,
int source_y_fraction) {
const uint8_t* src_ptr1 = src_ptr + src_stride;
if (source_y_fraction == 0) {
do {
wasm_v128_store(dst_ptr, wasm_v128_load(src_ptr));
src_ptr += 16;
dst_ptr += 16;
width -= 16;
} while (width > 0);
return;
}
if (source_y_fraction == 128) {
do {
v128_t s0 = wasm_v128_load(src_ptr);
v128_t s1 = wasm_v128_load(src_ptr1);
wasm_v128_store(dst_ptr, wasm_u8x16_avgr(s0, s1));
src_ptr += 16;
src_ptr1 += 16;
dst_ptr += 16;
width -= 16;
} while (width > 0);
return;
}
// s0 * (256 - f) + s1 * f + 128 fits in 16 bits. Both weights fit in 8
// bits since f is 1 to 255. The high bytes are the result (uzp2 on Arm).
v128_t w0 = SplatConst8(256 - source_y_fraction);
v128_t w1 = SplatConst8(source_y_fraction);
v128_t round = SplatConst16(128);
do {
v128_t s0 = wasm_v128_load(src_ptr);
v128_t s1 = wasm_v128_load(src_ptr1);
v128_t lo = wasm_i16x8_add(wasm_u16x8_extmul_low_u8x16(s0, w0),
wasm_u16x8_extmul_low_u8x16(s1, w1));
v128_t hi = wasm_i16x8_add(wasm_u16x8_extmul_high_u8x16(s0, w0),
wasm_u16x8_extmul_high_u8x16(s1, w1));
lo = wasm_i16x8_add(lo, round);
hi = wasm_i16x8_add(hi, round);
wasm_v128_store(
dst_ptr, wasm_i8x16_shuffle(lo, hi, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
21, 23, 25, 27, 29, 31));
src_ptr += 16;
src_ptr1 += 16;
dst_ptr += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_INTERPOLATEROW_WASMSIMD
#ifdef HAS_INTERPOLATEROW_16_WASMSIMD
void InterpolateRow_16_WASMSIMD(uint16_t* dst_ptr,
const uint16_t* src_ptr,
ptrdiff_t src_stride,
int width,
int source_y_fraction) {
const uint16_t* src_ptr1 = src_ptr + src_stride;
if (source_y_fraction == 0) {
do {
wasm_v128_store(dst_ptr, wasm_v128_load(src_ptr));
src_ptr += 8;
dst_ptr += 8;
width -= 8;
} while (width > 0);
return;
}
if (source_y_fraction == 128) {
do {
v128_t s0 = wasm_v128_load(src_ptr);
v128_t s1 = wasm_v128_load(src_ptr1);
wasm_v128_store(dst_ptr, wasm_u16x8_avgr(s0, s1));
src_ptr += 8;
src_ptr1 += 8;
dst_ptr += 8;
width -= 8;
} while (width > 0);
return;
}
// s0 * (256 - f) + s1 * f + 128 fits in 24 bits. Shift the result into
// the high 16 bits of each lane, then take the odd 16 bit lanes.
v128_t w0 = SplatConst16(256 - source_y_fraction);
v128_t w1 = SplatConst16(source_y_fraction);
v128_t round = SplatConst32(128);
do {
v128_t s0 = wasm_v128_load(src_ptr);
v128_t s1 = wasm_v128_load(src_ptr1);
v128_t lo = wasm_i32x4_add(wasm_u32x4_extmul_low_u16x8(s0, w0),
wasm_u32x4_extmul_low_u16x8(s1, w1));
v128_t hi = wasm_i32x4_add(wasm_u32x4_extmul_high_u16x8(s0, w0),
wasm_u32x4_extmul_high_u16x8(s1, w1));
lo = wasm_i32x4_shl(wasm_i32x4_add(lo, round), 8);
hi = wasm_i32x4_shl(wasm_i32x4_add(hi, round), 8);
wasm_v128_store(dst_ptr,
wasm_i16x8_shuffle(lo, hi, 1, 3, 5, 7, 9, 11, 13, 15));
src_ptr += 8;
src_ptr1 += 8;
dst_ptr += 8;
width -= 8;
} while (width > 0);
}
#endif // HAS_INTERPOLATEROW_16_WASMSIMD
#ifdef HAS_J400TOARGBROW_WASMSIMD
static const uint8_t kShuffleJ400ToARGB[64] = {
0, 0, 0, 128, 1, 1, 1, 128, 2, 2, 2, 128, 3, 3, 3, 128,
4, 4, 4, 128, 5, 5, 5, 128, 6, 6, 6, 128, 7, 7, 7, 128,
8, 8, 8, 128, 9, 9, 9, 128, 10, 10, 10, 128, 11, 11, 11, 128,
12, 12, 12, 128, 13, 13, 13, 128, 14, 14, 14, 128, 15, 15, 15, 128};
void J400ToARGBRow_WASMSIMD(const uint8_t* src_y,
uint8_t* dst_argb,
int width) {
v128_t shuf0 = LoadConst(kShuffleJ400ToARGB);
v128_t shuf1 = LoadConst(kShuffleJ400ToARGB + 16);
v128_t shuf2 = LoadConst(kShuffleJ400ToARGB + 32);
v128_t shuf3 = LoadConst(kShuffleJ400ToARGB + 48);
v128_t alpha = SplatConst32((int)0xff000000);
do {
asm("" : "+r"(dst_argb));
v128_t y = wasm_v128_load(src_y);
wasm_v128_store(dst_argb,
wasm_v128_or(LIBYUV_WASM_SWIZZLE(y, shuf0), alpha));
wasm_v128_store(dst_argb + 16,
wasm_v128_or(LIBYUV_WASM_SWIZZLE(y, shuf1), alpha));
wasm_v128_store(dst_argb + 32,
wasm_v128_or(LIBYUV_WASM_SWIZZLE(y, shuf2), alpha));
wasm_v128_store(dst_argb + 48,
wasm_v128_or(LIBYUV_WASM_SWIZZLE(y, shuf3), alpha));
src_y += 16;
dst_argb += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_J400TOARGBROW_WASMSIMD
#ifdef HAS_RGB565TOARGBROW_WASMSIMD
void RGB565ToARGBRow_WASMSIMD(const uint8_t* src_rgb565,
uint8_t* dst_argb,
int width) {
v128_t mask_b5 = SplatConst16(0x001f);
v128_t mask_g6 = SplatConst16(0x07e0);
v128_t mask_r5 = SplatConst16(0xf800);
v128_t mask_hi = SplatConst16(0xff00);
do {
asm("" : "+r"(dst_argb));
v128_t p = wasm_v128_load(src_rgb565);
v128_t b5 = wasm_v128_and(p, mask_b5);
v128_t b8 = wasm_v128_or(wasm_i16x8_shl(b5, 3), wasm_u16x8_shr(b5, 2));
v128_t g6 = wasm_v128_and(p, mask_g6);
v128_t g8_hi = wasm_v128_and(
wasm_v128_or(wasm_i16x8_shl(g6, 5), wasm_u16x8_shr(g6, 1)), mask_hi);
v128_t bg = wasm_v128_or(b8, g8_hi);
v128_t r8 = wasm_v128_or(wasm_u16x8_shr(wasm_v128_and(p, mask_r5), 8),
wasm_u16x8_shr(p, 13));
v128_t ra = wasm_v128_or(r8, mask_hi);
v128_t d0 = wasm_i16x8_shuffle(bg, ra, 0, 8, 1, 9, 2, 10, 3, 11);
v128_t d1 = wasm_i16x8_shuffle(bg, ra, 4, 12, 5, 13, 6, 14, 7, 15);
wasm_v128_store(dst_argb, d0);
wasm_v128_store(dst_argb + 16, d1);
src_rgb565 += 16;
dst_argb += 32;
width -= 8;
} while (width > 0);
}
#endif // HAS_RGB565TOARGBROW_WASMSIMD
#ifdef HAS_ARGB1555TOARGBROW_WASMSIMD
// 5 to 8 bit replication with multiplies: (v * 0x108) >> 5 == v << 3 | v >> 2.
void ARGB1555ToARGBRow_WASMSIMD(const uint8_t* src_argb1555,
uint8_t* dst_argb,
int width) {
v128_t mask_b5 = SplatConst16(0x001f);
v128_t mask_g5 = SplatConst16(0x03e0);
v128_t mask_hi = SplatConst16(0xff00);
v128_t mul_br = SplatConst16(0x0108);
v128_t mul_g = SplatConst16(0x0042);
do {
asm("" : "+r"(dst_argb));
v128_t p = wasm_v128_load(src_argb1555);
v128_t b8 =
wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(p, mask_b5), mul_br), 5);
// g5 << 5 times 0x42 is g5 << 11 | g5 << 6; the high byte is g8.
v128_t g8_hi = wasm_v128_and(
wasm_i16x8_mul(wasm_v128_and(p, mask_g5), mul_g), mask_hi);
v128_t bg = wasm_v128_or(b8, g8_hi);
v128_t r5 = wasm_u16x8_shr(wasm_i16x8_shl(p, 1), 11);
v128_t r8 = wasm_u16x8_shr(wasm_i16x8_mul(r5, mul_br), 5);
v128_t a8_hi = wasm_v128_and(wasm_i16x8_shr(p, 15), mask_hi);
v128_t ra = wasm_v128_or(r8, a8_hi);
v128_t d0 = wasm_i16x8_shuffle(bg, ra, 0, 8, 1, 9, 2, 10, 3, 11);
v128_t d1 = wasm_i16x8_shuffle(bg, ra, 4, 12, 5, 13, 6, 14, 7, 15);
wasm_v128_store(dst_argb, d0);
wasm_v128_store(dst_argb + 16, d1);
src_argb1555 += 16;
dst_argb += 32;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGB1555TOARGBROW_WASMSIMD
#ifdef HAS_ARGB4444TOARGBROW_WASMSIMD
void ARGB4444ToARGBRow_WASMSIMD(const uint8_t* src_argb4444,
uint8_t* dst_argb,
int width) {
v128_t mask_lo = SplatConst8(0x0f);
v128_t mask_hi = SplatConst8(0xf0);
do {
asm("" : "+r"(dst_argb));
v128_t v = wasm_v128_load(src_argb4444);
v128_t v_lo = wasm_v128_and(v, mask_lo);
v_lo = wasm_v128_or(v_lo, wasm_i16x8_shl(v_lo, 4));
v128_t v_hi = wasm_v128_and(v, mask_hi);
v_hi = wasm_v128_or(v_hi, wasm_u16x8_shr(v_hi, 4));
v128_t d0 = wasm_i8x16_shuffle(v_lo, v_hi, 0, 16, 1, 17, 2, 18, 3, 19, 4,
20, 5, 21, 6, 22, 7, 23);
v128_t d1 = wasm_i8x16_shuffle(v_lo, v_hi, 8, 24, 9, 25, 10, 26, 11, 27, 12,
28, 13, 29, 14, 30, 15, 31);
wasm_v128_store(dst_argb, d0);
wasm_v128_store(dst_argb + 16, d1);
src_argb4444 += 16;
dst_argb += 32;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGB4444TOARGBROW_WASMSIMD
#ifdef HAS_ARGBTORGB565DITHERROW_WASMSIMD
void ARGBToRGB565DitherRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_rgb,
uint32_t dither4,
int width) {
v128_t dither = wasm_i32x4_splat((int32_t)dither4);
dither = wasm_i8x16_shuffle(dither, dither, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
2, 3, 3, 3, 3);
v128_t mask_b = SplatConst32(0x0000001f);
v128_t mask_g = SplatConst32(0x000007e0);
v128_t mask_r = SplatConst32(0x0000f800);
do {
asm("" : "+r"(src_argb));
v128_t s0 = wasm_u8x16_add_sat(wasm_v128_load(src_argb), dither);
v128_t s1 = wasm_u8x16_add_sat(wasm_v128_load(src_argb + 16), dither);
v128_t b0 = wasm_v128_and(wasm_u32x4_shr(s0, 3), mask_b);
v128_t g0 = wasm_v128_and(wasm_u32x4_shr(s0, 5), mask_g);
v128_t r0 = wasm_v128_and(wasm_u32x4_shr(s0, 8), mask_r);
v128_t b1 = wasm_v128_and(wasm_u32x4_shr(s1, 3), mask_b);
v128_t g1 = wasm_v128_and(wasm_u32x4_shr(s1, 5), mask_g);
v128_t r1 = wasm_v128_and(wasm_u32x4_shr(s1, 8), mask_r);
v128_t p0 = wasm_v128_or(wasm_v128_or(b0, g0), r0);
v128_t p1 = wasm_v128_or(wasm_v128_or(b1, g1), r1);
wasm_v128_store(dst_rgb, wasm_u16x8_narrow_i32x4(p0, p1));
src_argb += 32;
dst_rgb += 16;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGBTORGB565DITHERROW_WASMSIMD
#ifdef HAS_ARGBTORGB565ROW_WASMSIMD
void ARGBToRGB565Row_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_rgb,
int width) {
v128_t mask_b = SplatConst32(0x0000001f);
v128_t mask_g = SplatConst32(0x000007e0);
v128_t mask_r = SplatConst32(0x0000f800);
do {
asm("" : "+r"(src_argb));
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb + 16);
v128_t b0 = wasm_v128_and(wasm_u32x4_shr(s0, 3), mask_b);
v128_t g0 = wasm_v128_and(wasm_u32x4_shr(s0, 5), mask_g);
v128_t r0 = wasm_v128_and(wasm_u32x4_shr(s0, 8), mask_r);
v128_t b1 = wasm_v128_and(wasm_u32x4_shr(s1, 3), mask_b);
v128_t g1 = wasm_v128_and(wasm_u32x4_shr(s1, 5), mask_g);
v128_t r1 = wasm_v128_and(wasm_u32x4_shr(s1, 8), mask_r);
v128_t p0 = wasm_v128_or(wasm_v128_or(b0, g0), r0);
v128_t p1 = wasm_v128_or(wasm_v128_or(b1, g1), r1);
wasm_v128_store(dst_rgb, wasm_u16x8_narrow_i32x4(p0, p1));
src_argb += 32;
dst_rgb += 16;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGBTORGB565ROW_WASMSIMD
#ifdef HAS_ARGBTOARGB1555ROW_WASMSIMD
void ARGBToARGB1555Row_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_argb1555,
int width) {
v128_t mask_b = SplatConst32(0x0000001f);
v128_t mask_g = SplatConst32(0x000003e0);
v128_t mask_r = SplatConst32(0x00007c00);
v128_t mask_a = SplatConst32(0x00008000);
do {
asm("" : "+r"(src_argb));
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb + 16);
v128_t b0 = wasm_v128_and(wasm_u32x4_shr(s0, 3), mask_b);
v128_t g0 = wasm_v128_and(wasm_u32x4_shr(s0, 6), mask_g);
v128_t r0 = wasm_v128_and(wasm_u32x4_shr(s0, 9), mask_r);
v128_t a0 = wasm_v128_and(wasm_u32x4_shr(s0, 16), mask_a);
v128_t b1 = wasm_v128_and(wasm_u32x4_shr(s1, 3), mask_b);
v128_t g1 = wasm_v128_and(wasm_u32x4_shr(s1, 6), mask_g);
v128_t r1 = wasm_v128_and(wasm_u32x4_shr(s1, 9), mask_r);
v128_t a1 = wasm_v128_and(wasm_u32x4_shr(s1, 16), mask_a);
v128_t p0 = wasm_v128_or(wasm_v128_or(b0, g0), wasm_v128_or(r0, a0));
v128_t p1 = wasm_v128_or(wasm_v128_or(b1, g1), wasm_v128_or(r1, a1));
wasm_v128_store(dst_argb1555, wasm_u16x8_narrow_i32x4(p0, p1));
src_argb += 32;
dst_argb1555 += 16;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGBTOARGB1555ROW_WASMSIMD
#ifdef HAS_ARGBTOARGB4444ROW_WASMSIMD
void ARGBToARGB4444Row_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_argb4444,
int width) {
v128_t mask_lo = SplatConst16(0x000f);
v128_t mask_hi = SplatConst16(0x00f0);
do {
asm("" : "+r"(src_argb));
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb + 16);
v128_t n0 = wasm_v128_or(wasm_v128_and(wasm_u16x8_shr(s0, 4), mask_lo),
wasm_v128_and(wasm_u16x8_shr(s0, 8), mask_hi));
v128_t n1 = wasm_v128_or(wasm_v128_and(wasm_u16x8_shr(s1, 4), mask_lo),
wasm_v128_and(wasm_u16x8_shr(s1, 8), mask_hi));
wasm_v128_store(dst_argb4444, wasm_u8x16_narrow_i16x8(n0, n1));
src_argb += 32;
dst_argb4444 += 16;
width -= 8;
} while (width > 0);
}
#endif // HAS_ARGBTOARGB4444ROW_WASMSIMD
#ifdef HAS_ARGBSHUFFLEROW_WASMSIMD
void ARGBShuffleRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_argb,
const uint8_t* shuffler,
int width) {
v128_t shuf = wasm_v128_load(shuffler);
while (width >= 8) {
asm("" : "+r"(src_argb), "+r"(dst_argb));
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb + 16);
wasm_v128_store(dst_argb, LIBYUV_WASM_SWIZZLE(s0, shuf));
wasm_v128_store(dst_argb + 16, LIBYUV_WASM_SWIZZLE(s1, shuf));
src_argb += 32;
dst_argb += 32;
width -= 8;
}
if (width > 0) {
v128_t s0 = wasm_v128_load(src_argb);
wasm_v128_store(dst_argb, LIBYUV_WASM_SWIZZLE(s0, shuf));
}
}
#endif // HAS_ARGBSHUFFLEROW_WASMSIMD
#if defined(HAS_I422TOARGBROW_WASMSIMD) || \
defined(HAS_I422TOAR30ROW_WASMSIMD) || \
defined(HAS_NV12TOARGBROW_WASMSIMD) || \
defined(HAS_NV21TOARGBROW_WASMSIMD) || \
defined(HAS_I422TORGB24ROW_WASMSIMD) || \
defined(HAS_I422TORGB565ROW_WASMSIMD) || \
defined(HAS_I422TOARGB1555ROW_WASMSIMD) || \
defined(HAS_I422TOARGB4444ROW_WASMSIMD) || \
defined(HAS_NV12TORGB565ROW_WASMSIMD)
// Interleaves map to punpck on x64 and zip on arm64 when the two inputs
// differ. V8 arm64 lowers shuffle(x, x) to tbl with a rebuilt mask.
static __inline v128_t ZipLo8_WASMSIMD(v128_t a, v128_t b) {
return wasm_i8x16_shuffle(a, b, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6,
22, 7, 23);
}
static __inline v128_t ZipHi8_WASMSIMD(v128_t a, v128_t b) {
return wasm_i8x16_shuffle(a, b, 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29,
14, 30, 15, 31);
}
// YuvConstants splatted to 16-bit lanes, built once per row.
struct YuvCoefs_WASMSIMD {
v128_t ub, ug, vg, vr, yg, yb, x80, k00ff, k0101;
};
static __inline void LoadYuvCoefs_WASMSIMD(const struct YuvConstants* yc,
int yb_adjust,
struct YuvCoefs_WASMSIMD* c) {
c->ub = wasm_i16x8_splat(yc->kUVToB[0]);
c->ug = wasm_i16x8_splat(yc->kUVToG[0]);
c->vg = wasm_i16x8_splat(yc->kUVToG[1]);
c->vr = wasm_i16x8_splat(yc->kUVToR[1]);
c->yg = wasm_i16x8_splat(yc->kYToRgb[0]);
c->yb = wasm_i16x8_splat(yc->kYBiasToRgb[0] - yb_adjust);
c->x80 = SplatConst8(0x80);
c->k00ff = SplatConst16(0x00ff);
c->k0101 = SplatConst16(0x0101);
}
// Returns ((y * 0x0101 * yg) >> 16) + yb for 8 Y in 16-bit lanes.
static __inline v128_t YToRgb_WASMSIMD(v128_t y,
const struct YuvCoefs_WASMSIMD* c) {
// The barrier keeps clang from widening yg outside the loop, which would
// turn extmul (umull on arm64) into extend + i32x4.mul.
v128_t yg = c->yg;
asm volatile("" : "+r"(yg));
v128_t y16 = wasm_i16x8_mul(y, c->k0101);
v128_t lo = wasm_u32x4_extmul_low_u16x8(y16, yg);
v128_t hi = wasm_u32x4_extmul_high_u16x8(y16, yg);
return wasm_i16x8_add(wasm_i16x8_shuffle(lo, hi, 1, 3, 5, 7, 9, 11, 13, 15),
c->yb);
}
// Converts 16 pixels to B, G, R with 6 fractional bits (unclamped int16).
// y = 16 Y. uv = 8 U/V pairs, U in the low byte of each 16-bit lane (V if vu).
// Even and odd pixels are kept apart so each pair shares its chroma terms:
// rgb[0], rgb[2], rgb[4] = even B, G, R and rgb[1], rgb[3], rgb[5] = odd.
static __inline void YuvToRgb16_WASMSIMD(v128_t y,
v128_t uv,
int vu,
const struct YuvCoefs_WASMSIMD* c,
v128_t* rgb) {
v128_t ye = YToRgb_WASMSIMD(wasm_v128_and(y, c->k00ff), c);
v128_t yo = YToRgb_WASMSIMD(wasm_u16x8_shr(y, 8), c);
// u - 128 and v - 128 as int8, sign extended to int16.
v128_t uvs = wasm_v128_xor(uv, c->x80);
v128_t lo = wasm_i16x8_shr(wasm_i16x8_shl(uvs, 8), 8);
v128_t hi = wasm_i16x8_shr(uvs, 8);
v128_t ui = vu ? hi : lo;
v128_t vi = vu ? lo : hi;
v128_t b = wasm_i16x8_mul(ui, c->ub);
v128_t g =
wasm_i16x8_add(wasm_i16x8_mul(ui, c->ug), wasm_i16x8_mul(vi, c->vg));
v128_t r = wasm_i16x8_mul(vi, c->vr);
rgb[0] = wasm_i16x8_add_sat(ye, b);
rgb[1] = wasm_i16x8_add_sat(yo, b);
rgb[2] = wasm_i16x8_sub_sat(ye, g);
rgb[3] = wasm_i16x8_sub_sat(yo, g);
rgb[4] = wasm_i16x8_add_sat(ye, r);
rgb[5] = wasm_i16x8_add_sat(yo, r);
}
// Loads 16 Y and 8 U/V, or 8 Y and 4 U/V when width < 16.
static __inline void ReadI422_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
int width,
v128_t* y,
v128_t* uv) {
if (width >= 16) {
*y = wasm_v128_load(src_y);
*uv = ZipLo8_WASMSIMD(wasm_v128_load64_zero(src_u),
wasm_v128_load64_zero(src_v));
} else {
*y = wasm_v128_load64_zero(src_y);
*uv = ZipLo8_WASMSIMD(wasm_v128_load32_zero(src_u),
wasm_v128_load32_zero(src_v));
}
}
// Loads 16 Y and 8 UV pairs, or 8 Y and 4 UV pairs when width < 16.
static __inline void ReadNV12_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_uv,
int width,
v128_t* y,
v128_t* uv) {
if (width >= 16) {
*y = wasm_v128_load(src_y);
*uv = wasm_v128_load(src_uv);
} else {
*y = wasm_v128_load64_zero(src_y);
*uv = wasm_v128_load64_zero(src_uv);
}
}
#endif
#if defined(HAS_I422TOARGBROW_WASMSIMD) || \
defined(HAS_NV12TOARGBROW_WASMSIMD) || defined(HAS_NV21TOARGBROW_WASMSIMD)
// Clamps even/odd B, G, R to 8 bits and stores 16 ARGB pixels, or 8 when
// width < 16.
static __inline void StoreARGB_WASMSIMD(const v128_t* rgb,
v128_t ff,
uint8_t* dst_argb,
int width) {
v128_t bg_e = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[0], 6),
wasm_i16x8_shr(rgb[2], 6));
v128_t gb_o = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[3], 6),
wasm_i16x8_shr(rgb[1], 6));
v128_t rr = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[4], 6),
wasm_i16x8_shr(rgb[5], 6));
v128_t br_e = ZipLo8_WASMSIMD(bg_e, rr);
v128_t ga_e = ZipHi8_WASMSIMD(bg_e, ff);
v128_t br_o = ZipHi8_WASMSIMD(gb_o, rr);
v128_t ga_o = ZipLo8_WASMSIMD(gb_o, ff);
v128_t e = ZipLo8_WASMSIMD(br_e, ga_e); // Pixels 0, 2, 4, 6.
v128_t o = ZipLo8_WASMSIMD(br_o, ga_o); // Pixels 1, 3, 5, 7.
wasm_v128_store(dst_argb, wasm_i32x4_shuffle(e, o, 0, 4, 1, 5));
wasm_v128_store(dst_argb + 16, wasm_i32x4_shuffle(e, o, 2, 6, 3, 7));
if (width >= 16) {
e = ZipHi8_WASMSIMD(br_e, ga_e);
o = ZipHi8_WASMSIMD(br_o, ga_o);
wasm_v128_store(dst_argb + 32, wasm_i32x4_shuffle(e, o, 0, 4, 1, 5));
wasm_v128_store(dst_argb + 48, wasm_i32x4_shuffle(e, o, 2, 6, 3, 7));
}
}
#endif
#ifdef HAS_I422TOARGBROW_WASMSIMD
void I422ToARGBRow_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_argb,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t ff = SplatConst8(0xff);
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadI422_WASMSIMD(src_y, src_u, src_v, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
StoreARGB_WASMSIMD(rgb, ff, dst_argb, width);
src_y += 16;
src_u += 8;
src_v += 8;
dst_argb += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TOARGBROW_WASMSIMD
#ifdef HAS_I422TOAR30ROW_WASMSIMD
// Clamps B, G, R with 6 fractional bits to 10 bits and packs them into the
// low and high 16 bits of AR30, without the alpha bits.
static __inline void RgbToAR30_WASMSIMD(v128_t b,
v128_t g,
v128_t r,
v128_t zero,
v128_t k3ff0,
v128_t* lo,
v128_t* hi) {
b = wasm_i16x8_max(wasm_i16x8_min(b, k3ff0), zero);
g = wasm_i16x8_max(wasm_i16x8_min(g, k3ff0), zero);
r = wasm_i16x8_max(wasm_i16x8_min(r, k3ff0), zero);
*lo = wasm_v128_or(wasm_u16x8_shr(b, 4),
wasm_i16x8_shl(wasm_v128_and(g, k3ff0), 6));
*hi = wasm_v128_or(wasm_v128_and(r, k3ff0), wasm_u16x8_shr(g, 10));
}
void I422ToAR30Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_ar30,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t zero = SplatConst16(0);
v128_t k3ff0 = SplatConst16(0x3ff0);
v128_t kc000 = SplatConst16(0xc000);
LoadYuvCoefs_WASMSIMD(yuvconstants, 24, &c); // AR30 bias.
do {
v128_t y, uv, rgb[6], lo_e, hi_e, lo_o, hi_o;
ReadI422_WASMSIMD(src_y, src_u, src_v, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
RgbToAR30_WASMSIMD(rgb[0], rgb[2], rgb[4], zero, k3ff0, &lo_e, &hi_e);
RgbToAR30_WASMSIMD(rgb[1], rgb[3], rgb[5], zero, k3ff0, &lo_o, &hi_o);
hi_e = wasm_v128_or(hi_e, kc000);
hi_o = wasm_v128_or(hi_o, kc000);
// Even pixels 0, 2, 4, 6 and odd pixels 1, 3, 5, 7 as 32 bits.
v128_t e = wasm_i16x8_shuffle(lo_e, hi_e, 0, 8, 1, 9, 2, 10, 3, 11);
v128_t o = wasm_i16x8_shuffle(lo_o, hi_o, 0, 8, 1, 9, 2, 10, 3, 11);
wasm_v128_store(dst_ar30, wasm_i32x4_shuffle(e, o, 0, 4, 1, 5));
wasm_v128_store(dst_ar30 + 16, wasm_i32x4_shuffle(e, o, 2, 6, 3, 7));
if (width >= 16) {
e = wasm_i16x8_shuffle(lo_e, hi_e, 4, 12, 5, 13, 6, 14, 7, 15);
o = wasm_i16x8_shuffle(lo_o, hi_o, 4, 12, 5, 13, 6, 14, 7, 15);
wasm_v128_store(dst_ar30 + 32, wasm_i32x4_shuffle(e, o, 0, 4, 1, 5));
wasm_v128_store(dst_ar30 + 48, wasm_i32x4_shuffle(e, o, 2, 6, 3, 7));
}
src_y += 16;
src_u += 8;
src_v += 8;
dst_ar30 += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TOAR30ROW_WASMSIMD
#ifdef HAS_NV12TOARGBROW_WASMSIMD
void NV12ToARGBRow_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_uv,
uint8_t* dst_argb,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t ff = SplatConst8(0xff);
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadNV12_WASMSIMD(src_y, src_uv, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
StoreARGB_WASMSIMD(rgb, ff, dst_argb, width);
src_y += 16;
src_uv += 16;
dst_argb += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_NV12TOARGBROW_WASMSIMD
#ifdef HAS_NV21TOARGBROW_WASMSIMD
void NV21ToARGBRow_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_vu,
uint8_t* dst_argb,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t ff = SplatConst8(0xff);
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, vu, rgb[6];
ReadNV12_WASMSIMD(src_y, src_vu, width, &y, &vu);
YuvToRgb16_WASMSIMD(y, vu, 1, &c, rgb);
StoreARGB_WASMSIMD(rgb, ff, dst_argb, width);
src_y += 16;
src_vu += 16;
dst_argb += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_NV21TOARGBROW_WASMSIMD
#ifdef HAS_I422TORGB24ROW_WASMSIMD
// Swizzles that interleave B/G pairs and R into 3 vectors of RGB24. R is
// 8 even pixels followed by 8 odd pixels.
static const uint8_t kI422ToRGB24Swizzle[96] = {
0, 1, 128, 2, 3, 128, 4, 5, 128, 6, 7, 128, 8, 9,
128, 10, 128, 128, 0, 128, 128, 8, 128, 128, 1, 128, 128, 9,
128, 128, 2, 128, 3, 128, 4, 5, 128, 6, 7, 128, 8, 9,
128, 10, 11, 128, 12, 13, 128, 10, 128, 128, 3, 128, 128, 11,
128, 128, 4, 128, 128, 12, 128, 128, 128, 6, 7, 128, 8, 9,
128, 10, 11, 128, 12, 13, 128, 14, 15, 128, 5, 128, 128, 13,
128, 128, 6, 128, 128, 14, 128, 128, 7, 128, 128, 15};
void I422ToRGB24Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_rgb24,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t m0 = LoadConst(kI422ToRGB24Swizzle);
v128_t m1 = LoadConst(kI422ToRGB24Swizzle + 16);
v128_t m2 = LoadConst(kI422ToRGB24Swizzle + 32);
v128_t m3 = LoadConst(kI422ToRGB24Swizzle + 48);
v128_t m4 = LoadConst(kI422ToRGB24Swizzle + 64);
v128_t m5 = LoadConst(kI422ToRGB24Swizzle + 80);
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadI422_WASMSIMD(src_y, src_u, src_v, 16, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
v128_t bg_e = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[0], 6),
wasm_i16x8_shr(rgb[2], 6));
v128_t bg_o = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[1], 6),
wasm_i16x8_shr(rgb[3], 6));
v128_t r = wasm_u8x16_narrow_i16x8(wasm_i16x8_shr(rgb[4], 6),
wasm_i16x8_shr(rgb[5], 6));
v128_t b = ZipLo8_WASMSIMD(bg_e, bg_o);
v128_t g = ZipHi8_WASMSIMD(bg_e, bg_o);
v128_t bg0 = ZipLo8_WASMSIMD(b, g);
v128_t bg2 = ZipHi8_WASMSIMD(b, g);
v128_t bg1 = wasm_i8x16_shuffle(bg0, bg2, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23);
wasm_v128_store(dst_rgb24, wasm_v128_or(LIBYUV_WASM_SWIZZLE(bg0, m0),
LIBYUV_WASM_SWIZZLE(r, m1)));
wasm_v128_store(dst_rgb24 + 16, wasm_v128_or(LIBYUV_WASM_SWIZZLE(bg1, m2),
LIBYUV_WASM_SWIZZLE(r, m3)));
wasm_v128_store(dst_rgb24 + 32, wasm_v128_or(LIBYUV_WASM_SWIZZLE(bg2, m4),
LIBYUV_WASM_SWIZZLE(r, m5)));
src_y += 16;
src_u += 8;
src_v += 8;
dst_rgb24 += 48;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TORGB24ROW_WASMSIMD
#if defined(HAS_I422TORGB565ROW_WASMSIMD) || \
defined(HAS_I422TOARGB1555ROW_WASMSIMD) || \
defined(HAS_I422TOARGB4444ROW_WASMSIMD) || \
defined(HAS_NV12TORGB565ROW_WASMSIMD)
// Clamps x with 6 fractional bits to 8 bits, keeps the top 'bits' and moves
// them to bit 'pos'.
static __inline v128_t PackChannel_WASMSIMD(v128_t x,
v128_t zero,
v128_t k3fff,
int bits,
int pos) {
x = wasm_i16x8_max(wasm_i16x8_min(x, k3fff), zero);
return wasm_i16x8_shl(wasm_u16x8_shr(x, 14 - bits), pos);
}
// Packs even/odd B, G, R into 16 bit pixels with the given channel sizes and
// stores 16 pixels, or 8 when width < 16. k = {0, 0x3fff, alpha} in 16-bit
// lanes. Alpha is set if the channels use fewer than 16 bits.
static __inline void Store16Bit_WASMSIMD(const v128_t* rgb,
int bbits,
int gbits,
int rbits,
const v128_t* k,
uint8_t* dst,
int width) {
v128_t p[2];
int i;
for (i = 0; i < 2; ++i) {
p[i] = wasm_v128_or(
PackChannel_WASMSIMD(rgb[i], k[0], k[1], bbits, 0),
wasm_v128_or(PackChannel_WASMSIMD(rgb[2 + i], k[0], k[1], gbits, bbits),
PackChannel_WASMSIMD(rgb[4 + i], k[0], k[1], rbits,
bbits + gbits)));
if (bbits + gbits + rbits < 16) {
p[i] = wasm_v128_or(p[i], k[2]);
}
}
wasm_v128_store(dst,
wasm_i16x8_shuffle(p[0], p[1], 0, 8, 1, 9, 2, 10, 3, 11));
if (width >= 16) {
wasm_v128_store(dst + 16,
wasm_i16x8_shuffle(p[0], p[1], 4, 12, 5, 13, 6, 14, 7, 15));
}
}
#endif
#ifdef HAS_I422TORGB565ROW_WASMSIMD
void I422ToRGB565Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_rgb565,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t k[2] = {SplatConst16(0), SplatConst16(0x3fff)};
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadI422_WASMSIMD(src_y, src_u, src_v, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
Store16Bit_WASMSIMD(rgb, 5, 6, 5, k, dst_rgb565, width);
src_y += 16;
src_u += 8;
src_v += 8;
dst_rgb565 += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TORGB565ROW_WASMSIMD
#ifdef HAS_I422TOARGB1555ROW_WASMSIMD
void I422ToARGB1555Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_argb1555,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t k[3] = {SplatConst16(0), SplatConst16(0x3fff), SplatConst16(0x8000)};
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadI422_WASMSIMD(src_y, src_u, src_v, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
Store16Bit_WASMSIMD(rgb, 5, 5, 5, k, dst_argb1555, width);
src_y += 16;
src_u += 8;
src_v += 8;
dst_argb1555 += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TOARGB1555ROW_WASMSIMD
#ifdef HAS_I422TOARGB4444ROW_WASMSIMD
void I422ToARGB4444Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_u,
const uint8_t* src_v,
uint8_t* dst_argb4444,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t k[3] = {SplatConst16(0), SplatConst16(0x3fff), SplatConst16(0xf000)};
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadI422_WASMSIMD(src_y, src_u, src_v, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
Store16Bit_WASMSIMD(rgb, 4, 4, 4, k, dst_argb4444, width);
src_y += 16;
src_u += 8;
src_v += 8;
dst_argb4444 += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_I422TOARGB4444ROW_WASMSIMD
#ifdef HAS_NV12TORGB565ROW_WASMSIMD
void NV12ToRGB565Row_WASMSIMD(const uint8_t* src_y,
const uint8_t* src_uv,
uint8_t* dst_rgb565,
const struct YuvConstants* yuvconstants,
int width) {
struct YuvCoefs_WASMSIMD c;
v128_t k[2] = {SplatConst16(0), SplatConst16(0x3fff)};
LoadYuvCoefs_WASMSIMD(yuvconstants, 0, &c);
do {
v128_t y, uv, rgb[6];
ReadNV12_WASMSIMD(src_y, src_uv, width, &y, &uv);
YuvToRgb16_WASMSIMD(y, uv, 0, &c, rgb);
Store16Bit_WASMSIMD(rgb, 5, 6, 5, k, dst_rgb565, width);
src_y += 16;
src_uv += 16;
dst_rgb565 += 32;
width -= 16;
} while (width > 0);
}
#endif // HAS_NV12TORGB565ROW_WASMSIMD
#ifdef HAS_ARGBBLENDROW_WASMSIMD
// Broadcast 255 - alpha (from byte 3 of each pixel) to 16 bit lanes.
static const uint8_t kShuffleAlpha16[16] = {3, 128, 3, 128, 7, 128, 7, 128,
11, 128, 11, 128, 15, 128, 15, 128};
void ARGBBlendRow_WASMSIMD(const uint8_t* src_argb,
const uint8_t* src_argb1,
uint8_t* dst_argb,
int width) {
v128_t shuf_a = LoadConst(kShuffleAlpha16);
v128_t one = SplatConst16(1);
v128_t mask_lo = SplatConst16(0x00ff);
v128_t mask_alpha = SplatConst32((int)0xff000000);
while (width >= 4) {
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb1);
src_argb += 16;
src_argb1 += 16;
// a = 256 - alpha.
v128_t a = wasm_i16x8_add(
LIBYUV_WASM_SWIZZLE(wasm_v128_xor(s0, mask_alpha), shuf_a), one);
v128_t s1_lo =
wasm_u16x8_shr(wasm_i16x8_mul(wasm_v128_and(s1, mask_lo), a), 8);
v128_t s1_hi =
wasm_v128_andnot(wasm_i16x8_mul(wasm_u16x8_shr(s1, 8), a), mask_lo);
v128_t res = wasm_v128_or(s0, mask_alpha);
res = wasm_u8x16_add_sat(res, s1_lo);
res = wasm_u8x16_add_sat(res, s1_hi);
wasm_v128_store(dst_argb, res);
dst_argb += 16;
width -= 4;
}
while (width > 0) {
uint32_t s0 = *(const uint32_t*)src_argb;
uint32_t s1 = *(const uint32_t*)src_argb1;
src_argb += 4;
src_argb1 += 4;
uint32_t a = 256 - (s0 >> 24);
uint32_t b = (((s1 & 0x000000ff) * a) >> 8) + (s0 & 0x000000ff);
uint32_t g = (((s1 & 0x0000ff00) * a) >> 8) + (s0 & 0x0000ff00);
uint32_t r = (((s1 & 0x00ff0000) >> 8) * a) + (s0 & 0x00ff0000);
b = (b > 255) ? 255 : b;
g = (g > 0xff00) ? 0xff00 : (g & 0xff00);
r = (r > 0x00ff0000) ? 0x00ff0000 : (r & 0x00ff0000);
*(uint32_t*)dst_argb = 0xff000000u | r | g | b;
dst_argb += 4;
--width;
}
}
#endif // HAS_ARGBBLENDROW_WASMSIMD
#ifdef HAS_BLENDPLANEROW_WASMSIMD
void BlendPlaneRow_WASMSIMD(const uint8_t* src0,
const uint8_t* src1,
const uint8_t* alpha,
uint8_t* dst,
int width) {
// src0 * a + src1 * (255 - a) + 255 fits in 16 bits. 255 - a is a ^ 255.
// The high bytes are the result (uzp2 on Arm).
v128_t c255 = SplatConst16(255);
v128_t ff = SplatConst8(255);
do {
v128_t s0 = wasm_v128_load(src0);
v128_t s1 = wasm_v128_load(src1);
v128_t a = wasm_v128_load(alpha);
v128_t ia = wasm_v128_xor(a, ff);
src0 += 16;
src1 += 16;
alpha += 16;
v128_t lo = wasm_i16x8_add(wasm_u16x8_extmul_low_u8x16(s0, a),
wasm_u16x8_extmul_low_u8x16(s1, ia));
v128_t hi = wasm_i16x8_add(wasm_u16x8_extmul_high_u8x16(s0, a),
wasm_u16x8_extmul_high_u8x16(s1, ia));
lo = wasm_i16x8_add(lo, c255);
hi = wasm_i16x8_add(hi, c255);
wasm_v128_store(dst, wasm_i8x16_shuffle(lo, hi, 1, 3, 5, 7, 9, 11, 13, 15,
17, 19, 21, 23, 25, 27, 29, 31));
dst += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_BLENDPLANEROW_WASMSIMD
#ifdef HAS_ARGBEXTRACTALPHAROW_WASMSIMD
void ARGBExtractAlphaRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_a,
int width) {
do {
asm("" : "+r"(src_argb), "+r"(dst_a));
v128_t s0 = wasm_v128_load(src_argb);
v128_t s1 = wasm_v128_load(src_argb + 16);
v128_t s2 = wasm_v128_load(src_argb + 32);
v128_t s3 = wasm_v128_load(src_argb + 48);
s0 = wasm_u32x4_shr(s0, 24);
s1 = wasm_u32x4_shr(s1, 24);
s2 = wasm_u32x4_shr(s2, 24);
s3 = wasm_u32x4_shr(s3, 24);
v128_t a = wasm_u8x16_narrow_i16x8(wasm_i16x8_narrow_i32x4(s0, s1),
wasm_i16x8_narrow_i32x4(s2, s3));
wasm_v128_store(dst_a, a);
src_argb += 64;
dst_a += 16;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGBEXTRACTALPHAROW_WASMSIMD
#ifdef HAS_ARGBCOPYYTOALPHAROW_WASMSIMD
// Swizzle Y[i] to the alpha byte of 32 bit lane i; 128 indices give 0.
static const uint8_t kYToAlphaSwizzleTable[64] = {
128, 128, 128, 0, 128, 128, 128, 1, 128, 128, 128, 2, 128, 128, 128, 3,
128, 128, 128, 4, 128, 128, 128, 5, 128, 128, 128, 6, 128, 128, 128, 7,
128, 128, 128, 8, 128, 128, 128, 9, 128, 128, 128, 10, 128, 128, 128, 11,
128, 128, 128, 12, 128, 128, 128, 13, 128, 128, 128, 14, 128, 128, 128, 15};
void ARGBCopyYToAlphaRow_WASMSIMD(const uint8_t* src, uint8_t* dst, int width) {
v128_t rgb_mask = SplatConst32(0x00ffffff);
v128_t idx0 = LoadConst(kYToAlphaSwizzleTable);
v128_t idx1 = LoadConst(kYToAlphaSwizzleTable + 16);
v128_t idx2 = LoadConst(kYToAlphaSwizzleTable + 32);
v128_t idx3 = LoadConst(kYToAlphaSwizzleTable + 48);
do {
asm("" : "+r"(src), "+r"(dst));
v128_t y = wasm_v128_load(src);
v128_t d0 = wasm_v128_and(wasm_v128_load(dst), rgb_mask);
v128_t d1 = wasm_v128_and(wasm_v128_load(dst + 16), rgb_mask);
v128_t d2 = wasm_v128_and(wasm_v128_load(dst + 32), rgb_mask);
v128_t d3 = wasm_v128_and(wasm_v128_load(dst + 48), rgb_mask);
wasm_v128_store(dst, wasm_v128_or(d0, LIBYUV_WASM_SWIZZLE(y, idx0)));
wasm_v128_store(dst + 16, wasm_v128_or(d1, LIBYUV_WASM_SWIZZLE(y, idx1)));
wasm_v128_store(dst + 32, wasm_v128_or(d2, LIBYUV_WASM_SWIZZLE(y, idx2)));
wasm_v128_store(dst + 48, wasm_v128_or(d3, LIBYUV_WASM_SWIZZLE(y, idx3)));
src += 16;
dst += 64;
width -= 16;
} while (width > 0);
}
#endif // HAS_ARGBCOPYYTOALPHAROW_WASMSIMD
#ifdef HAS_ARGBORROW_WASMSIMD
void ARGBOrRow_WASMSIMD(const uint8_t* src_argb,
uint8_t* dst_argb,
uint32_t value,
int width) {
v128_t val = wasm_i32x4_splat((int32_t)value);
do {
asm("" : "+r"(src_argb), "+r"(dst_argb));
v128_t s = wasm_v128_load(src_argb);
wasm_v128_store(dst_argb, wasm_v128_or(s, val));
src_argb += 16;
dst_argb += 16;
width -= 4;
} while (width > 0);
}
#endif // HAS_ARGBORROW_WASMSIMD
#ifdef HAS_ARGBALPHABLENDROW_WASMSIMD
void ARGBAlphaBlendRow_WASMSIMD(const uint8_t* src_argb0,
const uint8_t* src_argb1,
uint8_t* dst_argb,
int width) {
v128_t c1 = SplatConst16(1);
v128_t c00ffffff = SplatConst32(0x00ffffff);
do {
asm("" : "+r"(src_argb0), "+r"(src_argb1), "+r"(dst_argb));
v128_t s = wasm_v128_load(src_argb0);
v128_t d = wasm_v128_load(src_argb1);
src_argb0 += 16;
src_argb1 += 16;
v128_t sa = wasm_u32x4_shr(s, 24);
v128_t da = wasm_u32x4_shr(d, 24);
v128_t prod16 = wasm_i16x8_add(wasm_i16x8_mul(da, sa), c1);
v128_t da_sa =
wasm_u16x8_shr(wasm_i16x8_add(prod16, wasm_u16x8_shr(prod16, 8)), 8);
v128_t da2 = wasm_i32x4_add(da, wasm_i32x4_sub(sa, da_sa));
v128_t t = wasm_u32x4_shr(wasm_i32x4_eq(da, wasm_i32x4_const_splat(0)), 24);
v128_t sa_eff = wasm_v128_or(sa, t);
v128_t den_eff = wasm_v128_or(da2, t);
v128_t num = wasm_i32x4_sub(wasm_i32x4_shl(sa_eff, 8), sa_eff);
v128_t ratio = wasm_u32x4_trunc_sat_f32x4(wasm_f32x4_div(
wasm_f32x4_convert_u32x4(num), wasm_f32x4_convert_u32x4(den_eff)));
v128_t r8 = wasm_i8x16_shuffle(ratio, ratio, 0, 0, 0, 0, 4, 4, 4, 4, 8, 8,
8, 8, 12, 12, 12, 12);
v128_t inv_r8 = wasm_v128_not(r8);
v128_t acc_lo =
wasm_i16x8_add(wasm_i16x8_add(wasm_u16x8_extmul_low_u8x16(d, inv_r8),
wasm_u16x8_extmul_low_u8x16(s, r8)),
c1);
v128_t acc_hi =
wasm_i16x8_add(wasm_i16x8_add(wasm_u16x8_extmul_high_u8x16(d, inv_r8),
wasm_u16x8_extmul_high_u8x16(s, r8)),
c1);
acc_lo =
wasm_u16x8_shr(wasm_i16x8_add(acc_lo, wasm_u16x8_shr(acc_lo, 8)), 8);
acc_hi =
wasm_u16x8_shr(wasm_i16x8_add(acc_hi, wasm_u16x8_shr(acc_hi, 8)), 8);
v128_t rgb = wasm_u8x16_narrow_i16x8(acc_lo, acc_hi);
v128_t out = wasm_v128_or(wasm_v128_and(rgb, c00ffffff),
wasm_i32x4_shl(da2, 24));
wasm_v128_store(dst_argb, out);
dst_argb += 16;
width -= 4;
} while (width > 0);
}
#endif // HAS_ARGBALPHABLENDROW_WASMSIMD
#undef LIBYUV_WASM_SWIZZLE
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv
#endif
#endif // !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)