Compare commits

...

75 Commits

Author SHA1 Message Date
9988505b2c asdf 2020-11-30 09:15:10 -05:00
0e6610c007 Move back btn 2020-11-27 16:42:53 +00:00
74477dfe6b Button size tweak 2020-11-27 15:34:57 +00:00
59713b7687 Back button 2020-11-27 15:20:40 +00:00
62102fecab two-up styles 2020-11-27 14:48:59 +00:00
b1fc4d1c57 Load demo img 2020-11-27 13:26:33 +00:00
02e0206285 Fix prerender SVG size 2020-11-27 11:49:10 +00:00
8f21972e70 Remove debug stuff 2020-11-27 11:37:20 +00:00
8d9ed5ad3e Responsive main section 2020-11-27 11:22:06 +00:00
be5a96a42a Responsive demo section 2020-11-27 11:06:31 +00:00
eeafef4e14 Replace thumbnails 2020-11-27 10:42:22 +00:00
067ce686b5 Tweak size 2020-11-27 10:03:40 +00:00
c78ad6c795 Home page with demo loading 2020-11-26 17:02:36 +00:00
be818caf48 Install button 2020-11-26 16:08:55 +00:00
4abc7f40d1 More CSS optimisation 2020-11-26 16:08:43 +00:00
903c8b5d02 Optimise amount of initial CSS 2020-11-26 15:40:36 +00:00
4c03ceee04 Footer 2020-11-26 15:13:35 +00:00
a2c465abdf Pause time while page is hidden 2020-11-26 14:47:08 +00:00
251dc2ce9b Get initial focus 2020-11-26 13:31:23 +00:00
e8948167db Fade into the center 2020-11-26 12:34:08 +00:00
9e1fb6dfb4 Background blobs 2020-11-26 11:38:50 +00:00
2d1a3b543a Get styles from CSS 2020-11-25 16:35:49 +00:00
4aaa5ffa78 lol 2020-11-25 15:54:35 +00:00
6fa13b919b Update canvas on resize if not updating every frame 2020-11-25 15:30:59 +00:00
fcef2b2d3e Initial blob shape 2020-11-25 15:13:53 +00:00
4d5761c780 Predictable initial blob position 2020-11-25 14:07:54 +00:00
be3ab8b6d1 Logo and animated blobs 2020-11-25 13:39:48 +00:00
49620e4c8f Paste button 2020-11-23 15:59:32 +00:00
3d1ecc1215 Don't restrict drag & drop to images (so it works with wp2 & JXL) 2020-11-23 14:23:21 +00:00
25fb1a9c80 Fix dev server config & redirect /editor 2020-11-23 14:20:41 +00:00
3ae1cf86f5 Autoformat staged C++ and Rust 2020-11-21 03:56:30 +00:00
a699a5c4dc Fix Lossless+Progressive JXL.
Also limit the workaround for memory usage to large images.
2020-11-20 22:27:49 +00:00
613401c541 Add .gitattributes to fold generated files in PRs 2020-11-20 22:21:21 +00:00
f450373e3f Fix nightly Rust pinning; rebuild Oxi 2020-11-20 22:21:21 +00:00
750872aca6 Delete old oxipng files 2020-11-20 22:21:21 +00:00
beaabe47dc Merge pull request #858 from GoogleChromeLabs/gh-actions
Switch from Travis to Github Actions
2020-11-20 17:08:51 -05:00
8f7369068c Remove sizereport step
Apparently it doesn't exist anymore, even though it was referenced in Travis.
2020-11-20 21:41:57 +00:00
10bfd60e20 Try to fix multiple OS 2020-11-20 21:39:18 +00:00
7f08348509 Delete .travis.yml 2020-11-20 21:37:38 +00:00
f77ddac652 Add Github Action 2020-11-20 21:37:00 +00:00
13631f1cfc Extra Wp2 Options (#853)
* wip

* wip

* Add extra options

* Even more options!

* Update src/features/encoders/wp2/client/index.tsx

Co-authored-by: Surma <surma@surma.dev>

Co-authored-by: Surma <surma@surma.dev>
2020-11-20 16:12:38 +00:00
f11e692d58 Unset loading on error. Fixes #855 2020-11-20 16:11:57 +00:00
f0221b626d Prettier ignore file 2020-11-20 11:51:43 +00:00
10c5ed0495 Don't prettify codec code 2020-11-20 10:48:41 +00:00
d945c79796 Use run-p for cross-platform parallel runs (#850) 2020-11-20 08:53:44 +00:00
30b628c1b9 Fixing windows build (#849)
Co-authored-by: Ingvar Stepanyan <rreverser@google.com>
2020-11-19 15:03:51 +00:00
6ebf94d1b6 Auto edge filter 2020-11-19 11:35:12 +00:00
a229662bed Change JXL defaults 2020-11-19 11:27:03 +00:00
e995b445ef Updating node version and lockfile 2020-11-19 11:20:04 +00:00
6da590c7d0 Merge branch 'rollup-build' into dev
# Conflicts:
#	_headers.ejs
#	codecs/oxipng/pkg/squoosh_oxipng_bg.js
#	src/codecs/avif/encoder.ts
#	src/codecs/oxipng/encoder.ts
#	src/codecs/processor.ts
#	src/codecs/util.ts
#	src/components/intro/imgs/logo.svg
#	src/missing-types.d.ts
#	webpack.config.js
2020-11-19 11:12:29 +00:00
56e10b3aa2 Rollup build 2020-11-19 11:00:23 +00:00
fd87ae7d2a Force-rebuild codecs with -O3
Follow-up to https://github.com/GoogleChromeLabs/squoosh/pull/838.

I have no idea what the JS+Wasm diffs in the original PR even represented if they weren't proper rebuilds, but this time I just removed node_modules in all codecs to enforce a proper, clean rebuild for each C++ codec.

(Still think the speed-ups are worth it.)
2020-11-02 17:37:35 +00:00
5df7dd7590 Update helper.Makefile 2020-11-02 13:54:41 +00:00
013946b137 Pass CODEC_DIR and LIBAOM_DIR via export
Slightly simpler than passing them in HELPER_MAKEFLAGS.
2020-11-02 13:54:41 +00:00
81c183b0d6 Restructure the AVIF directories
Change the way AVIF finds AOM from default ([avif source]/ext/aom) to custom paths. This allows us to avoid unpacking same archives into duplicate folders, and instead make multiple builds from the same source.
2020-11-02 13:54:41 +00:00
f523db6403 Try out new flags for building only AVIF encoder/decoder
See the discussion in https://github.com/AOMediaCodec/libavif/issues/254 where this was implemented.

This allows us to avoid using ERROR_ON_UNDEFINED_SYMBOLS and build a truly separate encoder/decoder libs.
2020-11-02 13:54:41 +00:00
cc6ea9e11c Switch to -O3 for C++ codecs 2020-11-02 12:46:12 +00:00
bd4b67037b Further optimize logo.svg (#761)
Co-authored-by: Jake Archibald <jaffathecake@gmail.com>
2020-10-15 15:48:56 +01:00
8c5c97e106 Remove obsolete @ts-ignore 2020-10-07 20:42:48 +01:00
a9d3bd71b5 Bump oxipng
Integrating some upstream fixes from my branch.
2020-10-07 20:42:48 +01:00
0d0a9b4cdf Add COOP+COEP headers 2020-10-07 20:42:48 +01:00
f583770696 Explicitly disable HDR only for encoder 2020-10-07 20:42:48 +01:00
bae243ccdb Add feature detection to OxiPNG 2020-10-07 20:42:48 +01:00
02c113a68f Point oxipng to a patched version
Some upstream changes required for parallel build to work.
2020-10-07 20:42:48 +01:00
600eead007 Disable parallel feature for non-parallel OxiPNG 2020-10-07 20:42:48 +01:00
05416768d5 Update oxipng build system 2020-10-07 20:42:48 +01:00
35d31f2324 Add some comments to explain Rust thread glue 2020-10-07 20:42:48 +01:00
82fadac70e Fixup import.meta in OxiPNG 2020-10-07 20:42:48 +01:00
47f9d22dd8 Switch to crossbeam-channel
Still not perfect due to usage of a static global, but this is much cleaner and more efficient thanks to proper blocking of Workers that wait for new messages instead of a manual spin-loop.
2020-10-07 20:42:48 +01:00
9420dba3bc Parallel OxiPNG improvements
- Refactor to work around Chromium's issue with postMessage queuing. https://bugs.chromium.org/p/chromium/issues/detail?id=1075645
 - Convert codec code to TypeScript.
 - Make separate parallel and non-parallel builds.
 - Switch to nightly Rust for OxiPNG to allow parallel builds (but also reuse it for regular builds to avoid installing two toolchains).
2020-10-07 20:42:48 +01:00
e462875807 Type fix for gesturestart event 2020-10-07 20:42:48 +01:00
0747d2c419 Rework fallback for postMessage issue
Now initialise all workers with module+memory separately, and then instead of using postMessage to send thread pointers, push them into a crossbeam-deque on the Rust side.

Rayon already depends on crossbeam-dequeue, so we're not even adding another dependency, and this model allows us to push "tasks" (thread pointers) on the main thread and pop them on worker threads in arbitrary order without sacrificing correctness.
2020-10-07 20:42:48 +01:00
4c658b79ef OxiPNG + threads PoC 2020-10-07 20:42:48 +01:00
685558847f Multithread AVIF PoC 2020-10-07 20:42:48 +01:00
63ac34a662 Promisify emscripten modules & fix webp examples (#817) 2020-09-30 00:05:59 +01:00
359 changed files with 31354 additions and 19609 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
/codecs/**/*.js linguist-generated=true
/codecs/*/pkg*/*.d.ts linguist-generated=true

22
.github/workflows/node.js.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Node.js CI
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- id: nvmrc
uses: browniebroke/read-nvmrc-action@v1
- uses: actions/setup-node@v1
with:
node-version: '${{ steps.nvmrc.outputs.node_version }}'
- run: npm ci
- run: npm run build

9
.gitignore vendored
View File

@ -1,6 +1,11 @@
.tmp
node_modules
/build
/*.log
*.scss.d.ts
*.css.d.ts
build
*.o
# Auto-generated by lib/feature-plugin.js
src/features-worker/index.ts
src/client/lazy-app/worker-bridge/meta.ts
src/client/lazy-app/feature-meta/index.ts

2
.nvmrc
View File

@ -1 +1 @@
10.16.2
14.15.1

12
.prettierignore Normal file
View File

@ -0,0 +1,12 @@
codecs
.tmp
node_modules
*.scss.d.ts
*.css.d.ts
build
*.o
# Auto-generated by lib/feature-plugin.js
src/features-worker/index.ts
src/client/lazy-app/worker-bridge/meta.ts
src/client/lazy-app/feature-meta/index.ts

4
.prettierrc.json Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}

View File

@ -1,7 +0,0 @@
language: node_js
cache: npm
script: npm run build
after_success: npm run sizereport
os:
- linux
- windows

View File

@ -1,18 +0,0 @@
# Long-term cache by default.
/*
Cache-Control: max-age=31536000
# And here are the exceptions:
/
Cache-Control: no-cache
/serviceworker.js
Cache-Control: no-cache
/manifest.json
Cache-Control: must-revalidate, max-age=3600
# URLs in /assets do not include a hash and are mutable.
# But it isn't a big deal if the user gets an old version.
/assets/*
Cache-Control: must-revalidate, max-age=3600

19
client-tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "./generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "dom", "dom.iterable"],
"types": []
},
"include": [
"src/features/**/client/**/*",
"src/features/**/shared/**/*",
"src/features/client-utils/**/*",
"src/shared/**/*",
"src/client/**/*",
// Not really clean, but we need these to access the type of the functions
// for comlink
"src/features/**/worker/**/*",
"src/features-worker/**/*",
"src/features/worker-utils/**/*"
]
}

View File

@ -1,138 +1,84 @@
CODEC_URL = https://github.com/AOMediaCodec/libavif/archive/v0.8.1.tar.gz
CODEC_URL = https://github.com/AOMediaCodec/libavif/archive/31d7c6d1e32cf467ac24fb8c7a76c4902a4c00db.tar.gz
CODEC_PACKAGE = node_modules/libavif.tar.gz
CODEC_ENC_DIR = node_modules/libavif-enc
CODEC_ENC_BUILD_DIR := $(CODEC_ENC_DIR)/build
CODEC_ENC_OUT := $(CODEC_ENC_BUILD_DIR)/libavif.a
CODEC_DEC_DIR = node_modules/libavif-dec
CODEC_DEC_BUILD_DIR := $(CODEC_DEC_DIR)/build
CODEC_DEC_OUT := $(CODEC_DEC_BUILD_DIR)/libavif.a
LIBAOM_URL = https://aomedia.googlesource.com/aom/+archive/v2.0.0.tar.gz
LIBAOM_PACKAGE = node_modules/libaom.tar.gz
LIBAOM_ENC_DIR := $(CODEC_ENC_DIR)/ext/aom
LIBAOM_ENC_BUILD_DIR := $(LIBAOM_ENC_DIR)/build.libavif
LIBAOM_ENC_OUT := $(LIBAOM_ENC_BUILD_DIR)/libaom.a
LIBAOM_DEC_DIR := $(CODEC_DEC_DIR)/ext/aom
LIBAOM_DEC_BUILD_DIR := $(LIBAOM_DEC_DIR)/build.libavif
LIBAOM_DEC_OUT := $(LIBAOM_DEC_BUILD_DIR)/libaom.a
export CODEC_DIR = node_modules/libavif
BUILD_DIR := node_modules/build
ENC_BUILD_DIR := $(BUILD_DIR)/enc
ENC_MT_BUILD_DIR := $(BUILD_DIR)/enc-mt
DEC_BUILD_DIR := $(BUILD_DIR)/dec
export LIBAOM_DIR = node_modules/libaom
OUT_ENC_JS = enc/avif_enc.js
OUT_ENC_CPP = $(OUT_ENC_JS:.js=.cpp)
OUT_ENC_WASM = $(OUT_ENC_JS:.js=.wasm)
OUT_ENC_MT_JS = enc/avif_enc_mt.js
OUT_DEC_JS = dec/avif_dec.js
OUT_DEC_CPP = $(OUT_DEC_JS:.js=.cpp)
OUT_DEC_WASM = $(OUT_DEC_JS:.js=.wasm)
# ERROR_ON_UNDEFINED_SYMBOLS=0 is needed to seperate the encoder and decoder
EMSCRIPTEN_FLAGS = ${CXXFLAGS} \
${LDFLAGS} \
--bind \
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s ERROR_ON_UNDEFINED_SYMBOLS=0
OUT_ENC_CPP = enc/avif_enc.cpp
OUT_DEC_CPP = dec/avif_dec.cpp
CODEC_EMCMAKE = emcmake cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=0 \
-DAVIF_CODEC_AOM=1 \
-DAVIF_LOCAL_AOM=1 \
../
LIBAOM_FLAGS = -DCMAKE_BUILD_TYPE=Release \
-DENABLE_CCACHE=0 \
-DAOM_TARGET_CPU=generic \
-DENABLE_DOCS=0 \
-DENABLE_TESTS=0 \
-DENABLE_EXAMPLES=0 \
-DENABLE_TOOLS=0 \
-DCONFIG_ACCOUNTING=1 \
-DCONFIG_INSPECTION=0 \
-DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 \
-DCONFIG_WEBM_IO=0
HELPER_MAKEFLAGS := -f helper.Makefile
.PHONY: all clean
all: $(OUT_ENC_JS) $(OUT_DEC_JS)
all: $(OUT_ENC_JS) $(OUT_DEC_JS) $(OUT_ENC_MT_JS)
$(OUT_ENC_JS): $(OUT_ENC_CPP) $(LIBAOM_ENC_OUT) $(CODEC_ENC_OUT)
$(CXX) \
-I $(CODEC_ENC_DIR)/include \
${EMSCRIPTEN_FLAGS} \
-s 'EXPORT_NAME="$(basename $(@F))"' \
-o $@ \
$+
$(OUT_ENC_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(MAKE) \
$(HELPER_MAKEFLAGS) \
BUILD_DIR=$(ENC_BUILD_DIR) \
OUT_JS=$@ \
OUT_CPP=$< \
LIBAOM_FLAGS="\
-DCONFIG_AV1_DECODER=0 \
-DCONFIG_MULTITHREAD=0 \
-DCONFIG_AV1_HIGHBITDEPTH=0 \
" \
LIBAVIF_FLAGS="-DAVIF_CODEC_AOM_DECODE=0"
$(OUT_DEC_JS): $(OUT_DEC_CPP) $(LIBAOM_DEC_OUT) $(CODEC_DEC_OUT)
$(CXX) \
-I $(CODEC_DEC_DIR)/include \
${EMSCRIPTEN_FLAGS} \
-s 'EXPORT_NAME="$(basename $(@F))"' \
-o $@ \
$+
$(OUT_ENC_MT_JS): $(OUT_ENC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(MAKE) \
$(HELPER_MAKEFLAGS) \
BUILD_DIR=$(ENC_MT_BUILD_DIR) \
OUT_JS=$@ \
OUT_CPP=$< \
LIBAOM_FLAGS="\
-DCONFIG_AV1_DECODER=0 \
-DCONFIG_AV1_HIGHBITDEPTH=0 \
" \
LIBAVIF_FLAGS="-DAVIF_CODEC_AOM_DECODE=0" \
OUT_FLAGS="-pthread"
$(CODEC_ENC_OUT): $(CODEC_ENC_DIR)/CMakeLists.txt $(LIBAOM_ENC_OUT)
mkdir -p $(CODEC_ENC_BUILD_DIR)
cd $(CODEC_ENC_BUILD_DIR) && \
$(CODEC_EMCMAKE) && \
$(MAKE)
$(CODEC_DEC_OUT): $(CODEC_DEC_DIR)/CMakeLists.txt $(LIBAOM_DEC_OUT)
mkdir -p $(CODEC_DEC_BUILD_DIR)
cd $(CODEC_DEC_BUILD_DIR) && \
$(CODEC_EMCMAKE) && \
$(MAKE)
$(LIBAOM_ENC_OUT): $(LIBAOM_ENC_DIR)/CMakeLists.txt
mkdir -p $(LIBAOM_ENC_BUILD_DIR)
cd $(LIBAOM_ENC_BUILD_DIR) && \
emcmake cmake \
$(LIBAOM_FLAGS) \
-DCONFIG_AV1_DECODER=0 \
-DCONFIG_AV1_HIGHBITDEPTH=0 \
../ && \
$(MAKE)
$(LIBAOM_DEC_OUT): $(LIBAOM_DEC_DIR)/CMakeLists.txt
mkdir -p $(LIBAOM_DEC_BUILD_DIR)
cd $(LIBAOM_DEC_BUILD_DIR) && \
emcmake cmake \
$(LIBAOM_FLAGS) \
-DCONFIG_AV1_ENCODER=0 \
../ && \
$(MAKE)
$(CODEC_ENC_DIR)/CMakeLists.txt: $(CODEC_ENC_DIR)
$(CODEC_DEC_DIR)/CMakeLists.txt: $(CODEC_DEC_DIR)
$(LIBAOM_ENC_DIR)/CMakeLists.txt: $(LIBAOM_ENC_DIR)
$(LIBAOM_DEC_DIR)/CMakeLists.txt: $(LIBAOM_DEC_DIR)
$(OUT_DEC_JS): $(OUT_DEC_CPP) $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_DIR)/CMakeLists.txt
$(MAKE) \
$(HELPER_MAKEFLAGS) \
BUILD_DIR=$(DEC_BUILD_DIR) \
OUT_JS=$@ \
OUT_CPP=$< \
LIBAOM_FLAGS="\
-DCONFIG_AV1_ENCODER=0 \
-DCONFIG_MULTITHREAD=0 \
" \
LIBAVIF_FLAGS="-DAVIF_CODEC_AOM_ENCODE=0"
$(CODEC_PACKAGE):
mkdir -p $(dir $@)
mkdir -p $(@D)
curl -sL $(CODEC_URL) -o $@
$(LIBAOM_PACKAGE):
mkdir -p $(dir $@)
mkdir -p $(@D)
curl -sL $(LIBAOM_URL) -o $@
$(CODEC_ENC_DIR) $(CODEC_DEC_DIR): $(CODEC_PACKAGE)
mkdir -p $@
tar xz --strip 1 -C $@ -f $(CODEC_PACKAGE)
$(CODEC_DIR)/CMakeLists.txt: $(CODEC_PACKAGE)
mkdir -p $(@D)
tar xzm --strip 1 -C $(@D) -f $(CODEC_PACKAGE)
$(LIBAOM_ENC_DIR) $(LIBAOM_DEC_DIR): $(LIBAOM_PACKAGE)
mkdir -p $@
tar xz -C $@ -f $(LIBAOM_PACKAGE)
$(LIBAOM_DIR)/CMakeLists.txt: $(LIBAOM_PACKAGE)
mkdir -p $(@D)
tar xzm -C $(@D) -f $(LIBAOM_PACKAGE)
clean:
$(RM) $(LIBAOM_PACKAGE) $(CODEC_PACKAGE) $(OUT_ENC_JS) $(OUT_ENC_WASM) $(OUT_DEC_JS) $(OUT_DEC_WASM)
$(MAKE) -C $(CODEC_ENC_BUILD_DIR) clean
$(MAKE) -C $(CODEC_DEC_BUILD_DIR) clean
$(MAKE) -C $(LIBAOM_ENC_BUILD_DIR) clean
$(MAKE) -C $(LIBAOM_DEC_BUILD_DIR) clean
$(MAKE) $(HELPER_MAKEFLAGS) BUILD_DIR=$(ENC_BUILD_DIR) OUT_JS=$(OUT_ENC_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) BUILD_DIR=$(ENC_MT_BUILD_DIR) OUT_JS=$(OUT_ENC_MT_JS) clean
$(MAKE) $(HELPER_MAKEFLAGS) BUILD_DIR=$(DEC_BUILD_DIR) OUT_JS=$(OUT_DEC_JS) clean

View File

@ -8,15 +8,10 @@ thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
val decode(std::string avifimage) {
// point raw.data and raw.size to the contents of an .avif(s)
avifROData raw = {
.data = (uint8_t*)avifimage.c_str(),
.size = avifimage.length()
};
avifImage* image = avifImageCreateEmpty();
avifDecoder* decoder = avifDecoderCreate();
avifResult decodeResult = avifDecoderRead(decoder, image, &raw);
avifResult decodeResult =
avifDecoderReadMemory(decoder, image, (uint8_t*)avifimage.c_str(), avifimage.length());
// image is an independent copy of decoded data, decoder may be destroyed here
avifDecoderDestroy(decoder);
@ -25,7 +20,8 @@ val decode(std::string avifimage) {
if (decodeResult == AVIF_RESULT_OK) {
// Convert to interleaved RGB(A)/BGR(A) using a libavif-allocated buffer.
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, image); // Defaults to AVIF_RGB_FORMAT_RGBA which is what we want.
avifRGBImageSetDefaults(&rgb,
image); // Defaults to AVIF_RGB_FORMAT_RGBA which is what we want.
rgb.depth = 8; // Does not need to match image->depth. We always want 8-bit pixels.
avifRGBImageAllocatePixels(&rgb);
@ -33,7 +29,9 @@ val decode(std::string avifimage) {
// We want to create a *copy* of the decoded data to be owned by the JavaScript side.
// For that, we perform `new Uint8Array(wasmMemBuffer, wasmPtr, wasmSize).slice()`:
result = ImageData.new_(Uint8ClampedArray.new_(typed_memory_view(rgb.rowBytes * rgb.height, rgb.pixels)), rgb.width, rgb.height);
result = ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(rgb.rowBytes * rgb.height, rgb.pixels)), rgb.width,
rgb.height);
// Now we can safely free the RGB pixels:
avifRGBImageFreePixels(&rgb);

View File

@ -1,6 +1,7 @@
interface AVIFModule extends EmscriptenWasm.Module {
export interface AVIFModule extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
export default function(opts: EmscriptenWasm.ModuleOpts): AVIFModule;
declare var moduleFactory: EmscriptenWasm.ModuleFactory<AVIFModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,4 +1,5 @@
#include <emscripten/bind.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
#include "avif/avif.h"
@ -50,13 +51,10 @@ val encode(std::string buffer, int width, int height, AvifOptions options) {
avifImage* image = avifImageCreate(width, height, depth, format);
if (
options.maxQuantizer == AVIF_QUANTIZER_LOSSLESS &&
options.minQuantizer == AVIF_QUANTIZER_LOSSLESS &&
options.minQuantizerAlpha == AVIF_QUANTIZER_LOSSLESS &&
options.maxQuantizerAlpha == AVIF_QUANTIZER_LOSSLESS &&
format == AVIF_PIXEL_FORMAT_YUV444
) {
if (options.maxQuantizer == AVIF_QUANTIZER_LOSSLESS &&
options.minQuantizer == AVIF_QUANTIZER_LOSSLESS &&
options.minQuantizerAlpha == AVIF_QUANTIZER_LOSSLESS &&
options.maxQuantizerAlpha == AVIF_QUANTIZER_LOSSLESS && format == AVIF_PIXEL_FORMAT_YUV444) {
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
} else {
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709;
@ -71,7 +69,7 @@ val encode(std::string buffer, int width, int height, AvifOptions options) {
avifImageRGBToYUV(image, &srcRGB);
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 1;
encoder->maxThreads = emscripten_num_logical_cores();
encoder->minQuantizer = options.minQuantizer;
encoder->maxQuantizer = options.maxQuantizer;
encoder->minQuantizerAlpha = options.minQuantizerAlpha;

View File

@ -1,7 +1,23 @@
import { EncodeOptions } from '../../../src/codecs/avif/encoder-meta';
interface AVIFModule extends EmscriptenWasm.Module {
encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array | null;
export interface EncodeOptions {
minQuantizer: number;
maxQuantizer: number;
minQuantizerAlpha: number;
maxQuantizerAlpha: number;
tileRowsLog2: number;
tileColsLog2: number;
speed: number;
subsample: number;
}
export default function(opts: EmscriptenWasm.ModuleOpts): AVIFModule;
export interface AVIFModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<AVIFModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

Binary file not shown.

2
codecs/avif/enc/avif_enc_mt.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export * from './avif_enc';
export { default } from './avif_enc';

3268
codecs/avif/enc/avif_enc_mt.js generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

103
codecs/avif/enc/avif_enc_mt.worker.js generated Normal file
View File

@ -0,0 +1,103 @@
var threadInfoStruct = 0;
var selfThreadId = 0;
var parentThreadId = 0;
var initializedJS = false;
var Module = {};
function threadPrintErr() {
var text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
}
function threadAlert() {
var text = Array.prototype.slice.call(arguments).join(' ');
postMessage({ cmd: 'alert', text: text, threadId: selfThreadId });
}
var err = threadPrintErr;
this.alert = threadAlert;
Module['instantiateWasm'] = function (info, receiveInstance) {
var instance = new WebAssembly.Instance(Module['wasmModule'], info);
Module['wasmModule'] = null;
receiveInstance(instance);
return instance.exports;
};
this.onmessage = function (e) {
try {
if (e.data.cmd === 'load') {
Module['wasmModule'] = e.data.wasmModule;
Module['wasmMemory'] = e.data.wasmMemory;
Module['buffer'] = Module['wasmMemory'].buffer;
Module['ENVIRONMENT_IS_PTHREAD'] = true;
import(e.data.urlOrBlob)
.then(function (avif_enc_mt) {
return avif_enc_mt.default(Module);
})
.then(function (instance) {
Module = instance;
postMessage({ cmd: 'loaded' });
});
} else if (e.data.cmd === 'objectTransfer') {
Module['PThread'].receiveObjectTransfer(e.data);
} else if (e.data.cmd === 'run') {
Module['__performance_now_clock_drift'] = performance.now() - e.data.time;
threadInfoStruct = e.data.threadInfoStruct;
Module['registerPthreadPtr'](
threadInfoStruct,
/*isMainBrowserThread=*/ 0,
/*isMainRuntimeThread=*/ 0,
);
selfThreadId = e.data.selfThreadId;
parentThreadId = e.data.parentThreadId;
var max = e.data.stackBase;
var top = e.data.stackBase + e.data.stackSize;
Module['establishStackSpace'](top, max);
Module['_emscripten_tls_init']();
Module['PThread'].receiveObjectTransfer(e.data);
Module['PThread'].setThreadStatus(Module['_pthread_self'](), 1);
if (!initializedJS) {
Module['___embind_register_native_and_builtin_types']();
initializedJS = true;
}
try {
var result = Module['dynCall']('ii', e.data.start_routine, [
e.data.arg,
]);
if (!Module['getNoExitRuntime']()) Module['PThread'].threadExit(result);
} catch (ex) {
if (ex === 'Canceled!') {
Module['PThread'].threadCancel();
} else if (ex != 'unwind') {
Atomics.store(
Module['HEAPU32'],
(threadInfoStruct + 4) >> /*C_STRUCTS.pthread.threadExitCode*/ 2,
ex instanceof Module['ExitStatus'] ? ex.status : -2,
);
/*A custom entry specific to Emscripten denoting that the thread crashed.*/ Atomics.store(
Module['HEAPU32'],
(threadInfoStruct + 0) >> /*C_STRUCTS.pthread.threadStatus*/ 2,
1,
);
Module['_emscripten_futex_wake'](
threadInfoStruct + 0,
/*C_STRUCTS.pthread.threadStatus*/ 2147483647,
);
if (!(ex instanceof Module['ExitStatus'])) throw ex;
}
}
} else if (e.data.cmd === 'cancel') {
if (threadInfoStruct) {
Module['PThread'].threadCancel();
}
} else if (e.data.target === 'setimmediate') {
} else if (e.data.cmd === 'processThreadQueue') {
if (threadInfoStruct) {
Module['_emscripten_current_thread_process_queued_calls']();
}
} else {
err('worker.js received unknown command ' + e.data.cmd);
err(e.data);
}
} catch (ex) {
err('worker.js onmessage() captured an uncaught exception: ' + ex);
if (ex && ex.stack) err(ex.stack);
throw ex;
}
};

View File

@ -0,0 +1,75 @@
# This is a helper Makefile for building LibAVIF + LibAOM with given params.
#
# Params that must be supplied by the caller:
# $(CODEC_DIR)
# $(LIBAOM_DIR)
# $(BUILD_DIR)
# $(OUT_JS)
# $(OUT_CPP)
# $(LIBAOM_FLAGS)
# $(LIBAVIF_FLAGS)
CODEC_BUILD_DIR := $(BUILD_DIR)/libavif
CODEC_OUT := $(CODEC_BUILD_DIR)/libavif.a
LIBAOM_BUILD_DIR := $(BUILD_DIR)/libaom
LIBAOM_OUT := $(LIBAOM_BUILD_DIR)/libaom.a
OUT_WASM = $(OUT_JS:.js=.wasm)
OUT_WORKER=$(OUT_JS:.js=.worker.js)
.PHONY: all clean
all: $(OUT_JS)
$(OUT_JS): $(OUT_CPP) $(LIBAOM_OUT) $(CODEC_OUT)
$(CXX) \
-I $(CODEC_DIR)/include \
$(CXXFLAGS) \
$(LDFLAGS) \
$(OUT_FLAGS) \
--bind \
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-s EXPORT_NAME="$(basename $(@F))" \
-o $@ \
$+
$(CODEC_OUT): $(CODEC_DIR)/CMakeLists.txt $(LIBAOM_OUT)
emcmake cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=0 \
-DAVIF_CODEC_AOM=1 \
-DAOM_LIBRARY=$(LIBAOM_OUT) \
-DAOM_INCLUDE_DIR=$(LIBAOM_DIR) \
$(LIBAVIF_FLAGS) \
-B $(CODEC_BUILD_DIR) \
$(CODEC_DIR) && \
$(MAKE) -C $(CODEC_BUILD_DIR)
$(LIBAOM_OUT): $(LIBAOM_DIR)/CMakeLists.txt
emcmake cmake \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_CCACHE=0 \
-DAOM_TARGET_CPU=generic \
-DENABLE_DOCS=0 \
-DENABLE_TESTS=0 \
-DENABLE_EXAMPLES=0 \
-DENABLE_TOOLS=0 \
-DCONFIG_ACCOUNTING=1 \
-DCONFIG_INSPECTION=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 \
-DCONFIG_WEBM_IO=0 \
$(LIBAOM_FLAGS) \
-B $(LIBAOM_BUILD_DIR) \
$(LIBAOM_DIR) && \
$(MAKE) -C $(LIBAOM_BUILD_DIR)
clean:
$(RM) $(OUT_JS) $(OUT_WASM) $(OUT_WORKER)
$(MAKE) -C $(CODEC_BUILD_DIR) clean
$(MAKE) -C $(LIBAOM_BUILD_DIR) clean

View File

@ -1,4 +1,10 @@
set -e
docker build -t squoosh-rust - < ../rust.Dockerfile
docker run --rm -v $PWD:/src squoosh-rust "$@"
if [ ! -z "$RUST_IMG" ]
then
# Get part after ":" (https://stackoverflow.com/a/15149278/439965).
IMG_SUFFIX=-${RUST_IMG#*:}
fi
IMG_NAME=squoosh-rust$IMG_SUFFIX
docker build -t $IMG_NAME --build-arg RUST_IMG - < ../rust.Dockerfile
docker run --rm -v $PWD:/src $IMG_NAME "$@"

View File

@ -1,8 +1,8 @@
FROM emscripten/emsdk:1.40.0
FROM emscripten/emsdk:2.0.8
RUN apt-get update && apt-get install -qqy autoconf libtool pkg-config
ENV CFLAGS "-Os -flto"
ENV CFLAGS "-O3 -flto"
ENV CXXFLAGS "${CFLAGS} -std=c++17"
ENV LDFLAGS "${CFLAGS}"
ENV LDFLAGS "${CFLAGS} -s PTHREAD_POOL_SIZE=navigator.hardwareConcurrency"
# Build and cache standard libraries with these flags
RUN emcc ${CXXFLAGS} --bind -xc++ /dev/null -o /dev/null
WORKDIR /src

View File

@ -1,11 +0,0 @@
# This is intentionally an old version of Rust. Newer versions
# generate _significantly_ bigger Wasm binaries.
FROM rust:1.37
RUN rustup target add wasm32-unknown-unknown
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
RUN mkdir /opt/binaryen && \
curl -L https://github.com/WebAssembly/binaryen/releases/download/1.38.32/binaryen-1.38.32-x86-linux.tar.gz | tar -xzf - -C /opt/binaryen --strip 1
ENV PATH="/opt/binaryen:${PATH}"
WORKDIR /src

View File

@ -1,21 +0,0 @@
#!/bin/bash
set -e
echo "============================================="
echo "Compiling wasm"
echo "============================================="
(
wasm-pack build -- --verbose --locked
rm pkg/.gitignore
)
echo "============================================="
echo "Compiling wasm done"
echo "============================================="
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Did you update your docker image?"
echo "Run \`docker pull ubuntu\`"
echo "Run \`docker pull rust\`"
echo "Run \`docker build -t squoosh-hqx .\`"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

View File

@ -1,7 +1,6 @@
{
"name": "hqx",
"scripts": {
"build:image": "docker build -t squoosh-hqx - < Dockerfile",
"build": "docker run --rm -v $(pwd):/src squoosh-hqx ./build.sh"
"build": "RUST_IMG=rust:1.40 ../build-rust.sh"
}
}

5
codecs/hqx/pkg/README.md Normal file
View File

@ -0,0 +1,5 @@
# HQX
- Source: <https://github.com/CryZe/wasmboy-rs>
- Version: v0.1.2
- License: Apache 2.0

52
codecs/hqx/pkg/squooshhqx.d.ts generated vendored
View File

@ -1,10 +1,48 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(input_image: Uint32Array, input_width: number, input_height: number, factor: number): Uint32Array;
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(
input_image: Uint32Array,
input_width: number,
input_height: number,
factor: number,
): Uint32Array;
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly resize: (
a: number,
b: number,
c: number,
d: number,
e: number,
f: number,
) => void;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_free: (a: number, b: number) => void;
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init(
module_or_path?: InitInput | Promise<InitInput>,
): Promise<InitOutput>;

View File

@ -1,2 +1,107 @@
import * as wasm from "./squooshhqx_bg.wasm";
export * from "./squooshhqx_bg.js";
let wasm;
let cachegetUint32Memory0 = null;
function getUint32Memory0() {
if (
cachegetUint32Memory0 === null ||
cachegetUint32Memory0.buffer !== wasm.memory.buffer
) {
cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer);
}
return cachegetUint32Memory0;
}
let WASM_VECTOR_LEN = 0;
function passArray32ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 4);
getUint32Memory0().set(arg, ptr / 4);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (
cachegetInt32Memory0 === null ||
cachegetInt32Memory0.buffer !== wasm.memory.buffer
) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU32FromWasm0(ptr, len) {
return getUint32Memory0().subarray(ptr / 4, ptr / 4 + len);
}
/**
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(input_image, input_width, input_height, factor) {
var ptr0 = passArray32ToWasm0(input_image, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.resize(8, ptr0, len0, input_width, input_height, factor);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
var v1 = getArrayU32FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 4);
return v1;
}
async function load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn(
'`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n',
e,
);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
if (
typeof input === 'string' ||
(typeof Request === 'function' && input instanceof Request) ||
(typeof URL === 'function' && input instanceof URL)
) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
return wasm;
}
export default init;

View File

@ -1,48 +0,0 @@
import * as wasm from './squooshhqx_bg.wasm';
let cachegetUint32Memory0 = null;
function getUint32Memory0() {
if (cachegetUint32Memory0 === null || cachegetUint32Memory0.buffer !== wasm.memory.buffer) {
cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer);
}
return cachegetUint32Memory0;
}
let WASM_VECTOR_LEN = 0;
function passArray32ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 4);
getUint32Memory0().set(arg, ptr / 4);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU32FromWasm0(ptr, len) {
return getUint32Memory0().subarray(ptr / 4, ptr / 4 + len);
}
/**
* @param {Uint32Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} factor
* @returns {Uint32Array}
*/
export function resize(input_image, input_width, input_height, factor) {
var ptr0 = passArray32ToWasm0(input_image, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.resize(8, ptr0, len0, input_width, input_height, factor);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
var v1 = getArrayU32FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 4);
return v1;
}

Binary file not shown.

View File

@ -18,7 +18,9 @@ all: $(OUT_JS)
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s 'EXPORT_NAME="$(basename $(@F))"' \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-o $@ \
$+

View File

@ -1,18 +1,17 @@
<!doctype html>
<!DOCTYPE html>
<style>
canvas {
image-rendering: pixelated;
}
</style>
<script src='imagequant.js'></script>
<script>
const Module = imagequant();
<script type="module">
import imagequant from './imagequant.js';
async function loadImage(src) {
// Load image
const img = document.createElement('img');
img.src = src;
await new Promise(resolve => img.onload = resolve);
await new Promise((resolve) => (img.onload = resolve));
// Make canvas same size as image
const canvas = document.createElement('canvas');
[canvas.width, canvas.height] = [img.width, img.height];
@ -22,19 +21,32 @@
return ctx.getImageData(0, 0, img.width, img.height);
}
Module.onRuntimeInitialized = async _ => {
console.log('Version:', Module.version().toString(16));
async function main() {
const module = await imagequant();
console.log('Version:', module.version().toString(16));
const image = await loadImage('../example.png');
// const rawImage = Module.quantize(image.data, image.width, image.height, 256, 1.0);
const rawImage = Module.zx_quantize(image.data, image.width, image.height, 1.0);
const rawImage = module.quantize(
image.data,
image.width,
image.height,
256,
1.0,
);
console.log('done');
const imageData = new ImageData(new Uint8ClampedArray(rawImage.buffer), image.width, image.height);
const imageData = new ImageData(
new Uint8ClampedArray(rawImage.buffer),
image.width,
image.height,
);
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
document.body.appendChild(canvas);
};
}
main();
</script>

View File

@ -1,6 +1,19 @@
interface QuantizerModule extends EmscriptenWasm.Module {
quantize(data: BufferSource, width: number, height: number, numColors: number, dither: number): Uint8ClampedArray;
zx_quantize(data: BufferSource, width: number, height: number, dither: number): Uint8ClampedArray;
export interface QuantizerModule extends EmscriptenWasm.Module {
quantize(
data: BufferSource,
width: number,
height: number,
numColors: number,
dither: number,
): Uint8ClampedArray;
zx_quantize(
data: BufferSource,
width: number,
height: number,
dither: number,
): Uint8ClampedArray;
}
export default function(opts: EmscriptenWasm.ModuleOpts): QuantizerModule;
declare var moduleFactory: EmscriptenWasm.ModuleFactory<QuantizerModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

BIN
codecs/imagequant/imagequant.wasm Normal file → Executable file

Binary file not shown.

59
codecs/jxl/Makefile Normal file
View File

@ -0,0 +1,59 @@
CODEC_URL = https://gitlab.com/wg1/jpeg-xl.git
CODEC_VERSION = v0.1
CODEC_DIR = node_modules/jxl
CODEC_BUILD_DIR := $(CODEC_DIR)/build
CODEC_OUT := $(CODEC_BUILD_DIR)/lib/libjxl.a
OUT_JS = enc/jxl_enc.js dec/jxl_dec.js
OUT_WASM = $(OUT_JS:.js=.wasm)
.PHONY: all clean
all: $(OUT_JS)
%.js: %.cpp $(LIBAOM_OUT) $(CODEC_OUT)
$(CXX) \
-I $(CODEC_DIR) \
-I $(CODEC_DIR)/lib \
-I $(CODEC_DIR)/lib/include \
-I $(CODEC_BUILD_DIR)/lib/include \
-I $(CODEC_DIR)/third_party/highway \
-I $(CODEC_DIR)/third_party/skcms \
-I $(CODEC_DIR)/third_party/brunsli \
-I $(CODEC_DIR)/third_party/brunsli/c/include \
${CXXFLAGS} \
${LDFLAGS} \
--bind \
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-s EXPORT_NAME="$(basename $(@F))" \
-o $@ \
$+ \
$(CODEC_BUILD_DIR)/artifacts/libbrunslienc-static.bc \
$(CODEC_BUILD_DIR)/artifacts/libbrunslicommon-static.bc \
$(CODEC_BUILD_DIR)/artifacts/libbrunslidec-static.bc \
$(CODEC_BUILD_DIR)/third_party/brotli/libbrotlidec-static.a \
$(CODEC_BUILD_DIR)/third_party/brotli/libbrotlienc-static.a \
$(CODEC_BUILD_DIR)/third_party/brotli/libbrotlicommon-static.a \
$(CODEC_BUILD_DIR)/third_party/libskcms.a \
$(CODEC_BUILD_DIR)/third_party/highway/libhwy.a
$(CODEC_OUT): $(CODEC_DIR)/CMakeLists.txt
mkdir -p $(CODEC_BUILD_DIR)
cd $(CODEC_BUILD_DIR) && \
emcmake cmake ../ && \
$(MAKE) jxl-static
$(CODEC_DIR)/CMakeLists.txt: $(CODEC_DIR)
$(CODEC_DIR):
mkdir -p $@
git clone $(CODEC_URL) --recursive -j`nproc` --depth 1 --branch $(CODEC_VERSION) $@
clean:
$(RM) $(OUT_JS) $(OUT_WASM)
$(MAKE) -C $(CODEC_BUILD_DIR) clean

View File

@ -0,0 +1,93 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <jxl/decode.h>
#include "lib/jxl/color_encoding_internal.h"
#include "skcms.h"
using namespace emscripten;
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
// R, G, B, A
#define COMPONENTS_PER_PIXEL 4
#ifndef JXL_DEBUG_ON_ALL_ERROR
#define JXL_DEBUG_ON_ALL_ERROR 0
#endif
#if JXL_DEBUG_ON_ALL_ERROR
#define EXPECT_TRUE(a) \
if (!(a)) { \
fprintf(stderr, "Assertion failure (%d): %s\n", __LINE__, #a); \
return val::null(); \
}
#define EXPECT_EQ(a, b) \
{ \
int a_ = a; \
int b_ = b; \
if (a_ != b_) { \
fprintf(stderr, "Assertion failure (%d): %s (%d) != %s (%d)\n", __LINE__, #a, a_, #b, b_); \
return val::null(); \
} \
}
#else
#define EXPECT_TRUE(a) \
if (!(a)) { \
return val::null(); \
}
#define EXPECT_EQ(a, b) EXPECT_TRUE((a) == (b));
#endif
val decode(std::string data) {
std::unique_ptr<JxlDecoder,
std::integral_constant<decltype(&JxlDecoderDestroy), JxlDecoderDestroy>>
dec(JxlDecoderCreate(nullptr));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(
dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE));
auto next_in = (const uint8_t*)data.c_str();
auto avail_in = data.size();
EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec.get(), &next_in, &avail_in));
JxlBasicInfo info;
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec.get(), &info));
size_t pixel_count = info.xsize * info.ysize;
size_t component_count = pixel_count * COMPONENTS_PER_PIXEL;
EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec.get(), &next_in, &avail_in));
static const JxlPixelFormat format = {COMPONENTS_PER_PIXEL, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
size_t icc_size;
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetICCProfileSize(dec.get(), &format,
JXL_COLOR_PROFILE_TARGET_DATA, &icc_size));
std::vector<uint8_t> icc_profile(icc_size);
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderGetColorAsICCProfile(dec.get(), &format, JXL_COLOR_PROFILE_TARGET_DATA,
icc_profile.data(), icc_profile.size()));
auto float_pixels = std::make_unique<float[]>(component_count);
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(dec.get(), &format, float_pixels.get(),
component_count * sizeof(float)));
EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec.get(), &next_in, &avail_in));
auto byte_pixels = std::make_unique<uint8_t[]>(component_count);
// Convert to sRGB.
skcms_ICCProfile jxl_profile;
EXPECT_TRUE(skcms_Parse(icc_profile.data(), icc_profile.size(), &jxl_profile));
EXPECT_TRUE(skcms_Transform(
float_pixels.get(), skcms_PixelFormat_RGBA_ffff,
info.alpha_premultiplied ? skcms_AlphaFormat_PremulAsEncoded : skcms_AlphaFormat_Unpremul,
&jxl_profile, byte_pixels.get(), skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul,
skcms_sRGB_profile(), pixel_count));
return ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(component_count, byte_pixels.get())), info.xsize,
info.ysize);
}
EMSCRIPTEN_BINDINGS(my_module) {
function("decode", &decode);
}

