Compare commits

..

1 Commits

Author SHA1 Message Date
Surma
26e9a848dd Run wasm-opt on hqx module 2019-06-19 00:27:13 +01:00
36 changed files with 2247 additions and 933 deletions

2
.nvmrc
View File

@@ -1 +1 @@
10.16.2 10.16.0

View File

@@ -1,12 +1,23 @@
FROM ubuntu
RUN apt-get update && \
apt-get install -qqy git build-essential cmake python2.7
RUN git clone --recursive https://github.com/WebAssembly/wabt /usr/src/wabt
RUN mkdir -p /usr/src/wabt/build
WORKDIR /usr/src/wabt/build
RUN cmake .. -DCMAKE_INSTALL_PREFIX=/opt/wabt && \
make && \
make install
FROM rust FROM rust
RUN rustup target add wasm32-unknown-unknown RUN rustup install nightly && \
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh rustup target add --toolchain nightly wasm32-unknown-unknown && \
cargo install wasm-pack
COPY --from=0 /opt/wabt /opt/wabt
RUN mkdir /opt/binaryen && \ 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 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
RUN mkdir /opt/wabt && \ ENV PATH="/opt/binaryen:/opt/wabt/bin:${PATH}"
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1
ENV PATH="/opt/binaryen:/opt/wabt:${PATH}"
WORKDIR /src WORKDIR /src

View File

@@ -6,11 +6,11 @@ echo "============================================="
echo "Compiling wasm" echo "Compiling wasm"
echo "=============================================" echo "============================================="
( (
wasm-pack build rustup run nightly \
wasm-pack build --target no-modules
mv pkg/squooshhqx_bg.wasm pkg/squooshhqx_bg.unopt.wasm
wasm-opt -Os --no-validation pkg/squooshhqx_bg.unopt.wasm -o pkg/squooshhqx_bg.wasm
wasm-strip pkg/squooshhqx_bg.wasm wasm-strip pkg/squooshhqx_bg.wasm
echo "Optimising Wasm so it doesn't break Chrome (this takes like 10-15mins. get a cup of tea)"
echo "Once https://crbug.com/974804 is fixed, we can remove this step"
wasm-opt -Os --no-validation -o pkg/squooshhqx_bg.wasm pkg/squooshhqx_bg.wasm
rm pkg/.gitignore rm pkg/.gitignore
) )
echo "=============================================" echo "============================================="

24
codecs/hqx/index.html Normal file
View File

@@ -0,0 +1,24 @@
<!doctype html>
<script src ="./pkg/squooshhqx.js"></script>
<script type="module">
async function run() {
await wasm_bindgen("./pkg/squooshhqx_bg.wasm");
const bitmap = await createImageBitmap(await fetch("https://i.imgur.com/MNDnBSc.png").then(r => r.blob()));
const canvas = document.createElement("canvas");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(bitmap, 0, 0);
const imgdata = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
const factor = 4;
const r = wasm_bindgen.resize(new Uint32Array(imgdata.data.buffer), bitmap.width, bitmap.height, factor);
canvas.width = bitmap.width * factor;
canvas.height = bitmap.height * factor;
const output = new ImageData(new Uint8ClampedArray(r.buffer), canvas.width, canvas.height);
ctx.putImageData(output, 0, 0);
canvas.style = `width: ${canvas.width}px; height: ${canvas.height}px; image-rendering: pixelated;`;
document.body.append(canvas);
}
run();
</script>

View File

@@ -0,0 +1,11 @@
// THIS IS NOT A NODE SCRIPT
// This is a d8 script. Please install jsvu[1] and install v8.
// Then run `npm run --silent benchmark`.
// [1]: https://github.com/GoogleChromeLabs/jsvu
async function init() {
const start = Date.now();
const module = await WebAssembly.compile(readbuffer("pkg/squooshhqx_bg.wasm"));
print(`${Date.now()/1000 - start/1000}`);
}
init().catch(e => console.error(e.stack));

View File

@@ -9,7 +9,6 @@
"squooshhqx.js", "squooshhqx.js",
"squooshhqx.d.ts" "squooshhqx.d.ts"
], ],
"module": "squooshhqx.js", "browser": "squooshhqx.js",
"types": "squooshhqx.d.ts", "types": "squooshhqx.d.ts"
"sideEffects": "false"
} }

View File

