diff --git a/codecs/hqx/build.sh b/codecs/hqx/build.sh index 7af2abe0..6f62fc8a 100755 --- a/codecs/hqx/build.sh +++ b/codecs/hqx/build.sh @@ -6,7 +6,7 @@ echo "=============================================" echo "Compiling wasm" echo "=============================================" ( - wasm-pack build --target no-modules + wasm-pack build 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" diff --git a/codecs/hqx/index.html b/codecs/hqx/index.html deleted file mode 100644 index d059e5ea..00000000 --- a/codecs/hqx/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - \ No newline at end of file diff --git a/codecs/hqx/pkg/package.json b/codecs/hqx/pkg/package.json index dca922f5..03ff78cb 100644 --- a/codecs/hqx/pkg/package.json +++ b/codecs/hqx/pkg/package.json @@ -9,6 +9,7 @@ "squooshhqx.js", "squooshhqx.d.ts" ], - "browser": "squooshhqx.js", - "types": "squooshhqx.d.ts" + "module": "squooshhqx.js", + "types": "squooshhqx.d.ts", + "sideEffects": "false" } \ No newline at end of file diff --git a/codecs/hqx/pkg/squooshhqx.d.ts b/codecs/hqx/pkg/squooshhqx.d.ts index 2f6c3755..571cb095 100644 --- a/codecs/hqx/pkg/squooshhqx.d.ts +++ b/codecs/hqx/pkg/squooshhqx.d.ts @@ -7,14 +7,3 @@ * @returns {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} -*/ -export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise; - \ No newline at end of file diff --git a/codecs/hqx/pkg/squooshhqx.js b/codecs/hqx/pkg/squooshhqx.js index 12d43607..8b48e815 100644 --- a/codecs/hqx/pkg/squooshhqx.js +++ b/codecs/hqx/pkg/squooshhqx.js @@ -1,91 +1,46 @@ -(function() { - const __exports = {}; - let wasm; +import * as wasm from './squooshhqx_bg.wasm'; - let cachegetUint32Memory = null; - function getUint32Memory() { - if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { - cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); - } - return cachegetUint32Memory; +let cachegetUint32Memory = null; +function getUint32Memory() { + if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { + cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); } + return cachegetUint32Memory; +} - let WASM_VECTOR_LEN = 0; +let WASM_VECTOR_LEN = 0; - function passArray32ToWasm(arg) { - const ptr = wasm.__wbindgen_malloc(arg.length * 4); - getUint32Memory().set(arg, ptr / 4); - WASM_VECTOR_LEN = arg.length; - return ptr; +function passArray32ToWasm(arg) { + const ptr = wasm.__wbindgen_malloc(arg.length * 4); + getUint32Memory().set(arg, ptr / 4); + WASM_VECTOR_LEN = arg.length; + return ptr; +} + +let cachegetInt32Memory = null; +function getInt32Memory() { + if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) { + cachegetInt32Memory = new Int32Array(wasm.memory.buffer); } + return cachegetInt32Memory; +} - 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) { + return getUint32Memory().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) { + const retptr = 8; + const ret = wasm.resize(retptr, passArray32ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, factor); + const memi32 = getInt32Memory(); + const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); + wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4); + return v0; +} - function getArrayU32FromWasm(ptr, len) { - return getUint32Memory().subarray(ptr / 4, ptr / 4 + len); - } - /** - * @param {Uint32Array} input_image - * @param {number} input_width - * @param {number} input_height - * @param {number} factor - * @returns {Uint32Array} - */ - __exports.resize = function(input_image, input_width, input_height, factor) { - const retptr = 8; - const ret = wasm.resize(retptr, passArray32ToWasm(input_image), WASM_VECTOR_LEN, input_width, input_height, factor); - const memi32 = getInt32Memory(); - const v0 = getArrayU32FromWasm(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1]).slice(); - wasm.__wbindgen_free(memi32[retptr / 4 + 0], memi32[retptr / 4 + 1] * 4); - return v0; - }; - - 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); - -})(); diff --git a/codecs/resize/benchmark.js b/codecs/resize/benchmark.js deleted file mode 100644 index 4182f611..00000000 --- a/codecs/resize/benchmark.js +++ /dev/null @@ -1,53 +0,0 @@ -// 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. Don’t 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)); diff --git a/codecs/resize/build.sh b/codecs/resize/build.sh index c2418c29..8fb56da0 100755 --- a/codecs/resize/build.sh +++ b/codecs/resize/build.sh @@ -6,7 +6,7 @@ echo "=============================================" echo "Compiling wasm" echo "=============================================" ( - wasm-pack build --target no-modules + wasm-pack build wasm-strip pkg/resize_bg.wasm ) echo "=============================================" diff --git a/codecs/resize/package.json b/codecs/resize/package.json index 1786a997..439f4262 100644 --- a/codecs/resize/package.json +++ b/codecs/resize/package.json @@ -2,7 +2,6 @@ "name": "resize", "scripts": { "build:image": "docker build -t squoosh-resize .", - "build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh", - "benchmark": "v8 --no-liftoff --no-wasm-tier-up ./benchmark.js" + "build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh" } } diff --git a/codecs/resize/pkg/resize.d.ts b/codecs/resize/pkg/resize.d.ts index 25a0e4fe..f5266aa1 100644 --- a/codecs/resize/pkg/resize.d.ts +++ b/codecs/resize/pkg/resize.d.ts @@ -11,14 +11,3 @@ * @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; - -/** -* 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} -*/ -export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise; - \ No newline at end of file diff --git a/codecs/resize/pkg/resize.js b/codecs/resize/pkg/resize.js index f7d41ff0..dbb364ae 100644 --- a/codecs/resize/pkg/resize.js +++ b/codecs/resize/pkg/resize.js @@ -1,95 +1,50 @@ -(function() { - const __exports = {}; - let wasm; +import * as wasm from './resize_bg.wasm'; - let cachegetUint8Memory = null; - function getUint8Memory() { - if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { - cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory; +let cachegetUint8Memory = null; +function getUint8Memory() { + if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { + cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); } + return cachegetUint8Memory; +} - let WASM_VECTOR_LEN = 0; +let WASM_VECTOR_LEN = 0; - function passArray8ToWasm(arg) { - const ptr = wasm.__wbindgen_malloc(arg.length * 1); - getUint8Memory().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; +function passArray8ToWasm(arg) { + const ptr = wasm.__wbindgen_malloc(arg.length * 1); + getUint8Memory().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} + +let cachegetInt32Memory = null; +function getInt32Memory() { + if (cachegetInt32Memory === null || cachegetInt32Memory.buffer !== wasm.memory.buffer) { + cachegetInt32Memory = new Int32Array(wasm.memory.buffer); } + return cachegetInt32Memory; +} - 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) { + return getUint8Memory().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) { + 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; +} - function getArrayU8FromWasm(ptr, len) { - return getUint8Memory().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} - */ - __exports.resize = function(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; - }; - - 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); - -})(); diff --git a/codecs/resize/pkg/resize_bg.wasm b/codecs/resize/pkg/resize_bg.wasm index c54e145b..50fc8dc9 100644 Binary files a/codecs/resize/pkg/resize_bg.wasm and b/codecs/resize/pkg/resize_bg.wasm differ diff --git a/src/codecs/hqx/processor.ts b/src/codecs/hqx/processor.ts index f17f15a9..be67a917 100644 --- a/src/codecs/hqx/processor.ts +++ b/src/codecs/hqx/processor.ts @@ -1,24 +1,12 @@ -import wasmUrl from '../../../codecs/hqx/pkg/squooshhqx_bg.wasm'; -import '../../../codecs/hqx/pkg/squooshhqx'; +import { resize } from '../../../codecs/hqx/pkg'; import { HqxOptions } from './processor-meta'; -interface WasmBindgenExports { - resize: typeof import('../../../codecs/hqx/pkg/squooshhqx').resize; -} - -type WasmBindgen = ((url: string) => Promise) & WasmBindgenExports; - -declare var wasm_bindgen: WasmBindgen; - -const ready = wasm_bindgen(wasmUrl); - export async function hqx( data: ImageData, opts: HqxOptions, ): Promise { const input = data; - await ready; - const result = wasm_bindgen.resize( + const result = resize( new Uint32Array(input.data.buffer), input.width, input.height, diff --git a/src/codecs/resize/processor.ts b/src/codecs/resize/processor.ts index 142a8e8a..afe2fc2b 100644 --- a/src/codecs/resize/processor.ts +++ b/src/codecs/resize/processor.ts @@ -1,17 +1,6 @@ -import wasmUrl from '../../../codecs/resize/pkg/resize_bg.wasm'; -import '../../../codecs/resize/pkg/resize'; import { WorkerResizeOptions } from './processor-meta'; import { getContainOffsets } from './util'; - -interface WasmBindgenExports { - resize: typeof import('../../../codecs/resize/pkg/resize').resize; -} - -type WasmBindgen = ((url: string) => Promise) & WasmBindgenExports; - -declare var wasm_bindgen: WasmBindgen; - -const ready = wasm_bindgen(wasmUrl); +import { resize as codecResize } from '../../../codecs/resize/pkg'; function crop(data: ImageData, sx: number, sy: number, sw: number, sh: number): ImageData { const inputPixels = new Uint32Array(data.data.buffer); @@ -41,9 +30,7 @@ export async function resize(data: ImageData, opts: WorkerResizeOptions): Promis input = crop(input, Math.round(sx), Math.round(sy), Math.round(sw), Math.round(sh)); } - await ready; - - const result = wasm_bindgen.resize( + const result = codecResize( new Uint8Array(input.data.buffer), input.width, input.height, opts.width, opts.height, resizeMethods.indexOf(opts.method), opts.premultiply, opts.linearRGB, ); diff --git a/webpack.config.js b/webpack.config.js index c6c16b3f..e24d5cf8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -147,8 +147,9 @@ module.exports = async function (_, env) { loader: 'exports-loader' }, { - // For some reason using `include` here breaks the build. - test: /[/\\]codecs[/\\].*\.wasm$/, + // Emscripten modules don't work with Webpack's Wasm loader. + test: /\.wasm$/, + exclude: /_bg\.wasm$/, // This is needed to make webpack NOT process wasm files. // See https://github.com/webpack/webpack/issues/6725 type: 'javascript/auto', @@ -157,6 +158,11 @@ module.exports = async function (_, env) { 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)$/, loader: 'file-loader',