7
codecs/jxl/dec/jxl_dec.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
export interface JXLModule extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<JXLModule>;
export default moduleFactory;

1195
codecs/jxl/dec/jxl_dec.js generated Normal file

File diff suppressed because it is too large Load Diff

BIN
codecs/jxl/dec/jxl_dec.wasm Executable file

Binary file not shown.

124
codecs/jxl/enc/jxl_enc.cpp Normal file
View File

@ -0,0 +1,124 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include "lib/jxl/enc_file.h"
#include "lib/jxl/external_image.h"
using namespace emscripten;
thread_local const val Uint8Array = val::global("Uint8Array");
struct JXLOptions {
// 1 = slowest
// 7 = fastest
int speed;
float quality;
bool progressive;
int epf;
int nearLossless;
bool lossyPalette;
};
val encode(std::string image, int width, int height, JXLOptions options) {
jxl::CompressParams cparams;
jxl::PassesEncoderState passes_enc_state;
jxl::CodecInOut io;
jxl::PaddedBytes bytes;
jxl::ImageBundle* main = &io.Main();
cparams.epf = options.epf;
cparams.speed_tier = static_cast<jxl::SpeedTier>(options.speed);
cparams.near_lossless = options.nearLossless;
if (options.lossyPalette) {
cparams.lossy_palette = true;
cparams.palette_colors = 0;
cparams.options.predictor = jxl::Predictor::Zero;
}
// Reduce memory usage of tree learning for lossless data.
// TODO(veluca93): this is a mitigation for excessive memory usage in the JXL encoder.
float megapixels = width * height * 0.000001;
if (megapixels > 8) {
cparams.options.nb_repeats = 0.1;
} else if (megapixels > 4) {
cparams.options.nb_repeats = 0.3;
} else {
// default is OK.
}
float quality = options.quality;
// Quality settings roughly match libjpeg qualities.
if (quality < 7 || quality == 100) {
cparams.modular_mode = true;
// Internal modular quality to roughly match VarDCT size.
cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 3.0f, 100.0f);
} else {
cparams.modular_mode = false;
if (quality >= 30) {
cparams.butteraugli_distance = 0.1 + (100 - quality) * 0.09;
} else {
cparams.butteraugli_distance = 6.4 + pow(2.5, (30 - quality) / 5.0f) / 6.25f;
}
}
if (options.progressive) {
cparams.qprogressive_mode = true;
cparams.responsive = 1;
if (!cparams.modular_mode) {
cparams.progressive_dc = 1;
}
}
if (cparams.modular_mode) {
if (cparams.quality_pair.first != 100 || cparams.quality_pair.second != 100) {
cparams.color_transform = jxl::ColorTransform::kXYB;
} else {
cparams.color_transform = jxl::ColorTransform::kNone;
}
}
if (cparams.near_lossless) {
// Near-lossless assumes -R 0
cparams.responsive = 0;
cparams.modular_mode = true;
}
io.metadata.m.SetAlphaBits(8);
if (!io.metadata.size.Set(width, height)) {
return val::null();
}
uint8_t* inBuffer = (uint8_t*)image.c_str();
auto result = jxl::ConvertImage(
jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width,
height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_alpha=*/8, /*bits_per_sample=*/8,
/*big_endian=*/false, /*flipped_y=*/false, /*pool=*/nullptr, main);
if (!result) {
return val::null();
}
auto js_result = val::null();
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes)) {
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
}
return js_result;
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<JXLOptions>("JXLOptions")
.field("speed", &JXLOptions::speed)
.field("quality", &JXLOptions::quality)
.field("progressive", &JXLOptions::progressive)
.field("nearLossless", &JXLOptions::nearLossless)
.field("lossyPalette", &JXLOptions::lossyPalette)
.field("epf", &JXLOptions::epf);
function("encode", &encode);
}