@@ -7,3 +7,14 @@
* @returns {Uint32Array} * @returns {Uint32Array}
*/ */
export function resize(input_image: Uint32Array, input_width: number, input_height: number, factor: number): Uint32Array; export function resize(input_image: Uint32Array, input_width: number, input_height: number, factor: number): Uint32Array;
/**
* If `module_or_path` is {RequestInfo}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {RequestInfo | BufferSource | WebAssembly.Module} module_or_path
*
* @returns {Promise<any>}
*/
export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise<any>;

View File

@@ -1,4 +1,6 @@
import * as wasm from './squooshhqx_bg.wasm'; (function() {
const __exports = {};
let wasm;
let cachegetUint32Memory = null; let cachegetUint32Memory = null;
function getUint32Memory() { function getUint32Memory() {
@@ -17,17 +19,17 @@ function passArray32ToWasm(arg) {
return ptr; return ptr;
} }
let cachegetInt32Memory = null;
function getInt32Memory() {
if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) {
cachegetInt32Memory = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory;
}
function getArrayU32FromWasm(ptr, len) { function getArrayU32FromWasm(ptr, len) {
return getUint32Memory().subarray(ptr / 4, ptr / 4 + len); return getUint32Memory().subarray(ptr / 4, ptr / 4 + len);
} }
let cachedGlobalArgumentPtr = null;
function globalArgumentPtr() {
if (cachedGlobalArgumentPtr === null) {
cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr();
}
return cachedGlobalArgumentPtr;
}
/** /**
* @param {Uint32Array} input_image * @param {Uint32Array} input_image
* @param {number} input_width * @param {number} input_width
@@ -35,12 +37,61 @@ function getArrayU32FromWasm(ptr, len) {
* @param {number} factor * @param {number} factor
* @returns {Uint32Array} * @returns {Uint32Array}
*/ */
export function resize(input_image, input_width, input_height, factor) { __exports.resize = function(input_image, input_width, input_height, factor) {
const retptr = 8; const ptr0 = passArray32ToWasm(input_image);
const ret = wasm.resize(retptr, passArray32ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, factor); const len0 = WASM_VECTOR_LEN;
const memi32 = getInt32Memory(); const retptr = globalArgumentPtr();
const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); wasm.resize(retptr, ptr0, len0, input_width, input_height, factor);
wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4); const mem = getUint32Memory();
return v0; const rustptr = mem[retptr / 4];
const rustlen = mem[retptr / 4 + 1];
const realRet = getArrayU32FromWasm(rustptr, rustlen).slice();
wasm.__wbindgen_free(rustptr, rustlen * 4);
return realRet;
};
function init(module) {
let result;
const imports = {};
if (module instanceof URL || typeof module === 'string' || module instanceof Request) {
const response = fetch(module);
if (typeof WebAssembly.instantiateStreaming === 'function') {
result = WebAssembly.instantiateStreaming(response, imports)
.catch(e => {
console.warn("`WebAssembly.instantiateStreaming` failed. Assuming this is because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
return response
.then(r => r.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports));
});
} else {
result = response
.then(r => r.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports));
}
} else {
result = WebAssembly.instantiate(module, imports)
.then(result => {
if (result instanceof WebAssembly.Instance) {
return { instance: result, module };
} else {
return result;
}
});
}
return result.then(({instance, module}) => {
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
return wasm;
});
} }
self.wasm_bindgen = Object.assign(init, __exports);
})();

View File

