img2num
Version:
Img2Num is a raster vectorization library - it converts images to SVGs
174 lines (159 loc) • 4.85 kB
JavaScript
/**
* @packageDocumentation
* Advanced low-level interface for communicating with the WASM worker.
* Provides granular control over calling WASM functions asynchronously,
* handling memory transfers, and managing worker lifecycle.
*
* @file Manages function call requests to the WASM worker.
* @internal
*
* @module wasm-client
* @license MIT
* @copyright Ryan Millard 2026
* @author Ryan Millard
* @since 0.0.0
* @description This module provides low-level image processing functions using WASM.
* You must specify exact C++ function signatures and manage the input data carefully.
* This allows more granular control.
*/
import { createWorker } from "@__TARGET__/worker.js";
/**
* Worker instance that handles communication with the WASM module.
* @type {Worker | null}
* @private
*/
let worker;
/**
* Incremental ID counter for correlating requests and responses.
* @type {number}
* @private
*/
let idCounter = 0;
/**
* Maps request IDs to their corresponding promise callbacks.
* @type {Map<number, {resolve: Function, reject: Function}>}
* @private
*/
const callbacks = new Map();
/**
* Flag to ensure the WASM worker is initialized only once.
* @type {boolean}
* @private
*/
let initialized = false;
/**
* Handle response messages from the WASM worker.
* @param {{ id: number, output?: any, returnValue?: any, error?: string }} data
*/
function handleMessage(data) {
const cb = callbacks.get(data.id);
if (!cb) return;
callbacks.delete(data.id);
if (data.error) {
cb.reject(new Error(data.error));
} else {
cb.resolve({ output: data.output, returnValue: data.returnValue });
}
}
/**
* @summary Initialize the WASM worker.
*
* @description
* Sets up message and error handlers. Safe to call multiple times;
* subsequent calls are no-ops. After initialization, functions can be called
* via {@link callWasm}.
*
* @function initWasmWorker
*
* @example
* import { initWasmWorker } from "./wasmClient.js";
*
* initWasmWorker();
*
* @since 0.0.0
*/
export async function initWasmWorker() {
if (initialized) return;
worker = await createWorker();
worker.onMessage(handleMessage);
worker.onError((event) => {
const output = event.message || "WASM worker error";
const err = new Error(`[Img2Num wasmClient] Error: ${output}`);
for (const [_id, cb] of callbacks) {
cb.reject(err);
}
callbacks.clear();
});
initialized = true;
}
/**
* @summary Call a function in the WASM worker.
*
* @description
* Directly send a request to the WASM worker to call the specified function,
* passing specific buffers and and arguments.
*
* @async
* @function callWasm
* @param {Object} __named_parameters - Options for the WASM call.
* @property {string} __named_parameters.funcName - The name of the WASM function to invoke.
* @property {Object} [__named_parameters.args={}] - Named arguments to pass to the WASM function.
* @property {string[]} [__named_parameters.bufferKeys=[]] - Keys of arguments that should be transferred as ArrayBuffers.
* @property {string} [__named_parameters.returnType="void"] - Expected return type.
*
* @returns {Promise<{output: any, returnValue: any}>} Resolves with the result of the WASM function call.
*
* @throws {Error} If the worker has not been initialized.
*
* @example
* import { callWasm, initWasmWorker } from "./wasmClient.js";
*
* initWasmWorker();
*
* const result = await callWasm({
* funcName: "gaussian_blur_fft",
* args: { pixels, width, height, sigma_pixels: 5 },
* bufferKeys: ["pixels"],
* returnType: "Uint8ClampedArray"
* });
* console.log(result.output);
*
* @since 0.0.0
*/
export async function callWasm({ funcName, args = {}, bufferKeys = [], returnType = "void" }) {
if (!initialized) throw new Error("WASM worker not initialized. Call initWasmWorker() first.");
const id = idCounter++;
return new Promise((resolve, reject) => {
callbacks.set(id, { resolve, reject });
worker.postMessage({ id, funcName, args, bufferKeys, returnType });
});
}
/**
* @summary Terminate the WASM worker.
*
* @description
* Terminate the WASM worker used to call WASM functions.
* Rejects any pending calls before termination and resets internal state
* so the worker can be reinitialized later.
* The worker **must be reinitialized** if it is to be used again.
*
* @function terminateWasmWorker
*
* @example
* import { terminateWasmWorker } from "./wasmClient.js";
*
* terminateWasmWorker();
*
* @since 0.0.0
*/
export function terminateWasmWorker() {
if (!worker) return;
worker.terminate();
// Reject any pending calls before clearing
for (const [_id, cb] of callbacks) {
cb.reject(new Error("WASM worker terminated"));
}
worker = null;
callbacks.clear();
initialized = false;
}