21
codecs/jxl/enc/jxl_enc.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
export interface EncodeOptions {
speed: number;
quality: number;
progressive: boolean;
epf: number;
nearLossless: number;
lossyPalette: boolean;
}
export interface JXLModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<JXLModule>;
export default moduleFactory;

1606
codecs/jxl/enc/jxl_enc.js generated Normal file

File diff suppressed because it is too large Load Diff

BIN
codecs/jxl/enc/jxl_enc.wasm Executable file

Binary file not shown.

6
codecs/jxl/package.json Normal file
View File

@ -0,0 +1,6 @@
{
"name": "jxl",
"scripts": {
"build": "../build-cpp.sh"
}
}

View File

@ -14,11 +14,13 @@ all: $(OUT_JS)
-I $(CODEC_DIR) \
${CXXFLAGS} \
${LDFLAGS} \
--bind \
--closure 1 \
--bind \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s 'EXPORT_NAME="$(basename $(@F))"' \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-o $@ \
$+

View File

@ -1,13 +1,12 @@
<!doctype html>
<script src='mozjpeg_enc.js'></script>
<script>
const module = mozjpeg_enc();
<!DOCTYPE html>
<script type="module">
import mozjpeg_enc from './mozjpeg_enc.js';
async function loadImage(src) {
// Load image
const img = document.createElement('img');
img.src = src;
await new Promise(resolve => img.onload = resolve);
await new Promise((resolve) => (img.onload = resolve));
// Make canvas same size as image
const canvas = document.createElement('canvas');
[canvas.width, canvas.height] = [img.width, img.height];
@ -17,7 +16,9 @@
return ctx.getImageData(0, 0, img.width, img.height);
}
module.onRuntimeInitialized = async _ => {
async function main() {
const module = await mozjpeg_enc();
console.log('Version:', module.version().toString(16));
const image = await loadImage('../example.png');
const result = module.encode(image.data, image.width, image.height, {
@ -39,10 +40,12 @@
chroma_quality: 75,
});
const blob = new Blob([result], {type: 'image/jpeg'});
const blob = new Blob([result], { type: 'image/jpeg' });
const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);
};
}
main();
</script>