@@ -1,5 +1,6 @@
/* tslint:disable */ /* tslint:disable */
export const memory: WebAssembly.Memory; export const memory: WebAssembly.Memory;
export function resize(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function __wbindgen_global_argument_ptr(): number;
export function __wbindgen_malloc(a: number): number; export function __wbindgen_malloc(a: number): number;
export function __wbindgen_free(a: number, b: number): void; export function __wbindgen_free(a: number, b: number): void;
export function resize(a: number, b: number, c: number, d: number, e: number, f: number): void;

Binary file not shown.

View File

@@ -4,40 +4,84 @@ set -e
export OPTIMIZE="-Os" export OPTIMIZE="-Os"
export PREFIX="/src/build" export PREFIX="/src/build"
export CFLAGS="${OPTIMIZE} -I${PREFIX}/include/"
export CPPFLAGS="${OPTIMIZE} -I${PREFIX}/include/"
export LDFLAGS="${OPTIMIZE} -L${PREFIX}/lib/"
apt-get update
apt-get install -qqy autoconf libtool
echo "============================================="
echo "Compiling zlib"
echo "============================================="
test -n "$SKIP_ZLIB" || (
cd node_modules/zlib
emconfigure ./configure --prefix=${PREFIX}/
emmake make
emmake make install
)
echo "============================================="
echo "Compiling zlib done"
echo "============================================="
echo "============================================="
echo "Compiling libpng"
echo "============================================="
test -n "$SKIP_LIBPNG" || (
cd node_modules/libpng
autoreconf -i
emconfigure ./configure --with-zlib-prefix=${PREFIX}/ --prefix=${PREFIX}/
emmake make
emmake make install
)
echo "============================================="
echo "Compiling libpng done"
echo "============================================="
echo "=============================================" echo "============================================="
echo "Compiling optipng" echo "Compiling optipng"
echo "=============================================" echo "============================================="
( (
cd node_modules/optipng emcc \
CFLAGS="${OPTIMIZE} -Isrc/zlib" emconfigure ./configure --prefix=${PREFIX} ${OPTIMIZE} \
emmake make -Wno-implicit-function-declaration \
emmake make install -I ${PREFIX}/include \
mkdir -p ${PREFIX}/lib -I node_modules/optipng/src/opngreduc \
mv ${PREFIX}/bin/optipng ${PREFIX}/lib/liboptipng.so -I node_modules/optipng/src/pngxtern \
-I node_modules/optipng/src/cexcept \
-I node_modules/optipng/src/gifread \
-I node_modules/optipng/src/pnmio \
-I node_modules/optipng/src/minitiff \
--std=c99 -c \
node_modules/optipng/src/opngreduc/*.c \
node_modules/optipng/src/pngxtern/*.c \
node_modules/optipng/src/gifread/*.c \
node_modules/optipng/src/minitiff/*.c \
node_modules/optipng/src/pnmio/*.c \
node_modules/optipng/src/optipng/*.c
emcc \
--bind \
${OPTIMIZE} \
-s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME="optipng"' \
-I ${PREFIX}/include \
-I node_modules/optipng/src/opngreduc \
-I node_modules/optipng/src/pngxtern \
-I node_modules/optipng/src/cexcept \
-I node_modules/optipng/src/gifread \
-I node_modules/optipng/src/pnmio \
-I node_modules/optipng/src/minitiff \
-o "optipng.js" \
--std=c++11 \
optipng.cpp \
*.o \
${PREFIX}/lib/libz.so ${PREFIX}/lib/libpng.a
) )
echo "=============================================" echo "============================================="
echo "Compiling optipng done" echo "Compiling optipng done"
echo "=============================================" echo "============================================="
echo "============================================="
echo "Compiling optipng wrapper"
echo "============================================="
(
emcc \
--bind \
${OPTIMIZE} \
-s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME="optipng"' \
-o "optipng.js" \
--std=c++11 \
optipng.cpp \
${PREFIX}/lib/liboptipng.so
)
echo "============================================="
echo "Compiling optipng wrapper done"
echo "============================================="
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Did you update your docker image?" echo "Did you update your docker image?"
echo "Run \`docker pull trzeci/emscripten-upstream\`" echo "Run \`docker pull trzeci/emscripten\`"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{ {
"name": "optipng", "name": "optipng",
"scripts": { "scripts": {
"install": "tar-dependency install", "install": "tar-dependency install && napa",
"build": "npm run build:wasm", "build": "npm run build:wasm",
"build:wasm": "docker run --rm -v $(pwd):/src trzeci/emscripten-upstream ./build.sh" "build:wasm": "docker run --rm -v $(pwd):/src -e SKIP_ZLIB=\"${SKIP_ZLIB}\" -e SKIP_LIBPNG=\"${SKIP_LIBPNG}\" trzeci/emscripten ./build.sh"
}, },
"tarDependencies": { "tarDependencies": {
"node_modules/optipng": { "node_modules/optipng": {
@@ -11,7 +11,12 @@
"strip": 1 "strip": 1
} }
}, },
"napa": {
"libpng": "emscripten-ports/libpng",
"zlib": "emscripten-ports/zlib"
},
"dependencies": { "dependencies": {
"napa": "3.0.0",
"tar-dependency": "0.0.3" "tar-dependency": "0.0.3"
} }
} }

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "resize" name = "squooshresize"
version = "0.1.0" version = "0.1.0"
authors = ["Surma <surma@surma.link>"] authors = ["Surma <surma@surma.link>"]

View File

@@ -1,9 +1,18 @@
FROM ubuntu
RUN apt-get update && \
apt-get install -qqy git build-essential cmake python2.7
RUN git clone --recursive https://github.com/WebAssembly/wabt /usr/src/wabt
RUN mkdir -p /usr/src/wabt/build
WORKDIR /usr/src/wabt/build
RUN cmake .. -DCMAKE_INSTALL_PREFIX=/opt/wabt && \
make && \
make install
FROM rust FROM rust
RUN rustup target add wasm32-unknown-unknown RUN rustup install nightly && \
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh rustup target add --toolchain nightly wasm32-unknown-unknown && \
cargo install wasm-pack
RUN mkdir /opt/wabt && \ COPY --from=0 /opt/wabt /opt/wabt
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1 ENV PATH="/opt/wabt/bin:${PATH}"
ENV PATH="/opt/wabt:${PATH}"
WORKDIR /src WORKDIR /src

View File

@@ -0,0 +1,53 @@
// THIS IS NOT A NODE SCRIPT
// This is a d8 script. Please install jsvu[1] and install v8.
// Then run `npm run --silent benchmark`.
// [1]: https://github.com/GoogleChromeLabs/jsvu
self = global = this;
load("./pkg/resize.js");
async function init() {
// Adjustable constants.
const inputDimensions = 2000;
const outputDimensions = 1500;
const algorithm = 3; // Lanczos
const iterations = new Array(100);
// Constants. Dont change.
const imageByteSize = inputDimensions * inputDimensions * 4;
const imageBuffer = new Uint8ClampedArray(imageByteSize);
const module = await WebAssembly.compile(readbuffer("./pkg/resize_bg.wasm"));
await wasm_bindgen(module);
[[false, false], [true, false], [false, true], [true, true]].forEach(
opts => {
print(`\npremultiplication: ${opts[0]}`);
print(`color space conversion: ${opts[1]}`);
print(`==============================`);
for (let i = 0; i < 100; i++) {
const start = Date.now();
wasm_bindgen.resize(
imageBuffer,
inputDimensions,
inputDimensions,
outputDimensions,
outputDimensions,
algorithm,
...opts
);
iterations[i] = Date.now() - start;
}
const average =
iterations.reduce((sum, c) => sum + c) / iterations.length;
const stddev = Math.sqrt(
iterations
.map(i => Math.pow(i - average, 2))
.reduce((sum, c) => sum + c) / iterations.length
);
print(`n = ${iterations.length}`);
print(`Average: ${average}`);
print(`StdDev: ${stddev}`);
}
);
}
init().catch(e => console.error(e, e.stack));

View File

@@ -6,9 +6,9 @@ echo "============================================="
echo "Compiling wasm" echo "Compiling wasm"
echo "=============================================" echo "============================================="
( (
wasm-pack build rustup run nightly \
wasm-pack build --target no-modules
wasm-strip pkg/resize_bg.wasm wasm-strip pkg/resize_bg.wasm
rm pkg/.gitignore
) )
echo "=============================================" echo "============================================="
echo "Compiling wasm done" echo "Compiling wasm done"

View File

@@ -2,6 +2,7 @@
"name": "resize", "name": "resize",
"scripts": { "scripts": {
"build:image": "docker build -t squoosh-resize .", "build:image": "docker build -t squoosh-resize .",
"build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh" "build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh",
"benchmark": "v8 --no-liftoff --no-wasm-tier-up ./benchmark.js"
} }
} }

View File

@@ -1,15 +0,0 @@
{
"name": "resize",
"collaborators": [
"Surma <surma@surma.link>"
],
"version": "0.1.0",
"files": [
"resize_bg.wasm",
"resize.js",
"resize.d.ts"
],
"module": "resize.js",
"types": "resize.d.ts",
"sideEffects": "false"
}

View File

@@ -1,13 +1,13 @@
/* tslint:disable */ /* tslint:disable */
/** /**
* @param {Uint8Array} input_image * @param {Uint8Array} arg0
* @param {number} input_width * @param {number} arg1
* @param {number} input_height * @param {number} arg2
* @param {number} output_width * @param {number} arg3
* @param {number} output_height * @param {number} arg4
* @param {number} typ_idx * @param {number} arg5
* @param {boolean} premultiply * @param {boolean} arg6
* @param {boolean} color_space_conversion * @param {boolean} arg7
* @returns {Uint8Array} * @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 function resize(arg0: Uint8Array, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number, arg6: boolean, arg7: boolean): Uint8Array;

View File

@@ -1,4 +1,7 @@
import * as wasm from './resize_bg.wasm'; (function() {
var wasm;
const __exports = {};
let cachegetUint8Memory = null; let cachegetUint8Memory = null;
function getUint8Memory() { function getUint8Memory() {
@@ -17,34 +20,95 @@ function passArray8ToWasm(arg) {
return ptr; return ptr;
} }
let cachegetInt32Memory = null;
function getInt32Memory() {
if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) {
cachegetInt32Memory = new Int32Array(wasm.memory.buffer);
}
return cachegetInt32Memory;
}
function getArrayU8FromWasm(ptr, len) { function getArrayU8FromWasm(ptr, len) {
return getUint8Memory().subarray(ptr / 1, ptr / 1 + len); return getUint8Memory().subarray(ptr / 1, ptr / 1 + len);
} }
/**
* @param {Uint8Array} input_image let cachedGlobalArgumentPtr = null;
* @param {number} input_width function globalArgumentPtr() {
* @param {number} input_height if (cachedGlobalArgumentPtr === null) {
* @param {number} output_width cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr();
* @param {number} output_height }
* @param {number} typ_idx return cachedGlobalArgumentPtr;
* @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) {
const retptr = 8;
const ret = wasm.resize(retptr, passArray8ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, output_width, output_height, typ_idx, premultiply, color_space_conversion);
const memi32 = getInt32Memory();
const v0 = getArrayU8FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice();
wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 1);
return v0;
} }
let cachegetUint32Memory = null;
function getUint32Memory() {
if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) {
cachegetUint32Memory = new Uint32Array(wasm.memory.buffer);
}
return cachegetUint32Memory;
}
/**
* @param {Uint8Array} arg0
* @param {number} arg1
* @param {number} arg2
* @param {number} arg3
* @param {number} arg4
* @param {number} arg5
* @param {boolean} arg6
* @param {boolean} arg7
* @returns {Uint8Array}
*/
__exports.resize = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
const ptr0 = passArray8ToWasm(arg0);
const len0 = WASM_VECTOR_LEN;
const retptr = globalArgumentPtr();
wasm.resize(retptr, ptr0, len0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
const mem = getUint32Memory();
const rustptr = mem[retptr / 4];
const rustlen = mem[retptr / 4 + 1];
const realRet = getArrayU8FromWasm(rustptr, rustlen).slice();
wasm.__wbindgen_free(rustptr, rustlen * 1);
return realRet;
};
const heap = new Array(32);
heap.fill(undefined);
heap.push(undefined, null, true, false);
let heap_next = heap.length;
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
__exports.__wbindgen_object_drop_ref = function(i) { dropObject(i); };
function init(path_or_module) {
let instantiation;
const imports = { './resize': __exports };
if (path_or_module instanceof WebAssembly.Module) {
instantiation = WebAssembly.instantiate(path_or_module, imports)
.then(instance => {
return { instance, module: path_or_module }
});
} else {
const data = fetch(path_or_module);
if (typeof WebAssembly.instantiateStreaming === 'function') {
instantiation = WebAssembly.instantiateStreaming(data, imports)
.catch(e => {
console.warn("`WebAssembly.instantiateStreaming` failed. Assuming this is because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
return data
.then(r => r.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports));
});
} else {
instantiation = data
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.instantiate(buffer, imports));
}
}
return instantiation.then(({instance}) => {
wasm = init.wasm = instance.exports;
});
};
self.wasm_bindgen = Object.assign(init, __exports);
})();

