| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const V = "v=12"; |
|
|
| const isolated = typeof crossOriginIsolated !== "undefined" && crossOriginIsolated; |
|
|
| |
| |
| |
| const POOL = Math.max(1, Math.min(navigator.hardwareConcurrency || 4, 16) - 1); |
|
|
| async function load() { |
| const dir = isolated ? "./pkg-threads" : "./pkg"; |
| stage = "import"; postMessage({ type: "stage", stage }); |
| const mod = await import(`${dir}/inkvec_wasm.js?${V}`); |
| stage = "instantiate"; postMessage({ type: "stage", stage }); |
| await mod.default({ module_or_path: new URL(`${dir}/inkvec_wasm_bg.wasm?${V}`, import.meta.url) }); |
| stage = "pool"; postMessage({ type: "stage", stage }); |
| let threads = 0; |
| if (isolated && mod.initThreadPool) { |
| |
| |
| |
| try { |
| await mod.initThreadPool(POOL); |
| threads = POOL; |
| stage = "ready"; |
| } catch (e) { |
| poolError = String((e && e.stack) || e); |
| console.warn("inkvec: thread pool did not start, running on one core:", e); |
| } |
| } |
| return { mod, threads }; |
| } |
|
|
| let poolError = null; |
| let stage = "start"; |
|
|
| const ready = load().then(({ mod, threads }) => { |
| postMessage({ type: "ready", version: mod.version(), threads, poolError }); |
| return mod; |
| }); |
| ready.catch((e) => postMessage({ type: "loadfail", stage, error: String((e && e.stack) || e) })); |
|
|
| |
| |
| let denoiser = null; |
| async function denoiserModule() { |
| if (!denoiser) denoiser = import(`./denoise.js?${V}`); |
| return denoiser; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function denoise(intake, mode, mod, report) { |
| const note = {}; |
| let probe = null; |
|
|
| if (mode === "auto") { |
| report({ type: "stage", stage: "checking" }); |
| const t = performance.now(); |
| probe = intake.trace(); |
| note.probeMs = performance.now() - t; |
| const residual = intake.residual(probe); |
| const threshold = mod.denoiser_threshold(); |
| note.residual = residual ?? null; |
| note.threshold = threshold; |
| if (!(residual > threshold)) { |
| |
| note.skipped = residual === undefined || residual === null ? "unmeasurable" : "clean"; |
| report({ type: "stage", stage: "kept" }); |
| return { svg: probe, note }; |
| } |
| } |
|
|
| const den = await denoiserModule(); |
| report({ type: "stage", stage: "denoiser" }); |
| |
| |
| let last = 0; |
| await den.load({ |
| url: mod.denoiser_model_url(), |
| sha256: mod.denoiser_model_sha256(), |
| onStage: (s) => report({ type: "stage", stage: "denoiser:" + s }), |
| onProgress: (p) => { |
| const now = performance.now(); |
| if (p.cached || p.received === p.total || now - last > 250) { |
| last = now; |
| report({ type: "download", ...p }); |
| } |
| }, |
| }); |
|
|
| const [pw, ph] = intake.denoiser_input_size; |
| const backend = den.activeBackend(); |
| report({ type: "stage", stage: "denoising", width: pw, height: ph, backend }); |
| const t0 = performance.now(); |
| const out = await den.run(intake.denoiser_input(), pw, ph); |
| note.ms = performance.now() - t0; |
| note.backend = den.activeBackend(); |
| intake.take_denoiser_output(out); |
| note.denoised = true; |
| return { svg: null, note, pixels: intake.rgba8(), width: intake.width, height: intake.height }; |
| } |
|
|
| onmessage = async (e) => { |
| const mod = await ready; |
| const { id, bytes, o } = e.data; |
| const report = (m) => postMessage({ ...m, id }); |
| const t0 = performance.now(); |
| let intake = null; |
| try { |
| const mode = o.denoise || "off"; |
| if (mode === "off") { |
| |
| const svg = mod.trace(bytes, o.precision, o.min_area, o.colors, o.merge, o.max_dim, |
| o.time_budget, o.no_background, o.minify, o.margin, o.content_units, |
| o.cutout); |
| postMessage({ type: "done", id, svg, ms: performance.now() - t0 }); |
| return; |
| } |
|
|
| intake = mod.prepare(bytes, o.precision, o.min_area, o.colors, o.merge, o.max_dim, |
| o.time_budget, o.no_background, o.minify, o.margin, o.content_units, |
| o.cutout); |
| const r = await denoise(intake, mode, mod, report); |
| let svg = r.svg; |
| if (svg === null) { |
| report({ type: "stage", stage: "tracing" }); |
| svg = intake.trace(); |
| } |
| const msg = { type: "done", id, svg, ms: performance.now() - t0, denoise: r.note }; |
| if (r.pixels) { |
| msg.pixels = r.pixels; |
| msg.width = r.width; |
| msg.height = r.height; |
| postMessage(msg, [r.pixels.buffer]); |
| } else { |
| postMessage(msg); |
| } |
| } catch (err) { |
| postMessage({ type: "error", id, error: String((err && err.message) || err) }); |
| } finally { |
| intake?.free?.(); |
| } |
| }; |
|
|