From d9e1177cd875908b21a0e9da5a96f5a8119b181e Mon Sep 17 00:00:00 2001 From: Jake Archibald Date: Mon, 21 Sep 2020 14:23:04 +0100 Subject: [PATCH] wip on rust wasm integration --- codecs/resize/pkg/squoosh_resize.d.ts | 68 +++++++-- codecs/resize/pkg/squoosh_resize.js | 136 +++++++++++++++++- codecs/resize/pkg/squoosh_resize_bg.js | 52 ------- src/client/index.tsx | 34 ++--- .../processors/resize/shared/index.ts | 7 - .../processors/resize/worker/resize.ts | 13 +- 6 files changed, 217 insertions(+), 93 deletions(-) delete mode 100644 codecs/resize/pkg/squoosh_resize_bg.js diff --git a/codecs/resize/pkg/squoosh_resize.d.ts b/codecs/resize/pkg/squoosh_resize.d.ts index e984b053..a07bae4c 100644 --- a/codecs/resize/pkg/squoosh_resize.d.ts +++ b/codecs/resize/pkg/squoosh_resize.d.ts @@ -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} module_or_path + * + * @returns {Promise} + */ +export default function init( + module_or_path?: InitInput | Promise, +): Promise; diff --git a/codecs/resize/pkg/squoosh_resize.js b/codecs/resize/pkg/squoosh_resize.js index ae32b04d..82659497 100644 --- a/codecs/resize/pkg/squoosh_resize.js +++ b/codecs/resize/pkg/squoosh_resize.js @@ -1,2 +1,134 @@ -import * as wasm from "./squoosh_resize_bg.wasm"; -export * from "./squoosh_resize_bg.js"; \ No newline at end of file +import * as __wbg_star0 from 'env'; + +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 = {}; + imports['env'] = __wbg_star0; + + 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; diff --git a/codecs/resize/pkg/squoosh_resize_bg.js b/codecs/resize/pkg/squoosh_resize_bg.js deleted file mode 100644 index bfbf2f9f..00000000 --- a/codecs/resize/pkg/squoosh_resize_bg.js +++ /dev/null @@ -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; -} - diff --git a/src/client/index.tsx b/src/client/index.tsx index f6ddfc3a..dc19e94e 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -29,30 +29,26 @@ async function demo() { const ctx = canvas.getContext('2d')!; ctx.drawImage(img, 0, 0); const data = ctx.getImageData(0, 0, img.width, img.height); - const result = await api.mozjpegEncode(data, { - quality: 75, - baseline: false, - arithmetic: false, - progressive: true, - optimize_coding: true, - smoothing: 0, - color_space: 3, - quant_table: 3, - trellis_multipass: false, - trellis_opt_zero: false, - trellis_opt_table: false, - trellis_loops: 1, - auto_subsample: true, - chroma_subsample: 2, - separate_chroma_quality: false, - chroma_quality: 75, + const result = await api.resize(data, { + fitMethod: 'stretch', + height: 20, + width: 20, + linearRGB: false, + premultiply: true, + method: 'lanczos3', }); { - const resultUrl = URL.createObjectURL(new Blob([result])); + /*const resultUrl = URL.createObjectURL(new Blob([result])); const img = new Image(); img.src = resultUrl; - document.body.append(img); + document.body.append(img);*/ + + const canvas = document.createElement('canvas'); + canvas.width = result.width; + canvas.height = result.height; + const ctx = canvas.getContext('2d')!; + ctx.putImageData(result, 0, 0); } } diff --git a/src/features/processors/resize/shared/index.ts b/src/features/processors/resize/shared/index.ts index f142d218..2a5f6df4 100644 --- a/src/features/processors/resize/shared/index.ts +++ b/src/features/processors/resize/shared/index.ts @@ -22,13 +22,6 @@ type WorkerResizeMethods = | 'mitchell' | 'lanczos3' | 'hqx'; -const workerResizeMethods: WorkerResizeMethods[] = [ - 'triangle', - 'catrom', - 'mitchell', - 'lanczos3', - 'hqx', -]; export type ResizeOptions = | BrowserResizeOptions diff --git a/src/features/processors/resize/worker/resize.ts b/src/features/processors/resize/worker/resize.ts index aed8d267..d4cf37b6 100644 --- a/src/features/processors/resize/worker/resize.ts +++ b/src/features/processors/resize/worker/resize.ts @@ -1,6 +1,7 @@ import { WorkerResizeOptions } from '../shared'; import { getContainOffsets } from '../shared/util'; -import { resize as codecResize } from 'codecs/resize/pkg'; +import initWasm, { resize as wasmResize } from 'codecs/resize/pkg'; +import wasmUrl from 'url:codecs/resize/pkg/squoosh_resize_bg.wasm'; function crop( data: ImageData, @@ -32,10 +33,18 @@ const resizeMethods: WorkerResizeOptions['method'][] = [ 'lanczos3', ]; +let wasmReady: Promise; + export default async function resize( data: ImageData, opts: WorkerResizeOptions, ): Promise { + if (!wasmReady) { + wasmReady = initWasm(wasmUrl); + } + + await wasmReady; + let input = data; if (opts.fitMethod === 'contain') { @@ -54,7 +63,7 @@ export default async function resize( ); } - const result = codecResize( + const result = wasmResize( new Uint8Array(input.data.buffer), input.width, input.height,