View File

@@ -1,5 +1,6 @@
/* tslint:disable */ /* tslint:disable */
export const memory: WebAssembly.Memory; export const memory: WebAssembly.Memory;
export function __wbindgen_global_argument_ptr(): number;
export function __wbindgen_malloc(a: number): number; export function __wbindgen_malloc(a: number): number;
export function __wbindgen_free(a: number, b: number): void; export function __wbindgen_free(a: number, b: number): void;
export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): void; export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): void;

Binary file not shown.

View File

@@ -1,8 +1,17 @@
FROM ubuntu
RUN apt-get update && \
apt-get install -qqy git build-essential cmake python2.7
RUN git clone --recursive https://github.com/WebAssembly/wabt /usr/src/wabt
RUN mkdir -p /usr/src/wabt/build
WORKDIR /usr/src/wabt/build
RUN cmake .. -DCMAKE_INSTALL_PREFIX=/opt/wabt && \
make && \
make install
FROM rust FROM rust
RUN rustup target add wasm32-unknown-unknown RUN rustup install nightly && \
rustup target add --toolchain nightly wasm32-unknown-unknown
RUN mkdir /opt/wabt && \ COPY --from=0 /opt/wabt /opt/wabt
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.11/wabt-1.0.11-linux.tar.gz | tar -xzf - -C /opt/wabt --strip 1 ENV PATH="/opt/wabt/bin:${PATH}"
ENV PATH="/opt/wabt:${PATH}"
WORKDIR /src WORKDIR /src