View File

@ -1,7 +1,37 @@
import { EncodeOptions } from '../../src/codecs/mozjpeg/encoder-meta';
interface MozJPEGModule extends EmscriptenWasm.Module {
encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array;
export const enum MozJpegColorSpace {
GRAYSCALE = 1,
RGB,
YCbCr,
}
export default function(opts: EmscriptenWasm.ModuleOpts): MozJPEGModule;
export interface EncodeOptions {
quality: number;
baseline: boolean;
arithmetic: boolean;
progressive: boolean;
optimize_coding: boolean;
smoothing: number;
color_space: MozJpegColorSpace;
quant_table: number;
trellis_multipass: boolean;
trellis_opt_zero: boolean;
trellis_opt_table: boolean;
trellis_loops: number;
auto_subsample: boolean;
chroma_subsample: number;
separate_chroma_quality: boolean;
chroma_quality: number;
}
export interface MozJPEGModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<MozJPEGModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

BIN
codecs/mozjpeg_enc/mozjpeg_enc.wasm Normal file → Executable file

Binary file not shown.

299
codecs/oxipng/Cargo.lock generated
View File

@ -1,16 +1,22 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "adler32"
version = "1.1.0"
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "autocfg"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bit-vec"
@ -38,9 +44,9 @@ checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
[[package]]
name = "bytemuck"
version = "1.2.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431"
checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac"
[[package]]
name = "byteorder"
@ -50,9 +56,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cc"
version = "1.0.58"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
[[package]]
name = "cfg-if"
@ -60,6 +66,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloudflare-zlib"
version = "0.2.5"
@ -78,6 +90,18 @@ dependencies = [
"cc",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "const_fn"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
[[package]]
name = "crc"
version = "1.8.1"
@ -89,58 +113,57 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"lazy_static",
]
@ -156,27 +179,34 @@ dependencies = [
[[package]]
name = "either"
version = "1.5.3"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "hermit-abi"
version = "0.1.15"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"libc",
]
[[package]]
name = "image"
version = "0.23.7"
version = "0.23.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2397fc43bd5648b7117aabb3c5e62d0e62c194826ec77b0b4d0c41e62744635"
checksum = "b4f0a8345b33b082aedec2f4d7d4a926b845cee184cbe78b703413066564431b"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-iter",
"num-rational",
"num-traits",
@ -185,11 +215,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.4.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"hashbrown",
"rayon",
]
@ -210,39 +241,42 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.72"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "libdeflater"
version = "0.2.0"
name = "libdeflate-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66dca08b13369865b2f6dca1dd05f833985cbe6c12a676b04d55f78b85e80246"
checksum = "2f5b1582a0ebf8c55a46166c04d7c66f6bb17add3a6cbf69a082ac2219f31671"
dependencies = [
"cc",
]
[[package]]
name = "log"
version = "0.4.8"
name = "libdeflater"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
checksum = "93edd93a53970951da84ef733a8b6e30189a8f8a9e19610f69e4cc5bb1f4d654"
dependencies = [
"cfg-if",
"libdeflate-sys",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "memoffset"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg",
]
@ -257,10 +291,20 @@ dependencies = [
]
[[package]]
name = "num-integer"
version = "0.1.43"
name = "miniz_oxide"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
@ -268,9 +312,9 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.41"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg",
"num-integer",
@ -279,9 +323,9 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
@ -290,9 +334,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.12"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
@ -308,42 +352,59 @@ dependencies = [
]
[[package]]
name = "oxipng"
version = "3.0.0"
name = "once_cell"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fd695858078338d73862ff3755f820eff0bf4f3304e4b52f22aba53463183a"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "oxipng"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fefb26bde273c3db896a313151301a69e698a7495ee577fe2168ed7065c29c4"
dependencies = [
"bit-vec",
"byteorder",
"cloudflare-zlib",
"crc",
"crossbeam-channel",
"image",
"indexmap",
"itertools",
"libdeflater",
"log",
"miniz_oxide",
"miniz_oxide 0.4.3",
"rayon",
"rgb",
"zopfli",
"rustc_version",
]
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "png"
version = "0.16.6"
version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c150bf7479fafe3dd8740dbe48cc33b2a3efb7b0fe3483aced8bbc39f6d0238d"
checksum = "dfe7f9f1c730833200b134370e1d5098964231af8450bce9b78ee3ab5278b970"
dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide",
"miniz_oxide 0.3.7",
]
[[package]]
name = "proc-macro2"
version = "1.0.18"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
@ -359,9 +420,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.3.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
dependencies = [
"autocfg",
"crossbeam-deque",
@ -371,12 +432,12 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.7.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"num_cpus",
@ -384,33 +445,63 @@ dependencies = [
[[package]]
name = "rgb"
version = "0.8.20"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ef54b45ae131327a88597e2463fee4098ad6c88ba7b6af4b3987db8aad4098"
checksum = "287f3c3f8236abb92d8b7e36797f19159df4b58f0a658cc3fb6dd3004b1f3bd3"
dependencies = [
"bytemuck",
]
[[package]]
name = "rustc_version"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65c94201b44764d6d1f7e37c15a8289ed55e546c1762c7f1d57f616966e0c181"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428"
dependencies = [
"pest",
]
[[package]]
name = "squoosh-oxipng"
version = "0.1.0"
dependencies = [
"crossbeam-channel",
"log",
"once_cell",
"oxipng",
"rayon",
"wasm-bindgen",
]
[[package]]
name = "syn"
version = "1.0.34"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
dependencies = [
"proc-macro2",
"quote",
@ -418,10 +509,10 @@ dependencies = [
]
[[package]]
name = "typed-arena"
version = "1.7.0"
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-xid"
@ -431,19 +522,19 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "wasm-bindgen"
version = "0.2.64"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.64"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df"
checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
dependencies = [
"bumpalo",
"lazy_static",
@ -456,9 +547,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.64"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8"
checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -466,9 +557,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.64"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75"
checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
dependencies = [
"proc-macro2",
"quote",
@ -479,18 +570,6 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.64"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae"
[[package]]
name = "zopfli"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4079b79464426ade2a1b0177fb0ce8396ba6b4084267407e333573c666073964"
dependencies = [
"adler32",
"byteorder",
"crc",
"typed-arena",
]
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"

View File

@ -5,14 +5,23 @@ authors = ["Ingvar Stepanyan <me@rreverser.com>"]
edition = "2018"
publish = false
[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-O", "--no-validation"]
[lib]
crate-type = ["cdylib"]
[dependencies]
oxipng = { version = "3.0.0", default-features = false }
wasm-bindgen = "0.2.64"
log = { version = "0.4", features = ["release_max_level_off"] }
oxipng = { version = "4.0.0", default-features = false, features = ["libdeflater"] }
wasm-bindgen = "0.2.68"
log = { version = "0.4.11", features = ["release_max_level_off"] }
rayon = { version = "1.5.0", optional = true }
once_cell = { version = "1.5.2", optional = true }
crossbeam-channel = { version = "0.5.0", optional = true }
[profile.release]
lto = true
opt-level = "s"
[features]
parallel = ["oxipng/parallel", "rayon", "crossbeam-channel", "once_cell"]

View File

@ -1,12 +0,0 @@
FROM rust
RUN rustup target add wasm32-unknown-unknown
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
RUN mkdir /opt/wabt && \
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.17/wabt-1.0.17-ubuntu.tar.gz | tar -xzf - -C /opt/wabt --strip 1
RUN mkdir /opt/wasi-sdk && \
curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-11/wasi-sdk-11.0-linux.tar.gz | tar -xzf - -C /opt/wasi-sdk --strip 1
ENV PATH="/opt/wabt/bin:/opt/wasi-sdk/bin:${PATH}"
WORKDIR /src

10
codecs/oxipng/build.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
rm -rf pkg,{-parallel}
wasm-pack build -t web
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory' wasm-pack build -t web -d pkg-parallel -- -Z build-std=panic_abort,std --features=parallel
# Workaround https://github.com/rustwasm/wasm-bindgen/issues/2133:
sed -i "s|maybe_memory:|maybe_memory?:|" pkg-parallel/squoosh_oxipng.d.ts
rm pkg{,-parallel}/.gitignore

View File

@ -1,6 +1,6 @@
{
"name": "oxipng",
"scripts": {
"build": "../build-rust.sh"
"build": "RUST_IMG=rustlang/rust@sha256:744aeea5a38f95aa7a96ec37269a65f0c6197a1cdd87d6534e12bb869141d807 ../build-rust.sh ./build.sh"
}
}

View File

@ -0,0 +1,5 @@
# OxiPNG
- Source: <https://github.com/shssoichiro/oxipng>
- Version: v3.0.0
- License: MIT

View File

@ -0,0 +1,15 @@
{
"name": "squoosh-oxipng",
"collaborators": [
"Ingvar Stepanyan <me@rreverser.com>"
],
"version": "0.1.0",
"files": [
"squoosh_oxipng_bg.wasm",
"squoosh_oxipng.js",
"squoosh_oxipng.d.ts"
],
"module": "squoosh_oxipng.js",
"types": "squoosh_oxipng.d.ts",
"sideEffects": false
}

46
codecs/oxipng/pkg-parallel/squoosh_oxipng.d.ts generated vendored Normal file
View File

@ -0,0 +1,46 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data: Uint8Array, level: number): Uint8Array;
/**
* @param {number} num
* @returns {any}
*/
export function worker_initializer(num: number): any;
/**
*/
export function start_main_thread(): void;
/**
*/
export function start_worker_thread(): void;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly optimise: (a: number, b: number, c: number, d: number) => void;
readonly malloc: (a: number) => number;
readonly free: (a: number) => void;
readonly worker_initializer: (a: number) => number;
readonly start_main_thread: () => void;
readonly start_worker_thread: () => void;
readonly __wbindgen_export_0: WebAssembly.Memory;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_free: (a: number, b: number) => void;
readonly __wbindgen_start: () => void;
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
* @param {WebAssembly.Memory} maybe_memory
*
* @returns {Promise<InitOutput>}
*/
export default function init (module_or_path?: InitInput | Promise<InitInput>, maybe_memory?: WebAssembly.Memory): Promise<InitOutput>;

View File

@ -0,0 +1,180 @@
let wasm;
let memory;
const heap = new Array(32).fill(undefined);
heap.push(undefined, null, true, false);
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.__wbindgen_export_0.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().slice(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.__wbindgen_export_0.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.__wbindgen_export_0.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data, level) {
try {
const retptr = wasm.__wbindgen_export_1.value - 16;
wasm.__wbindgen_export_1.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_1.value += 16;
}
}
function getObject(idx) { return heap[idx]; }
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
/**
* @param {number} num
* @returns {any}
*/
export function worker_initializer(num) {
var ret = wasm.worker_initializer(num);
return takeObject(ret);
}
/**
*/
export function start_main_thread() {
wasm.start_main_thread();
}
/**
*/
export function start_worker_thread() {
wasm.start_worker_thread();
}
async function load(module, imports, maybe_memory) {
if (typeof Response === 'function' && module instanceof Response) {
memory = imports.wbg.memory = new WebAssembly.Memory({initial:17,maximum:16384,shared:true});
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
memory = imports.wbg.memory = maybe_memory;
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input, maybe_memory) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_module = function() {
var ret = init.__wbindgen_wasm_module;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_memory = function() {
var ret = wasm.__wbindgen_export_0;
return addHeapObject(ret);
};
imports.wbg.__wbg_of_6510501edc06d65e = function(arg0, arg1) {
var ret = Array.of(takeObject(arg0), takeObject(arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports, maybe_memory);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
wasm.__wbindgen_start();
return wasm;
}
export default init;

Binary file not shown.

12
codecs/oxipng/pkg-parallel/squoosh_oxipng_bg.wasm.d.ts generated vendored Normal file
View File

@ -0,0 +1,12 @@
/* tslint:disable */
/* eslint-disable */
export function optimise(a: number, b: number, c: number, d: number): void;
export function malloc(a: number): number;
export function free(a: number): void;
export function worker_initializer(a: number): number;
export function start_main_thread(): void;
export function start_worker_thread(): void;
export const __wbindgen_export_0: WebAssembly.Memory;
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_free(a: number, b: number): void;
export function __wbindgen_start(): void;

View File

@ -0,0 +1,5 @@
# OxiPNG
- Source: <https://github.com/shssoichiro/oxipng>
- Version: v3.0.0
- License: MIT

View File

@ -6,3 +6,25 @@
* @returns {Uint8Array}
*/
export function optimise(data: Uint8Array, level: number): Uint8Array;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly optimise: (a: number, b: number, c: number, d: number) => void;
readonly malloc: (a: number) => number;
readonly free: (a: number) => void;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_free: (a: number, b: number) => void;
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@ -1,2 +1,118 @@
import * as wasm from "./squoosh_oxipng_bg.wasm";
export * from "./squoosh_oxipng_bg.js";
let wasm;
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data, level) {
try {
const retptr = wasm.__wbindgen_export_0.value - 16;
wasm.__wbindgen_export_0.value = retptr;
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(retptr, ptr0, len0, level);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
} finally {
wasm.__wbindgen_export_0.value += 16;
}
}
async function load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
return wasm;
}
export default init;

View File

@ -1,60 +0,0 @@
import * as wasm from './squoosh_oxipng_bg.wasm';
const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} data
* @param {number} level
* @returns {Uint8Array}
*/
export function optimise(data, level) {
var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.optimise(8, ptr0, len0, level);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
}
export const __wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};

View File

@ -0,0 +1 @@
nightly

View File

@ -1,18 +1,21 @@
use oxipng::AlphaOptim;
use wasm_bindgen::prelude::*;
mod malloc_shim;
use wasm_bindgen::prelude::*;
use oxipng::AlphaOptim;
#[cfg(feature = "parallel")]
pub mod parallel;
#[wasm_bindgen(catch)]
#[wasm_bindgen]
pub fn optimise(data: &[u8], level: u8) -> Vec<u8> {
let mut options = oxipng::Options::from_preset(level);
options.alphas.insert(AlphaOptim::Black);
options.alphas.insert(AlphaOptim::White);
options.alphas.insert(AlphaOptim::Up);
options.alphas.insert(AlphaOptim::Down);
options.alphas.insert(AlphaOptim::Left);
options.alphas.insert(AlphaOptim::Right);
let mut options = oxipng::Options::from_preset(level);
options.alphas.insert(AlphaOptim::Black);
options.alphas.insert(AlphaOptim::White);
options.alphas.insert(AlphaOptim::Up);
options.alphas.insert(AlphaOptim::Down);
options.alphas.insert(AlphaOptim::Left);
options.alphas.insert(AlphaOptim::Right);
options.deflate = oxipng::Deflaters::Libdeflater;
oxipng::optimize_from_memory(data, &options).unwrap_throw()
options.deflate = oxipng::Deflaters::Libdeflater;
oxipng::optimize_from_memory(data, &options).unwrap_throw()
}

View File

@ -0,0 +1,62 @@
use crossbeam_channel::{bounded, Receiver, Sender};
use once_cell::sync::OnceCell;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = Array, js_name = of)]
fn array_of_2(a: JsValue, b: JsValue) -> JsValue;
}
// This is one of the parts that work around Chromium incorrectly implementing postMessage:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1075645
//
// rayon::ThreadPoolBuilder (used below) executes spawn handler to populate the worker pool,
// and then blocks the current thread until each worker unblocks its (opaque) lock.
//
// Normally, we could use postMessage directly inside the spawn handler to
// post module + memory + threadPtr to each worker, and the block the current thread.
//
// However, that bug means that postMessage is currently delayed until the next event loop,
// which will never spin since we block the current thread, and so the other workers will
// never be able to unblock us.
//
// To work around this problem, we:
// 1) Expose `worker_initializer` that returns module + memory pair (without threadPtr)
// that workers can be initialised with to become native threads.
// JavaScript can postMessage this pair in advance, and asynchronously wait for workers
// to acknowledge the receipt.
// 2) Create a global communication channel on the Rust side using crossbeam.
// It will be used to send threadPtr to the pre-initialised workers
// instead of postMessage.
// 3) Provide a separate `start_main_thread` that expects all workers to be ready,
// and just uses the provided channel to send `threadPtr`s using the
// shared memory and blocks the current thread until they're all grabbed.
// 4) Provide a `worker_initializer` that is expected to be invoked from various workers,
// reads one `threadPtr` from the shared channel and starts running it.
static CHANNEL: OnceCell<(Sender<rayon::ThreadBuilder>, Receiver<rayon::ThreadBuilder>)> =
OnceCell::new();
#[wasm_bindgen]
pub fn worker_initializer(num: usize) -> JsValue {
CHANNEL.get_or_init(|| bounded(num));
array_of_2(wasm_bindgen::module(), wasm_bindgen::memory())
}
#[wasm_bindgen]
pub fn start_main_thread() {
let (sender, _) = CHANNEL.get().unwrap();
rayon::ThreadPoolBuilder::new()
.num_threads(sender.capacity().unwrap())
.spawn_handler(|thread| Ok(sender.send(thread).unwrap_throw()))
.build_global()
.unwrap_throw()
}
#[wasm_bindgen]
pub fn start_worker_thread() {
let (_, receiver) = CHANNEL.get().unwrap();
receiver.recv().unwrap_throw().run()
}

View File

@ -1,14 +1,60 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {Uint8Array}
*/
export function resize(input_image: Uint8Array, input_width: number, input_height: number, output_width: number, output_height: number, typ_idx: number, premultiply: boolean, color_space_conversion: boolean): Uint8Array;
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {Uint8Array}
*/
export function resize(
input_image: Uint8Array,
input_width: number,
input_height: number,
output_width: number,
output_height: number,
typ_idx: number,
premultiply: boolean,
color_space_conversion: boolean,
): Uint8Array;
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly resize: (
a: number,
b: number,
c: number,
d: number,
e: number,
f: number,
g: number,
h: number,
i: number,
j: number,
) => void;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_free: (a: number, b: number) => void;
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init(
module_or_path?: InitInput | Promise<InitInput>,
): Promise<InitOutput>;

View File

@ -1,2 +1,131 @@
import * as wasm from "./squoosh_resize_bg.wasm";
export * from "./squoosh_resize_bg.js";
let wasm;
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (
cachegetUint8Memory0 === null ||
cachegetUint8Memory0.buffer !== wasm.memory.buffer
) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (
cachegetInt32Memory0 === null ||
cachegetInt32Memory0.buffer !== wasm.memory.buffer
) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {Uint8Array}
*/
export function resize(
input_image,
input_width,
input_height,
output_width,
output_height,
typ_idx,
premultiply,
color_space_conversion,
) {
var ptr0 = passArray8ToWasm0(input_image, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.resize(
8,
ptr0,
len0,
input_width,
input_height,
output_width,
output_height,
typ_idx,
premultiply,
color_space_conversion,
);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
}
async function load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn(
'`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n',
e,
);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
async function init(input) {
if (typeof input === 'undefined') {
input = import.meta.url.replace(/\.js$/, '_bg.wasm');
}
const imports = {};
if (
typeof input === 'string' ||
(typeof Request === 'function' && input instanceof Request) ||
(typeof URL === 'function' && input instanceof URL)
) {
input = fetch(input);
}
const { instance, module } = await load(await input, imports);
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
return wasm;
}
export default init;

View File

@ -1,52 +0,0 @@
import * as wasm from './squoosh_resize_bg.wasm';
let cachegetUint8Memory0 = null;
function getUint8Memory0() {
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory0;
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1);
getUint8Memory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
let cachegetInt32Memory0 = null;
function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory0;
}
function getArrayU8FromWasm0(ptr, len) {
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @param {Uint8Array} input_image
* @param {number} input_width
* @param {number} input_height
* @param {number} output_width
* @param {number} output_height
* @param {number} typ_idx
* @param {boolean} premultiply
* @param {boolean} color_space_conversion
* @returns {Uint8Array}
*/
export function resize(input_image, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion) {
var ptr0 = passArray8ToWasm0(input_image, wasm.__wbindgen_malloc);
var len0 = WASM_VECTOR_LEN;
wasm.resize(8, ptr0, len0, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1);
return v1;
}

Binary file not shown.

View File

@ -1,9 +1,13 @@
FROM emscripten/emsdk:1.39.19 AS wasm-tools
ARG RUST_IMG=rust:1.47
FROM emscripten/emsdk:2.0.8 AS wasm-tools
WORKDIR /opt/wasm-tools
RUN wget -qO- https://github.com/rustwasm/wasm-pack/releases/download/v0.9.1/wasm-pack-v0.9.1-x86_64-unknown-linux-musl.tar.gz | tar -xzf - --strip 1
FROM rust:1.44-stretch AS rust
FROM $RUST_IMG AS rust
ARG RUST_IMG
RUN rustup target add wasm32-unknown-unknown
RUN case $RUST_IMG in rustlang/rust@*) rustup component add rust-src; esac
COPY --from=wasm-tools /emsdk/upstream/bin/wasm-opt /emsdk/upstream/bin/clang /usr/local/bin/
COPY --from=wasm-tools /emsdk/upstream/lib/ /usr/local/lib/
COPY --from=wasm-tools /emsdk/upstream/emscripten/system/include/libc/ /wasm32/include/
@ -12,4 +16,4 @@ COPY --from=wasm-tools /opt/wasm-tools/wasm-pack /usr/local/cargo/bin/
ENV CPATH="/wasm32/include"
WORKDIR /src
CMD ["sh", "-c", "rm -rf pkg && wasm-pack build -- --verbose --locked && rm pkg/.gitignore"]
CMD ["sh", "-c", "rm -rf pkg && wasm-pack build --target web -- --verbose --locked && rm pkg/.gitignore"]

View File

@ -1,4 +1,4 @@
CODEC_URL := https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
CODEC_URL := https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz
CODEC_DIR = node_modules/libwebp
CODEC_OUT_RELATIVE = src/.libs/libwebp.a
CODEC_OUT := $(addprefix $(CODEC_DIR)/, $(CODEC_OUT_RELATIVE))
@ -18,7 +18,9 @@ all: $(OUT_JS)
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s 'EXPORT_NAME="$(basename $(@F))"' \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-o $@ \
$+

View File

@ -1,22 +1,20 @@
<!doctype html>
<script src='webp_dec.js'></script>
<script>
const Module = webp_dec();
async function loadFile(src) {
const resp = await fetch(src);
return await resp.arrayBuffer();
}
Module.onRuntimeInitialized = async _ => {
console.log('Version:', Module.version().toString(16));
webp_dec().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadFile('../../example.webp');
const imageData = Module.decode(image);
const imageData = module.decode(image);
const canvas = document.createElement('canvas');
canvas.width = result.width;
canvas.height = result.height;
canvas.width = imageData.width;
canvas.height = imageData.height;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
};
});
</script>

