fix(app): crop animated images on web with libwebp (#2947)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-24 22:45:53 +02:00
1 parent e0843ac4f5
commit bbfe809bef
104 files changed
+10884 -7316

No files matched your search

+160
View File
@@ -0,0 +1,160 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cc"
version = "1.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "find-msvc-tools"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b"
[[package]]
name = "glob"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
[[package]]
name = "libfluxwebp"
version = "0.1.0"
dependencies = [
"libwebp-sys",
"wasm-bindgen",
]
[[package]]
name = "libwebp-sys"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b3a87b44e34d17161e4f17d92a463d596cb13825dcd1758ed18fd3a721e189c"
dependencies = [
"cc",
"glob",
"pkg-config",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pkg-config"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "syn"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
[[package]]
name = "wasm-bindgen"
version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e"
dependencies = [
"unicode-ident",
]
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "libfluxwebp"
version = "0.1.0"
edition = "2024"
license = "AGPL-3.0-or-later"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "=0.2.128"
libwebp-sys = {version = "=0.14.4", default-features = false}
[profile.release]
codegen-units = 1
lto = true
opt-level = "z"
panic = "abort"
strip = true
[workspace]
@@ -0,0 +1,21 @@
MIT License
Copyright (c) the libwebp-sys authors (XianYou, Kornel Lesiński and contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,30 @@
Copyright (c) 2010, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Google nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+16
View File
@@ -0,0 +1,16 @@
# libfluxwebp licenses
`libfluxwebp_bg.wasm` and `libfluxwebp_simd_bg.wasm` are built from this crate
and bundled into the app. They contain third-party code that keeps its upstream
license.
| Component | Source | License |
| --- | --- | --- |
| libwebp 1.6.0 | Vendored by the `libwebp-sys` 0.14.4 crate | BSD-3-Clause, see `LICENSE-LIBWEBP.txt`, with the additional patent grant in `PATENTS-LIBWEBP.txt` |
| `libwebp-sys` 0.14.4 | Rust bindings and build script | MIT, see `LICENSE-LIBWEBP-SYS.txt` |
| `simd/xmmintrin.h`, `simd/emmintrin.h` | Emscripten 4.0.15 SSE compatibility headers | MIT or University of Illinois/NCSA, see `simd/LICENSE` |
The SIMD build compiles libwebp's SSE2 code paths through the Emscripten
headers, unmodified. The headers in `shim/` and `src/shim.rs` are Fluxer code.
No Fluxer license notice grants rights to third-party trademarks or brand names.
@@ -0,0 +1,23 @@
Additional IP Rights Grant (Patents)
------------------------------------
"These implementations" means the copyrightable works that implement the WebM
codecs distributed by Google as part of the WebM Project.
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable (except as stated in this section) patent license to
make, have made, use, offer to sell, sell, import, transfer, and otherwise
run, modify and propagate the contents of these implementations of WebM, where
such license applies only to those patent claims, both currently owned by
Google and acquired in the future, licensable by Google that are necessarily
infringed by these implementations of WebM. This grant does not include claims
that would be infringed only as a consequence of further modification of these
implementations. If you or your agent or exclusive licensee institute or order
or agree to the institution of patent litigation or any other patent
enforcement activity against any entity (including a cross-claim or
counterclaim in a lawsuit) alleging that any of these implementations of WebM
or any code incorporated within any of these implementations of WebM
constitute direct or contributory patent infringement, or inducement of
patent infringement, then any patent rights granted to you under this License
for these implementations of WebM shall terminate as of the date such
litigation is filed.
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_ASSERT_H
#define FLUXWEBP_ASSERT_H
#define assert(expr) ((void)0)
#endif
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_INTTYPES_H
#define FLUXWEBP_INTTYPES_H
#include <stdint.h>
#endif
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_MATH_H
#define FLUXWEBP_MATH_H
double fluxwebp_shim_pow(double x, double y);
double fluxwebp_shim_log(double x);
float fluxwebp_shim_expf(float x);
double fluxwebp_shim_log10(double x);
float fluxwebp_shim_logf(float x);
double fluxwebp_shim_round(double x);
#define pow(x, y) fluxwebp_shim_pow(x, y)
#define log(x) fluxwebp_shim_log(x)
#define expf(x) fluxwebp_shim_expf(x)
#define log10(x) fluxwebp_shim_log10(x)
#define logf(x) fluxwebp_shim_logf(x)
#define round(x) fluxwebp_shim_round(x)
#define fabs(x) __builtin_fabs(x)
#define floor(x) __builtin_floor(x)
#define ceil(x) __builtin_ceil(x)
#define sqrt(x) __builtin_sqrt(x)
#define sqrtf(x) __builtin_sqrtf(x)
#define rint(x) __builtin_rint(x)
#define rintf(x) __builtin_rintf(x)
#define fabsf(x) __builtin_fabsf(x)
#define floorf(x) __builtin_floorf(x)
#define ceilf(x) __builtin_ceilf(x)
#define isnan(x) __builtin_isnan(x)
#define isinf(x) __builtin_isinf(x)
#define lrint(x) ((long)__builtin_rint(x))
#define llrint(x) ((long long)__builtin_rint(x))
#define lrintf(x) ((long)__builtin_rintf(x))
#define llrintf(x) ((long long)__builtin_rintf(x))
#endif
+50
View File
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_STDIO_H
#define FLUXWEBP_STDIO_H
#include <stddef.h>
#include <stdarg.h>
typedef struct FluxWebpFile FILE;
#define stderr ((FILE*)0)
#define stdout ((FILE*)0)
#define fprintf(f, ...) ((void)0)
#define printf(...) ((void)0)
#define fflush(f) ((void)0)
static inline void fluxwebp_shim_put(char* buf, size_t cap, size_t* n, char c) {
if (*n + 1 < cap) buf[*n] = c;
(*n)++;
}
static inline int snprintf(char* buf, size_t cap, const char* fmt, ...) {
va_list ap;
size_t n = 0;
va_start(ap, fmt);
for (; *fmt; fmt++) {
if (*fmt != '%') {
fluxwebp_shim_put(buf, cap, &n, *fmt);
continue;
}
fmt++;
if (*fmt == 's') {
const char* s = va_arg(ap, const char*);
while (s && *s) fluxwebp_shim_put(buf, cap, &n, *s++);
} else if (*fmt == 'd') {
int v = va_arg(ap, int);
char tmp[12];
int i = 0;
unsigned int u = v < 0 ? 0u - (unsigned int)v : (unsigned int)v;
if (v < 0) fluxwebp_shim_put(buf, cap, &n, '-');
do { tmp[i++] = (char)('0' + u % 10); u /= 10; } while (u);
while (i) fluxwebp_shim_put(buf, cap, &n, tmp[--i]);
} else if (*fmt == '%') {
fluxwebp_shim_put(buf, cap, &n, '%');
} else {
break;
}
}
va_end(ap);
if (cap) buf[n < cap ? n : cap - 1] = '\0';
return (int)n;
}
#endif
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_STDLIB_H
#define FLUXWEBP_STDLIB_H
#include <stddef.h>
void* fluxwebp_shim_malloc(size_t size);
void* fluxwebp_shim_calloc(size_t nmemb, size_t size);
void fluxwebp_shim_free(void* ptr);
void fluxwebp_shim_qsort(void* base, size_t nitems, size_t size, int (*compar)(const void*, const void*));
#define malloc(size) fluxwebp_shim_malloc(size)
#define calloc(nmemb, size) fluxwebp_shim_calloc(nmemb, size)
#define free(ptr) fluxwebp_shim_free(ptr)
#define qsort(base, nitems, size, compar) fluxwebp_shim_qsort(base, nitems, size, compar)
#define abort() __builtin_trap()
static inline int abs(int x) { return x < 0 ? -x : x; }
#endif
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#ifndef FLUXWEBP_STRING_H
#define FLUXWEBP_STRING_H
#include <stddef.h>
#define memcpy(d, s, n) __builtin_memcpy(d, s, n)
#define memmove(d, s, n) __builtin_memmove(d, s, n)
#define memset(d, c, n) __builtin_memset(d, c, n)
#define memcmp(a, b, n) __builtin_memcmp(a, b, n)
#define strlen(s) __builtin_strlen(s)
#endif
+102
View File
@@ -0,0 +1,102 @@
Emscripten is available under 2 licenses, the MIT license and the
University of Illinois/NCSA Open Source License.
Both are permissive open source licenses, with little if any
practical difference between them.
The reason for offering both is that (1) the MIT license is
well-known, while (2) the University of Illinois/NCSA Open Source
License allows Emscripten's code to be integrated upstream into
LLVM, which uses that license, should the opportunity arise.
The full text of both licenses follows.
==============================================================================
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
==============================================================================
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal with the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimers.
Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimers
in the documentation and/or other materials provided with the
distribution.
Neither the names of Mozilla,
nor the names of its contributors may be used to endorse
or promote products derived from this Software without specific prior
written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
==============================================================================
This program uses portions of Node.js source code located in src/library_path.js,
in accordance with the terms of the MIT license. Node's license follows:
"""
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
The musl libc project is bundled in this repo, and it has the MIT license, see
system/lib/libc/musl/COPYRIGHT
The third_party/ subdirectory contains code with other licenses. None of it is
used by default, but certain options use it (e.g., the optional closure compiler
flag will run closure compiler from third_party/).
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,714 @@
/*
* Copyright 2020 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#ifndef __emscripten_xmmintrin_h__
#define __emscripten_xmmintrin_h__
#include <wasm_simd128.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#ifndef __SSE__
#error "SSE instruction set not enabled"
#endif
#ifdef WASM_SIMD_COMPAT_SLOW
#define DIAGNOSE_SLOW diagnose_if(1, "Instruction emulated via slow path.", "warning")
#else
#define DIAGNOSE_SLOW
#endif
// Emscripten SIMD support doesn't support MMX/float32x2/__m64.
// However, we support loading and storing 2-vectors, so
// recognize the type at least.
typedef float __m64 __attribute__((__vector_size__(8), __aligned__(8)));
typedef __f32x4 __m128;
typedef v128_t __m128i;
#define __f32x4_shuffle(__a, __b, __c0, __c1, __c2, __c3) \
((v128_t)(__builtin_shufflevector((__f32x4)(__a), (__f32x4)(__b), __c0, \
__c1, __c2, __c3)))
// This is defined as a macro because __builtin_shufflevector requires its
// mask argument to be a compile-time constant.
#define _mm_shuffle_ps(__a, __b, __mask) __extension__ ({ \
((__m128)__f32x4_shuffle(__a, __b, \
(((__mask) >> 0) & 0x3) + 0, \
(((__mask) >> 2) & 0x3) + 0, \
(((__mask) >> 4) & 0x3) + 4, \
(((__mask) >> 6) & 0x3) + 4)); })
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_set_ps(float __z, float __y, float __x, float __w)
{
return (__m128)wasm_f32x4_make(__w, __x, __y, __z);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_setr_ps(float __z, float __y, float __x, float __w)
{
return (__m128)wasm_f32x4_make(__z, __y, __x, __w);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_set_ss(float __w)
{
return (__m128)wasm_f32x4_make(__w, 0, 0, 0);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_set_ps1(float __w)
{
return (__m128)wasm_f32x4_splat(__w);
}
#define _mm_set1_ps _mm_set_ps1
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_setzero_ps(void)
{
return (__m128)wasm_f32x4_const(0.f, 0.f, 0.f, 0.f);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_load_ps(const float *__p)
{
return *(__m128*)__p;
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_loadl_pi(__m128 __a, const void /*__m64*/ *__p)
{
return (__m128)wasm_v128_load64_lane(__p, (v128_t)__a, 0);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_loadh_pi(__m128 __a, const void /*__m64*/ *__p)
{
return (__m128)wasm_v128_load64_lane(__p, (v128_t)__a, 1);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_loadr_ps(const float *__p)
{
__m128 __v = _mm_load_ps(__p);
return (__m128)__f32x4_shuffle(__v, __v, 3, 2, 1, 0);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_loadu_ps(const float *__p)
{
return (__m128)wasm_v128_load(__p);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_load_ps1(const float *__p)
{
return (__m128)wasm_v32x4_load_splat(__p);
}
#define _mm_load1_ps _mm_load_ps1
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_load_ss(const float *__p)
{
return (__m128)wasm_v128_load32_zero(__p);
}
static __inline__ void __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_storel_pi(__m64 *__p, __m128 __a)
{
wasm_v128_store64_lane((void*)__p, (v128_t)__a, 0);
}
static __inline__ void __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_storeh_pi(__m64 *__p, __m128 __a)
{
wasm_v128_store64_lane((void*)__p, (v128_t)__a, 1);
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_store_ps(float *__p, __m128 __a)
{
*(__m128 *)__p = __a;
}
// No NTA cache hint available.
#define _mm_stream_ps _mm_store_ps
#define _MM_HINT_T0 3
#define _MM_HINT_T1 2
#define _MM_HINT_T2 1
#define _MM_HINT_NTA 0
// No prefetch available, dummy it out.
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_prefetch(const void *__p, int __i)
{
((void)__p);
((void)__i);
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_sfence(void)
{
// Wasm/SharedArrayBuffer memory model is sequentially consistent.
// Perhaps a future version of the spec can provide a related fence.
__sync_synchronize();
}
#define _MM_SHUFFLE(w, z, y, x) (((w) << 6) | ((z) << 4) | ((y) << 2) | (x))
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_storer_ps(float *__p, __m128 __a)
{
_mm_store_ps(__p, _mm_shuffle_ps(__a, __a, _MM_SHUFFLE(0, 1, 2, 3)));
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_store_ps1(float *__p, __m128 __a)
{
_mm_store_ps(__p, _mm_shuffle_ps(__a, __a, _MM_SHUFFLE(0, 0, 0, 0)));
}
#define _mm_store1_ps _mm_store_ps1
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_store_ss(float *__p, __m128 __a)
{
wasm_v128_store32_lane((void*)__p, (v128_t)__a, 0);
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_storeu_ps(float *__p, __m128 __a)
{
struct __unaligned {
__m128 __v;
} __attribute__((__packed__, __may_alias__));
((struct __unaligned *)__p)->__v = __a;
}
static __inline__ int __attribute__((__always_inline__, __nodebug__))
_mm_movemask_ps(__m128 __a)
{
return (int)wasm_i32x4_bitmask((v128_t)__a);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_move_ss(__m128 __a, __m128 __b)
{
return (__m128)__f32x4_shuffle(__a, __b, 4, 1, 2, 3);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_add_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_add((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_add_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_add_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_sub_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_sub((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_sub_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_sub_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_mul_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_mul((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_mul_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_mul_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_div_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_div((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_div_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_div_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_min_ps(__m128 __a, __m128 __b)
{
// return (__m128)wasm_f32x4_pmin((v128_t)__a, (v128_t)__b); // TODO: Migrate to this, once it works in VMs
return (__m128)wasm_v128_bitselect((v128_t)__a, (v128_t)__b, (v128_t)wasm_f32x4_lt((v128_t)__a, (v128_t)__b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_min_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_min_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_max_ps(__m128 __a, __m128 __b)
{
// return (__m128)wasm_f32x4_pmax((v128_t)__a, (v128_t)__b); // TODO: Migrate to this, once it works in VMs
return (__m128)wasm_v128_bitselect((v128_t)__a, (v128_t)__b, (v128_t)wasm_f32x4_gt((v128_t)__a, (v128_t)__b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_max_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_max_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_rcp_ps(__m128 __a)
{
return (__m128)wasm_f32x4_div((v128_t)_mm_set1_ps(1.0f), (v128_t)__a);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_rcp_ss(__m128 __a)
{
return _mm_move_ss(__a, _mm_rcp_ps(__a));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_sqrt_ps(__m128 __a)
{
return (__m128)wasm_f32x4_sqrt((v128_t)__a);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_sqrt_ss(__m128 __a)
{
return _mm_move_ss(__a, _mm_sqrt_ps(__a));
}
#define _mm_rsqrt_ps(__a) _mm_rcp_ps(_mm_sqrt_ps((__a)))
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_rsqrt_ss(__m128 __a)
{
return _mm_move_ss(__a, _mm_rsqrt_ps(__a));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_unpackhi_ps(__m128 __a, __m128 __b)
{
return (__m128)__f32x4_shuffle(__a, __b, 2, 6, 3, 7);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_unpacklo_ps(__m128 __a, __m128 __b)
{
return (__m128)__f32x4_shuffle(__a, __b, 0, 4, 1, 5);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_movehl_ps(__m128 __a, __m128 __b)
{
return (__m128)__f32x4_shuffle(__a, __b, 6, 7, 2, 3);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_movelh_ps(__m128 __a, __m128 __b)
{
return (__m128)__f32x4_shuffle(__a, __b, 0, 1, 4, 5);
}
#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3) \
do { \
__m128 __row0 = (row0); \
__m128 __row1 = (row1); \
__m128 __row2 = (row2); \
__m128 __row3 = (row3); \
__m128 __tmp0 = _mm_unpacklo_ps(__row0, __row1); \
__m128 __tmp1 = _mm_unpackhi_ps(__row0, __row1); \
__m128 __tmp2 = _mm_unpacklo_ps(__row2, __row3); \
__m128 __tmp3 = _mm_unpackhi_ps(__row2, __row3); \
(row0) = _mm_movelh_ps(__tmp0, __tmp2); \
(row1) = _mm_movehl_ps(__tmp2, __tmp0); \
(row2) = _mm_movelh_ps(__tmp1, __tmp3); \
(row3) = _mm_movehl_ps(__tmp3, __tmp1); \
} while (0)
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmplt_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_lt((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmplt_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmplt_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmple_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_le((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmple_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmple_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpeq_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_eq((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpeq_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpeq_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpge_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_ge((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpge_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpge_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpgt_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_gt((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpgt_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpgt_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cmpord_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_and(wasm_f32x4_eq((v128_t)__a, (v128_t)__a),
wasm_f32x4_eq((v128_t)__b, (v128_t)__b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cmpord_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpord_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cmpunord_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_or(wasm_f32x4_ne((v128_t)__a, (v128_t)__a),
wasm_f32x4_ne((v128_t)__b, (v128_t)__b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cmpunord_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpunord_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_and_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_and((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_andnot_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_andnot((v128_t)__b, (v128_t)__a);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_or_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_or((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_xor_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_xor((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpneq_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_f32x4_ne((v128_t)__a, (v128_t)__b);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpneq_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpneq_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnge_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_not((v128_t)_mm_cmpge_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnge_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpnge_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpngt_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_not((v128_t)_mm_cmpgt_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpngt_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpngt_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnle_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_not((v128_t)_mm_cmple_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnle_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpnle_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnlt_ps(__m128 __a, __m128 __b)
{
return (__m128)wasm_v128_not((v128_t)_mm_cmplt_ps(__a, __b));
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_cmpnlt_ss(__m128 __a, __m128 __b)
{
return _mm_move_ss(__a, _mm_cmpnlt_ps(__a, __b));
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comieq_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) == wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comige_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) >= wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comigt_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) > wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comile_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) <= wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comilt_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) < wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_comineq_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) != wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomieq_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) == wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomige_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) >= wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomigt_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) > wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomile_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) <= wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomilt_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) < wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_ucomineq_ss(__m128 __a, __m128 __b)
{
return wasm_f32x4_extract_lane((v128_t)__a, 0) != wasm_f32x4_extract_lane((v128_t)__b, 0);
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_cvtsi32_ss(__m128 __a, int __b)
{
__f32x4 __v = (__f32x4)__a;
__v[0] = (float)__b;
return (__m128)__v;
}
#define _mm_cvt_si2ss _mm_cvtsi32_ss
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cvtss_si32(__m128 __a)
{
float e = ((__f32x4)__a)[0];
if (e < 2147483648.0f && e >= -2147483648.0f && (lrint(e) != 0 || fabsf(e) < 2.f))
return lrint(e);
else
return (int)0x80000000;
}
#define _mm_cvt_ss2si _mm_cvtss_si32
static __inline__ int __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW)) _mm_cvttss_si32(__m128 __a)
{
float e = ((__f32x4)__a)[0];
if (e < 2147483648.0f && e >= -2147483648.0f && (lrint(e) != 0 || fabsf(e) < 2.f))
return (int)e;
else
return (int)0x80000000;
}
#define _mm_cvtt_ss2si _mm_cvttss_si32
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_cvtsi64_ss(__m128 __a, long long __b)
{
__f32x4 __v = (__f32x4)__a;
__v[0] = (float)__b;
return (__m128)__v;
}
static __inline__ long long __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_cvtss_si64(__m128 __a)
{
float e = ((__f32x4)__a)[0];
long long x = llrintf(e);
if (e <= LLONG_MAX && e >= LLONG_MIN && (x != 0 || fabsf(e) < 2.f))
return x;
else
return 0x8000000000000000LL;
}
static __inline__ long long __attribute__((__always_inline__, __nodebug__, DIAGNOSE_SLOW))
_mm_cvttss_si64(__m128 __a)
{
float e = ((__f32x4)__a)[0];
long long x = llrintf(e);
if (e <= LLONG_MAX && e >= LLONG_MIN && (x != 0 || fabsf(e) < 2.f))
return (long long)e;
else
return 0x8000000000000000LL;
}
static __inline__ float __attribute__((__always_inline__, __nodebug__))
_mm_cvtss_f32(__m128 __a)
{
return (float)((__f32x4)__a)[0];
}
#define _mm_malloc(__size, __align) memalign((__align), (__size))
#define _mm_free free
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_undefined()
{
__m128 val;
return val;
}
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_undefined_ps()
{
__m128 val;
return val;
}
#define _MM_EXCEPT_MASK 0x003f
#define _MM_EXCEPT_INVALID 0x0001
#define _MM_EXCEPT_DENORM 0x0002
#define _MM_EXCEPT_DIV_ZERO 0x0004
#define _MM_EXCEPT_OVERFLOW 0x0008
#define _MM_EXCEPT_UNDERFLOW 0x0010
#define _MM_EXCEPT_INEXACT 0x0020
#define _MM_MASK_MASK 0x1f80
#define _MM_MASK_INVALID 0x0080
#define _MM_MASK_DENORM 0x0100
#define _MM_MASK_DIV_ZERO 0x0200
#define _MM_MASK_OVERFLOW 0x0400
#define _MM_MASK_UNDERFLOW 0x0800
#define _MM_MASK_INEXACT 0x1000
#define _MM_ROUND_MASK 0x6000
#define _MM_ROUND_NEAREST 0x0000
#define _MM_ROUND_DOWN 0x2000
#define _MM_ROUND_UP 0x4000
#define _MM_ROUND_TOWARD_ZERO 0x6000
#define _MM_FLUSH_ZERO_MASK 0x8000
#define _MM_FLUSH_ZERO_ON 0x8000
#define _MM_FLUSH_ZERO_OFF 0x0000
static __inline__ int __attribute__((__always_inline__, __nodebug__))
_mm_getcsr()
{
return _MM_MASK_INEXACT | _MM_MASK_DENORM | _MM_MASK_DIV_ZERO | _MM_MASK_OVERFLOW | _MM_MASK_UNDERFLOW | _MM_MASK_INVALID
| _MM_ROUND_NEAREST | _MM_FLUSH_ZERO_OFF;
}
#define _MM_GET_EXCEPTION_STATE() (_mm_getcsr() & _MM_EXCEPT_MASK)
#define _MM_GET_EXCEPTION_MASK() (_mm_getcsr() & _MM_MASK_MASK)
#define _MM_GET_ROUNDING_MODE() (_mm_getcsr() & _MM_ROUND_MASK)
#define _MM_GET_FLUSH_ZERO_MODE() (_mm_getcsr() & _MM_FLUSH_ZERO_MASK)
// Unavailable functions:
// void _MM_SET_EXCEPTION_STATE(unsigned int __a);
// void _MM_SET_EXCEPTION_MASK(unsigned int __a);
// void _MM_GET_ROUNDING_MODE(unsigned int __a);
// void _MM_GET_FLUSH_ZERO_MODE(unsigned int __a);
#endif /* __emscripten_xmmintrin_h__ */
+327
View File
@@ -0,0 +1,327 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
mod shim;
use libwebp_sys as ffi;
use std::ffi::CStr;
use std::ptr;
use wasm_bindgen::prelude::*;
const DEMUX_ABI: i32 = ffi::WEBP_DEMUX_ABI_VERSION as i32;
const MUX_ABI: i32 = ffi::WEBP_MUX_ABI_VERSION as i32;
const ENCODER_ABI: i32 = ffi::WEBP_ENCODER_ABI_VERSION as i32;
const DECODER_ABI: i32 = ffi::WEBP_DECODER_ABI_VERSION as i32;
const LOSSLESS_FORMAT: i32 = 2;
fn webp_data(bytes: &[u8]) -> ffi::WebPData {
ffi::WebPData {
bytes: bytes.as_ptr(),
size: bytes.len(),
}
}
#[wasm_bindgen]
pub fn webp_probe(bytes: &[u8]) -> Result<Box<[u32]>, JsError> {
let data = webp_data(bytes);
let dmux = unsafe { ffi::WebPDemuxInternal(&data, 0, ptr::null_mut(), DEMUX_ABI) };
if dmux.is_null() {
return Err(JsError::new("webp demux failed"));
}
let get = |feature| unsafe { ffi::WebPDemuxGetI(dmux, feature) };
let width = get(ffi::WebPFormatFeature::WEBP_FF_CANVAS_WIDTH);
let height = get(ffi::WebPFormatFeature::WEBP_FF_CANVAS_HEIGHT);
let frame_count = get(ffi::WebPFormatFeature::WEBP_FF_FRAME_COUNT);
let loop_count = get(ffi::WebPFormatFeature::WEBP_FF_LOOP_COUNT);
let flags = get(ffi::WebPFormatFeature::WEBP_FF_FORMAT_FLAGS);
let has_alpha = flags & ffi::WebPFeatureFlags::ALPHA_FLAG as u32 != 0;
let all_lossless = unsafe { all_frames_lossless(dmux) };
unsafe { ffi::WebPDemuxDelete(dmux) };
Ok(Box::new([
width,
height,
frame_count,
loop_count,
has_alpha as u32,
all_lossless as u32,
]))
}
unsafe fn all_frames_lossless(dmux: *const ffi::WebPDemuxer) -> bool {
unsafe {
let mut iter: ffi::WebPIterator = std::mem::zeroed();
if ffi::WebPDemuxGetFrame(dmux, 1, &mut iter) == 0 {
return false;
}
let mut lossless = true;
loop {
let mut features: ffi::WebPBitstreamFeatures = std::mem::zeroed();
let status = ffi::WebPGetFeaturesInternal(
iter.fragment.bytes,
iter.fragment.size,
&mut features,
DECODER_ABI,
);
if status != ffi::VP8StatusCode::VP8_STATUS_OK || features.format != LOSSLESS_FORMAT {
lossless = false;
break;
}
if ffi::WebPDemuxNextFrame(&mut iter) == 0 {
break;
}
}
ffi::WebPDemuxReleaseIterator(&mut iter);
lossless
}
}
#[wasm_bindgen]
pub struct AnimDecoder {
dec: *mut ffi::WebPAnimDecoder,
_bytes: Box<[u8]>,
frame: *mut u8,
timestamp: i32,
}
#[wasm_bindgen]
impl AnimDecoder {
#[wasm_bindgen(constructor)]
pub fn new(bytes: Box<[u8]>) -> Result<AnimDecoder, JsError> {
let dec = unsafe {
let mut options: ffi::WebPAnimDecoderOptions = std::mem::zeroed();
if ffi::WebPAnimDecoderOptionsInitInternal(&mut options, DEMUX_ABI) == 0 {
return Err(JsError::new("webp decoder options init failed"));
}
options.color_mode = ffi::WEBP_CSP_MODE::MODE_RGBA;
options.use_threads = 0;
let data = webp_data(&bytes);
ffi::WebPAnimDecoderNewInternal(&data, &options, DEMUX_ABI)
};
if dec.is_null() {
return Err(JsError::new("webp decoder init failed"));
}
Ok(AnimDecoder {
dec,
_bytes: bytes,
frame: ptr::null_mut(),
timestamp: 0,
})
}
#[wasm_bindgen(js_name = next)]
pub fn next_frame(&mut self) -> Result<bool, JsError> {
if unsafe { ffi::WebPAnimDecoderHasMoreFrames(self.dec) } == 0 {
return Ok(false);
}
let mut frame = ptr::null_mut();
let mut timestamp = 0;
if unsafe { ffi::WebPAnimDecoderGetNext(self.dec, &mut frame, &mut timestamp) } == 0 {
return Err(JsError::new("webp frame decode failed"));
}
self.frame = frame;
self.timestamp = timestamp;
Ok(true)
}
#[wasm_bindgen(getter)]
pub fn frame_ptr(&self) -> u32 {
self.frame as usize as u32
}
#[wasm_bindgen(getter)]
pub fn end_timestamp(&self) -> i32 {
self.timestamp
}
}
impl Drop for AnimDecoder {
fn drop(&mut self) {
unsafe { ffi::WebPAnimDecoderDelete(self.dec) };
}
}
#[wasm_bindgen]
pub struct AnimEncoder {
enc: *mut ffi::WebPAnimEncoder,
width: u32,
height: u32,
lossless: bool,
}
#[wasm_bindgen]
impl AnimEncoder {
#[wasm_bindgen(constructor)]
pub fn new(
width: u32,
height: u32,
loop_count: u32,
lossless: bool,
) -> Result<AnimEncoder, JsError> {
let (kmin, kmax) = if lossless { (9, 17) } else { (4, 5) };
let enc = unsafe {
let mut options: ffi::WebPAnimEncoderOptions = std::mem::zeroed();
if ffi::WebPAnimEncoderOptionsInitInternal(&mut options, MUX_ABI) == 0 {
return Err(JsError::new("webp encoder options init failed"));
}
options.anim_params.loop_count = loop_count.min(i32::MAX as u32) as i32;
options.anim_params.bgcolor = 0;
options.minimize_size = 0;
options.allow_mixed = 0;
options.kmin = kmin;
options.kmax = kmax;
ffi::WebPAnimEncoderNewInternal(width as i32, height as i32, &options, MUX_ABI)
};
if enc.is_null() {
return Err(JsError::new("webp encoder init failed"));
}
Ok(AnimEncoder {
enc,
width,
height,
lossless,
})
}
pub fn add(
&mut self,
rgba: &[u8],
start_ms: i32,
quality: f32,
method: i32,
) -> Result<(), JsError> {
if rgba.len() as u64 != u64::from(self.width) * u64::from(self.height) * 4 {
return Err(JsError::new("webp frame size mismatch"));
}
let added = unsafe {
let mut config: ffi::WebPConfig = std::mem::zeroed();
if ffi::WebPConfigInitInternal(
&mut config,
ffi::WebPPreset::WEBP_PRESET_DEFAULT,
quality,
ENCODER_ABI,
) == 0
{
return Err(JsError::new("webp config init failed"));
}
config.quality = quality;
config.method = method;
config.lossless = self.lossless as i32;
config.thread_level = 0;
config.alpha_quality = 100;
config.exact = 0;
config.use_sharp_yuv = 0;
let mut picture: ffi::WebPPicture = std::mem::zeroed();
if ffi::WebPPictureInitInternal(&mut picture, ENCODER_ABI) == 0 {
return Err(JsError::new("webp picture init failed"));
}
picture.width = self.width as i32;
picture.height = self.height as i32;
picture.use_argb = 1;
if ffi::WebPPictureImportRGBA(&mut picture, rgba.as_ptr(), self.width as i32 * 4) == 0 {
ffi::WebPPictureFree(&mut picture);
return Err(JsError::new("webp picture import failed"));
}
let added = ffi::WebPAnimEncoderAdd(self.enc, &mut picture, start_ms, &config);
ffi::WebPPictureFree(&mut picture);
added
};
if added == 0 {
return Err(self.error("webp frame encode failed"));
}
Ok(())
}
pub fn finish(&mut self, end_ms: i32) -> Result<Vec<u8>, JsError> {
if unsafe { ffi::WebPAnimEncoderAdd(self.enc, ptr::null_mut(), end_ms, ptr::null()) } == 0 {
return Err(self.error("webp encoder flush failed"));
}
let mut data = ffi::WebPData::default();
if unsafe { ffi::WebPAnimEncoderAssemble(self.enc, &mut data) } == 0 {
return Err(self.error("webp assemble failed"));
}
let out = unsafe { std::slice::from_raw_parts(data.bytes, data.size) }.to_vec();
unsafe { ffi::WebPFree(data.bytes as *mut _) };
Ok(out)
}
fn error(&self, what: &str) -> JsError {
let message = unsafe { ffi::WebPAnimEncoderGetError(self.enc) };
if message.is_null() {
return JsError::new(what);
}
let detail = unsafe { CStr::from_ptr(message) }.to_string_lossy();
JsError::new(&format!("{what}: {detail}"))
}
}
impl Drop for AnimEncoder {
fn drop(&mut self) {
unsafe { ffi::WebPAnimEncoderDelete(self.enc) };
}
}
#[cfg(test)]
mod tests {
use super::*;
fn frame(width: u32, height: u32, seed: u8) -> Vec<u8> {
(0..width * height)
.flat_map(|index| {
let value = (index as u8).wrapping_mul(seed);
[
value,
seed,
value ^ seed,
if index % 3 == 0 { 0 } else { 255 },
]
})
.collect()
}
fn encode(lossless: bool) -> Vec<u8> {
let mut encoder = AnimEncoder::new(16, 8, 3, lossless).expect("encoder");
let delays = [40, 70, 10];
let mut start = 0;
for (index, delay) in delays.iter().enumerate() {
encoder
.add(&frame(16, 8, index as u8 + 1), start, 75.0, 4)
.expect("add");
start += delay;
}
encoder.finish(start).expect("finish")
}
#[test]
fn probe_reports_canvas_frames_loop_alpha_and_lossless() {
let lossless = encode(true);
assert_eq!(
&*webp_probe(&lossless).expect("probe"),
&[16, 8, 3, 3, 1, 1]
);
let lossy = encode(false);
let probe = webp_probe(&lossy).expect("probe");
assert_eq!(&probe[..5], &[16, 8, 3, 3, 1]);
assert_eq!(probe[5], 0);
}
#[test]
fn lossless_round_trip_keeps_pixels_and_end_timestamps() {
let bytes = encode(true);
let mut decoder = AnimDecoder::new(bytes.into_boxed_slice()).expect("decoder");
let mut ends = Vec::new();
let mut index = 0u8;
while decoder.next_frame().expect("next") {
let pixels = unsafe { std::slice::from_raw_parts(decoder.frame, 16 * 8 * 4) };
let expected = frame(16, 8, index + 1);
for (got, want) in pixels.chunks(4).zip(expected.chunks(4)) {
assert_eq!(got[3], want[3]);
if want[3] != 0 {
assert_eq!(got, want);
}
}
ends.push(decoder.end_timestamp());
index += 1;
}
assert_eq!(ends, [40, 110, 120]);
}
}
+110
View File
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::alloc::{Layout, alloc, alloc_zeroed, dealloc};
use std::ffi::{c_int, c_void};
const ALIGN: usize = 16;
unsafe fn shim_alloc(size: usize, zeroed: bool) -> *mut c_void {
let Some(full) = size.checked_add(ALIGN) else {
return std::ptr::null_mut();
};
let Ok(layout) = Layout::from_size_align(full, ALIGN) else {
return std::ptr::null_mut();
};
unsafe {
let ptr = if zeroed {
alloc_zeroed(layout)
} else {
alloc(layout)
};
if ptr.is_null() {
return std::ptr::null_mut();
}
ptr.cast::<usize>().write(full);
ptr.add(ALIGN).cast()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fluxwebp_shim_malloc(size: usize) -> *mut c_void {
unsafe { shim_alloc(size, false) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fluxwebp_shim_calloc(nmemb: usize, size: usize) -> *mut c_void {
match nmemb.checked_mul(size) {
Some(total) => unsafe { shim_alloc(total, true) },
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fluxwebp_shim_free(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe {
let base = ptr.cast::<u8>().sub(ALIGN);
let full = base.cast::<usize>().read();
dealloc(base, Layout::from_size_align_unchecked(full, ALIGN));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fluxwebp_shim_qsort(
base: *mut c_void,
n: usize,
size: usize,
compar: unsafe extern "C" fn(*const c_void, *const c_void) -> c_int,
) {
if n < 2 || size == 0 {
return;
}
unsafe {
let bytes = std::slice::from_raw_parts_mut(base.cast::<u8>(), n * size);
let copy = bytes.to_vec();
let mut idx: Vec<usize> = (0..n).collect();
idx.sort_by(|&a, &b| {
let r = compar(
copy.as_ptr().add(a * size).cast(),
copy.as_ptr().add(b * size).cast(),
);
r.cmp(&0)
});
for (dst, &src) in idx.iter().enumerate() {
bytes[dst * size..dst * size + size]
.copy_from_slice(&copy[src * size..src * size + size]);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_pow(x: f64, y: f64) -> f64 {
x.powf(y)
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_log(x: f64) -> f64 {
x.ln()
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_log10(x: f64) -> f64 {
x.log10()
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_expf(x: f32) -> f32 {
x.exp()
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_logf(x: f32) -> f32 {
x.ln()
}
#[unsafe(no_mangle)]
pub extern "C" fn fluxwebp_shim_round(x: f64) -> f64 {
x.round()
}