View File

@@ -6,6 +6,7 @@ echo "============================================="
echo "Compiling wasm" echo "Compiling wasm"
echo "=============================================" echo "============================================="
( (
rustup run nightly \
cargo build \ cargo build \
--target wasm32-unknown-unknown \ --target wasm32-unknown-unknown \
--release --release

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

1517
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "squoosh", "name": "squoosh",
"version": "1.9.1", "version": "1.8.0",
"license": "apache-2.0", "license": "apache-2.0",
"scripts": { "scripts": {
"start": "webpack-dev-server --host 0.0.0.0 --hot", "start": "webpack-dev-server --host 0.0.0.0 --hot",
@@ -16,32 +16,32 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@types/node": "10.14.15", "@types/node": "10.14.9",
"@types/pretty-bytes": "5.1.0", "@types/pretty-bytes": "5.1.0",
"@types/webassembly-js-api": "0.0.3", "@types/webassembly-js-api": "0.0.3",
"@webcomponents/custom-elements": "1.2.4", "@webcomponents/custom-elements": "1.2.4",
"@webpack-cli/serve": "0.1.8", "@webpack-cli/serve": "0.1.8",
"assets-webpack-plugin": "3.9.10", "assets-webpack-plugin": "3.9.10",
"chalk": "2.4.2", "chalk": "2.4.2",
"chokidar": "3.0.2", "chokidar": "3.0.1",
"classnames": "2.2.6", "classnames": "2.2.6",
"clean-webpack-plugin": "1.0.1", "clean-webpack-plugin": "1.0.1",
"comlink": "3.1.1", "comlink": "3.1.1",
"copy-webpack-plugin": "5.0.4", "copy-webpack-plugin": "5.0.3",
"critters-webpack-plugin": "2.4.0", "critters-webpack-plugin": "2.3.0",
"css-loader": "1.0.1", "css-loader": "1.0.1",
"ejs": "2.6.2", "ejs": "2.6.2",
"escape-string-regexp": "2.0.0", "escape-string-regexp": "2.0.0",
"exports-loader": "0.7.0", "exports-loader": "0.7.0",
"file-drop-element": "0.2.0", "file-drop-element": "0.2.0",
"file-loader": "4.2.0", "file-loader": "4.0.0",
"gzip-size": "5.1.1", "gzip-size": "5.1.1",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"husky": "3.0.4", "husky": "2.4.1",
"idb-keyval": "3.2.0", "idb-keyval": "3.2.0",
"linkstate": "1.1.1", "linkstate": "1.1.1",
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"mini-css-extract-plugin": "0.8.0", "mini-css-extract-plugin": "0.7.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"node-fetch": "2.6.0", "node-fetch": "2.6.0",
"node-sass": "4.12.0", "node-sass": "4.12.0",
@@ -49,28 +49,28 @@
"pointer-tracker": "2.0.3", "pointer-tracker": "2.0.3",
"preact": "8.4.2", "preact": "8.4.2",
"prerender-loader": "1.3.0", "prerender-loader": "1.3.0",
"pretty-bytes": "5.3.0", "pretty-bytes": "5.2.0",
"progress-bar-webpack-plugin": "1.12.1", "progress-bar-webpack-plugin": "1.12.1",
"raw-loader": "3.1.0", "raw-loader": "3.0.0",
"readdirp": "3.1.2", "readdirp": "3.0.2",
"sass-loader": "7.3.1", "sass-loader": "7.1.0",
"script-ext-html-webpack-plugin": "2.1.4", "script-ext-html-webpack-plugin": "2.1.3",
"source-map-loader": "0.2.4", "source-map-loader": "0.2.4",
"style-loader": "1.0.0", "style-loader": "0.23.1",
"terser-webpack-plugin": "1.4.1", "terser-webpack-plugin": "1.3.0",
"travis-size-report": "1.1.0", "travis-size-report": "1.0.1",
"ts-loader": "6.0.3", "ts-loader": "6.0.3",
"tslint": "5.19.0", "tslint": "5.17.0",
"tslint-config-airbnb": "5.11.1", "tslint-config-airbnb": "5.11.1",
"tslint-config-semistandard": "8.0.1", "tslint-config-semistandard": "8.0.1",
"tslint-react": "4.0.0", "tslint-react": "4.0.0",
"typed-css-modules": "0.4.2", "typed-css-modules": "0.4.2",
"typescript": "3.5.3", "typescript": "3.5.2",
"url-loader": "2.1.0", "url-loader": "2.0.0",
"webpack": "4.28.0", "webpack": "4.28.0",
"webpack-bundle-analyzer": "3.4.1", "webpack-bundle-analyzer": "3.3.2",
"webpack-cli": "3.3.4", "webpack-cli": "3.3.4",
"webpack-dev-server": "3.8.0", "webpack-dev-server": "3.7.1",
"worker-plugin": "3.1.0" "worker-plugin": "3.1.0"
} }
} }