View File

@ -17,7 +17,10 @@ val decode(std::string buffer) {
int width, height;
std::unique_ptr<uint8_t[]> rgba(
WebPDecodeRGBA((const uint8_t*)buffer.c_str(), buffer.size(), &width, &height));
return rgba ? ImageData.new_(Uint8ClampedArray.new_(typed_memory_view(width * height * 4, rgba.get())), width, height) : val::null();
return rgba ? ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(width * height * 4, rgba.get())),
width, height)
: val::null();
}
EMSCRIPTEN_BINDINGS(my_module) {

View File

@ -1,5 +1,7 @@
interface WebPModule extends EmscriptenWasm.Module {
export interface WebPModule extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
export default function(opts: EmscriptenWasm.ModuleOpts): WebPModule;
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

BIN
codecs/webp/dec/webp_dec.wasm Normal file → Executable file

Binary file not shown.

View File

@ -1,8 +1,6 @@
<!doctype html>
<script src='webp_enc.js'></script>
<script>
const module = webp_enc();
async function loadImage(src) {
// Load image
const img = document.createElement('img');
@ -17,7 +15,7 @@
return ctx.getImageData(0, 0, img.width, img.height);
}
module.onRuntimeInitialized = async _ => {
webp_enc().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadImage('../../example.png');
const result = module.encode(image.data, image.width, image.height, {
@ -56,5 +54,5 @@
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);
};
});
</script>

View File

@ -1,7 +1,42 @@
import { EncodeOptions } from '../../../src/codecs/webp/encoder-meta';
interface WebPModule extends EmscriptenWasm.Module {
encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array | null;
export interface EncodeOptions {
quality: number;
target_size: number;
target_PSNR: number;
method: number;
sns_strength: number;
filter_strength: number;
filter_sharpness: number;
filter_type: number;
partitions: number;
segments: number;
pass: number;
show_compressed: number;
preprocessing: number;
autofilter: number;
partition_limit: number;
alpha_compression: number;
alpha_filtering: number;
alpha_quality: number;
lossless: number;
exact: number;
image_hint: number;
emulate_jpeg_size: number;
thread_level: number;
low_memory: number;
near_lossless: number;
use_delta_palette: number;
use_sharp_yuv: number;
}
export default function(opts: EmscriptenWasm.ModuleOpts): WebPModule;
export interface WebPModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

File diff suppressed because it is too large Load Diff

BIN
codecs/webp/enc/webp_enc.wasm Normal file → Executable file

Binary file not shown.

50
codecs/wp2/Makefile Normal file
View File

@ -0,0 +1,50 @@
CODEC_URL = https://chromium.googlesource.com/codecs/libwebp2/+archive/c90b5b476004c9a98731ae1c175cebab5de50fbf.tar.gz
CODEC_DIR = node_modules/wp2
CODEC_BUILD_DIR := $(CODEC_DIR)/.build
CODEC_OUT := $(CODEC_BUILD_DIR)/libwebp2.a
OUT_JS = enc/wp2_enc.js dec/wp2_dec.js
OUT_WASM = $(OUT_JS:.js=.wasm)
.PHONY: all clean
all: $(OUT_JS)
%.js: %.cpp $(CODEC_OUT)
$(CXX) \
-I $(CODEC_DIR) \
${CXXFLAGS} \
${LDFLAGS} \
--bind \
--closure 1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MODULARIZE=1 \
-s TEXTDECODER=2 \
-s ENVIRONMENT='worker' \
-s EXPORT_ES6=1 \
-s EXPORT_NAME="$(basename $(@F))" \
-o $@ \
$+
$(CODEC_OUT): $(CODEC_BUILD_DIR)/Makefile
cd $(CODEC_BUILD_DIR) && \
$(MAKE)
$(CODEC_BUILD_DIR)/Makefile: $(CODEC_DIR)/CMakeLists.txt
mkdir -p $(CODEC_BUILD_DIR)
cd $(CODEC_BUILD_DIR) && \
emcmake cmake \
-DWP2_ENABLE_SIMD=0 \
-DWP2_BUILD_TESTS=0 \
-DWP2_ENABLE_TESTS=0 \
-DWP2_BUILD_EXAMPLES=0 \
-DWP2_BUILD_EXTRAS=0 \
../
$(CODEC_DIR)/CMakeLists.txt:
mkdir -p $(CODEC_DIR)
curl -sL $(CODEC_URL) | tar xz -C $(CODEC_DIR)
clean:
$(RM) $(OUT_JS) $(OUT_WASM)
$(MAKE) -C $(CODEC_BUILD_DIR) clean

17
codecs/wp2/dec/README.md Normal file
View File

@ -0,0 +1,17 @@
# WebP2 decoder
- Source: <https://chromium.googlesource.com/codecs/libwebp2>
## Dependencies
- Docker
## Example
N/A
## API
### `ImageData decode(uint8_t* image_buffer, int image_width, int image_height)`
Decodes the given WebP2 buffer into raw RGBA represented as an `ImageData`.

View File

@ -0,0 +1,24 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <cstdio>
#include "src/wp2/decode.h"
using namespace emscripten;
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
val decode(std::string image_in) {
WP2::ArgbBuffer buffer(WP2_rgbA_32);
WP2Status status = WP2::Decode(image_in, &buffer);
if (status != WP2_STATUS_OK) {
return val::null();
}
return ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(buffer.stride * buffer.height, buffer.GetRow8(0))),
buffer.width, buffer.height);
}
EMSCRIPTEN_BINDINGS(my_module) {
function("decode", &decode);
}

