mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 04:55:42 +09:00
fix(app): crop animated images on web with libwebp (#2947)
This commit is contained in:
1 parent
e0843ac4f5
commit
bbfe809bef
104 files changed
+10884
-7316
No files matched your search
Generated
+160
@@ -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",
|
||||
]
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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__ */
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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(©[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()
|
||||
}
|
||||
Reference in new issue
Block a user