View File

@@ -1,12 +1,24 @@
import { resize } from '../../../codecs/hqx/pkg'; import wasmUrl from '../../../codecs/hqx/pkg/squooshhqx_bg.wasm';
import '../../../codecs/hqx/pkg/squooshhqx';
import { HqxOptions } from './processor-meta'; import { HqxOptions } from './processor-meta';
interface WasmBindgenExports {
resize: typeof import('../../../codecs/hqx/pkg/squooshhqx').resize;
}
type WasmBindgen = ((url: string) => Promise<void>) & WasmBindgenExports;
declare var wasm_bindgen: WasmBindgen;
const ready = wasm_bindgen(wasmUrl);
export async function hqx( export async function hqx(
data: ImageData, data: ImageData,
opts: HqxOptions, opts: HqxOptions,
): Promise<ImageData> { ): Promise<ImageData> {
const input = data; const input = data;
const result = resize( await ready;
const result = wasm_bindgen.resize(
new Uint32Array(input.data.buffer), new Uint32Array(input.data.buffer),
input.width, input.width,
input.height, input.height,

View File

@@ -24,6 +24,19 @@ interface State {
const sizePresets = [0.25, 0.3333, 0.5, 1, 2, 3, 4]; const sizePresets = [0.25, 0.3333, 0.5, 1, 2, 3, 4];
/**
* Should we allow the user to select hqx? Chrome currently has a wasm bug, so we currently avoid it
* there, unless overridden.
* crbug.com/974804
*/
const allowHqx: boolean = (() => {
const url = new URL(location.href);
return url.searchParams.has('allow-hqx')
// Yep. UA sniffing. Let's hope we can remove this soon.
// Block browsers with Chrome/, unless they also have Edge/ (since the Edge UA includes Chrome/)
|| !navigator.userAgent.includes('Chrome/') || navigator.userAgent.includes('Edge/');
})();
export default class ResizerOptions extends Component<Props, State> { export default class ResizerOptions extends Component<Props, State> {
state: State = { state: State = {
maintainAspect: true, maintainAspect: true,
@@ -150,7 +163,7 @@ export default class ResizerOptions extends Component<Props, State> {
<option value="mitchell">Mitchell</option> <option value="mitchell">Mitchell</option>
<option value="catrom">Catmull-Rom</option> <option value="catrom">Catmull-Rom</option>
<option value="triangle">Triangle (bilinear)</option> <option value="triangle">Triangle (bilinear)</option>
<option value="hqx">hqx (pixel art)</option> {allowHqx && <option value="hqx">hqx (pixel art)</option>}
<option value="browser-pixelated">Browser pixelated</option> <option value="browser-pixelated">Browser pixelated</option>
<option value="browser-low">Browser low quality</option> <option value="browser-low">Browser low quality</option>
<option value="browser-medium">Browser medium quality</option> <option value="browser-medium">Browser medium quality</option>

View File

@@ -1,6 +1,17 @@
import wasmUrl from '../../../codecs/resize/pkg/resize_bg.wasm';
import '../../../codecs/resize/pkg/resize';
import { WorkerResizeOptions } from './processor-meta'; import { WorkerResizeOptions } from './processor-meta';
import { getContainOffsets } from './util'; import { getContainOffsets } from './util';
import { resize as codecResize } from '../../../codecs/resize/pkg';
interface WasmBindgenExports {
resize: typeof import('../../../codecs/resize/pkg/resize').resize;
}
type WasmBindgen = ((url: string) => Promise<void>) & WasmBindgenExports;
declare var wasm_bindgen: WasmBindgen;
const ready = wasm_bindgen(wasmUrl);
function crop(data: ImageData, sx: number, sy: number, sw: number, sh: number): ImageData { function crop(data: ImageData, sx: number, sy: number, sw: number, sh: number): ImageData {
const inputPixels = new Uint32Array(data.data.buffer); const inputPixels = new Uint32Array(data.data.buffer);
@@ -30,7 +41,9 @@ export async function resize(data: ImageData, opts: WorkerResizeOptions): Promis
input = crop(input, Math.round(sx), Math.round(sy), Math.round(sw), Math.round(sh)); input = crop(input, Math.round(sx), Math.round(sy), Math.round(sw), Math.round(sh));
} }
const result = codecResize( await ready;
const result = wasm_bindgen.resize(
new Uint8Array(input.data.buffer), input.width, input.height, opts.width, opts.height, new Uint8Array(input.data.buffer), input.width, input.height, opts.width, opts.height,
resizeMethods.indexOf(opts.method), opts.premultiply, opts.linearRGB, resizeMethods.indexOf(opts.method), opts.premultiply, opts.linearRGB,
); );

View File

@@ -11,12 +11,6 @@
"src": "/assets/icon-large.png", "src": "/assets/icon-large.png",
"type": "image/png", "type": "image/png",
"sizes": "1024x1024" "sizes": "1024x1024"
},
{
"src": "/assets/icon-large-maskable.png",
"type": "image/png",
"sizes": "1024x1024",
"purpose": "maskable"
} }
], ],
"share_target": { "share_target": {

View File

@@ -1,3 +1,4 @@
const fs = require('fs');
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const CleanPlugin = require('clean-webpack-plugin'); const CleanPlugin = require('clean-webpack-plugin');
@@ -16,7 +17,11 @@ const CrittersPlugin = require('critters-webpack-plugin');
const AssetTemplatePlugin = require('./config/asset-template-plugin'); const AssetTemplatePlugin = require('./config/asset-template-plugin');
const addCssTypes = require('./config/add-css-types'); const addCssTypes = require('./config/add-css-types');
const VERSION = require('./package.json').version; function readJson (filename) {
return JSON.parse(fs.readFileSync(filename));
}
const VERSION = readJson('./package.json').version;
module.exports = async function (_, env) { module.exports = async function (_, env) {
const isProd = env.mode === 'production'; const isProd = env.mode === 'production';
@@ -142,14 +147,11 @@ module.exports = async function (_, env) {
}, },
{ {
// All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`. // All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`.
test: /\.js$/, test: /\/codecs\/.*\.js$/,
include: path.join(__dirname, 'src/codecs'),
loader: 'exports-loader' loader: 'exports-loader'
}, },
{ {
// Emscripten modules don't work with Webpack's Wasm loader. test: /\/codecs\/.*\.wasm$/,
test: /\.wasm$/,
exclude: /_bg\.wasm$/,
// This is needed to make webpack NOT process wasm files. // This is needed to make webpack NOT process wasm files.
// See https://github.com/webpack/webpack/issues/6725 // See https://github.com/webpack/webpack/issues/6725
type: 'javascript/auto', type: 'javascript/auto',
@@ -158,11 +160,6 @@ module.exports = async function (_, env) {
name: '[name].[hash:5].[ext]', name: '[name].[hash:5].[ext]',
}, },
}, },
{
// Wasm modules generated by Rust + wasm-pack work great with Webpack.
test: /_bg\.wasm$/,
type: 'webassembly/experimental',
},
{ {
test: /\.(png|svg|jpg|gif)$/, test: /\.(png|svg|jpg|gif)$/,
loader: 'file-loader', loader: 'file-loader',
@@ -175,7 +172,7 @@ module.exports = async function (_, env) {
plugins: [ plugins: [
new webpack.IgnorePlugin( new webpack.IgnorePlugin(
/(fs|crypto|path)/, /(fs|crypto|path)/,
/[/\\]codecs[/\\]/ new RegExp(`${path.sep}codecs${path.sep}`)
), ),
// Pretty progressbar showing build progress: // Pretty progressbar showing build progress:
@@ -242,7 +239,7 @@ module.exports = async function (_, env) {
removeRedundantAttributes: true, removeRedundantAttributes: true,
removeComments: true removeComments: true
}, },
manifest: require('./src/manifest.json'), manifest: readJson('./src/manifest.json'),
inject: 'body', inject: 'body',
compile: true compile: true
}), }),