7
codecs/wp2/dec/wp2_dec.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
export interface WP2Module extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WP2Module>;
export default moduleFactory;

1185
codecs/wp2/dec/wp2_dec.js generated Normal file

File diff suppressed because it is too large Load Diff

BIN
codecs/wp2/dec/wp2_dec.wasm Executable file

Binary file not shown.

17
codecs/wp2/enc/README.md Normal file
View File

@ -0,0 +1,17 @@
# WebP2 encoder
- Source: <https://chromium.googlesource.com/codecs/libwebp2>
## Dependencies
- Docker
## Example
N/A
## API
### `UInt8Array encode(uint8_t* image_buffer, int image_width, int image_height, WP2::EncoderConfig config)`
Encodes the given image with given dimension to WebP2.

View File

@ -0,0 +1,66 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <cstdio>
#include "src/wp2/encode.h"
using namespace emscripten;
thread_local const val Uint8Array = val::global("Uint8Array");
struct WP2Options {
float quality;
float alpha_quality;
int speed;
int pass;
int uv_mode;
float sns;
int csp_type;
int error_diffusion;
bool use_random_matrix;
};
val encode(std::string image_in, int image_width, int image_height, WP2Options options) {
WP2::EncoderConfig config = {};
config.quality = options.quality;
config.alpha_quality = options.alpha_quality;
config.speed = options.speed;
config.pass = options.pass;
config.uv_mode = static_cast<WP2::EncoderConfig::UVMode>(options.uv_mode);
config.csp_type = static_cast<WP2::Csp>(options.csp_type);
config.sns = options.sns;
config.error_diffusion = options.error_diffusion;
config.use_random_matrix = options.use_random_matrix;
uint8_t* image_buffer = (uint8_t*)image_in.c_str();
WP2::ArgbBuffer src = WP2::ArgbBuffer();
WP2Status status =
src.Import(WP2_rgbA_32, // Format. WP2_RGBA_32 is the same but NOT premultiplied alpha
image_width, image_height, image_buffer, 4 * image_width);
if (status != WP2_STATUS_OK) {
return val::null();
}
WP2::MemoryWriter memory_writer;
status = WP2::Encode(src, &memory_writer, config);
if (status != WP2_STATUS_OK) {
return val::null();
}
return Uint8Array.new_(typed_memory_view(memory_writer.size_, memory_writer.mem_));
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<WP2Options>("WP2Options")
.field("quality", &WP2Options::quality)
.field("alpha_quality", &WP2Options::alpha_quality)
.field("speed", &WP2Options::speed)
.field("pass", &WP2Options::pass)
.field("uv_mode", &WP2Options::uv_mode)
.field("csp_type", &WP2Options::csp_type)
.field("error_diffusion", &WP2Options::error_diffusion)
.field("use_random_matrix", &WP2Options::use_random_matrix)
.field("sns", &WP2Options::sns);
function("encode", &encode);
}

38
codecs/wp2/enc/wp2_enc.d.ts vendored Normal file
View File

@ -0,0 +1,38 @@
export interface EncodeOptions {
quality: number;
alpha_quality: number;
speed: number;
pass: number;
sns: number;
uv_mode: UVMode;
csp_type: Csp;
error_diffusion: number;
use_random_matrix: boolean;
}
export const enum UVMode {
UVModeAdapt = 0, // Mix of 420 and 444 (per block)
UVMode420, // All blocks 420
UVMode444, // All blocks 444
UVModeAuto, // Choose any of the above automatically
}
export const enum Csp {
kYCoCg,
kYCbCr,
kCustom,
kYIQ,
}
export interface WP2Module extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WP2Module>;
export default moduleFactory;

1277
codecs/wp2/enc/wp2_enc.js generated Normal file

File diff suppressed because it is too large Load Diff

BIN
codecs/wp2/enc/wp2_enc.wasm Executable file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More