-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from CrispyBaguette/web-worker
Web worker and HTML tweaks
- Loading branch information
Showing
4 changed files
with
181 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,24 @@ | ||
# wasm-palette-converter | ||
|
||
Go+Wasm image palette converter | ||
|
||
Build with: | ||
|
||
```bash | ||
GOOS=js GOARCH=wasm go build -o dist/main.wasm . | ||
``` | ||
|
||
Access with: | ||
|
||
``` | ||
cd dist | ||
npx http-server | ||
``` | ||
|
||
A version is also available on IPFS: | ||
|
||
``` | ||
/ipfs/QmQ4ptP2z6SonqyZfF5AiqgPa8RzHpPVyZWvZ8aVNxcoC9 | ||
/ipfs/QmU6MmYFEvEYcNLdJZKJ7LfTuAPhPnpLwiUt4YcmANQESP | ||
``` | ||
You can access it directly [here](https://ipfs.io/ipfs/QmQ4ptP2z6SonqyZfF5AiqgPa8RzHpPVyZWvZ8aVNxcoC9/). | ||
|
||
You can access it directly [here](https://ipfs.io/ipfs/QmU6MmYFEvEYcNLdJZKJ7LfTuAPhPnpLwiUt4YcmANQESP/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,94 @@ | ||
(async () => { | ||
// Load element references | ||
const fileInput = document.getElementById("source-image"); | ||
const btn = document.getElementById("go-btn"); | ||
|
||
// Setup Wasm stuff | ||
const go = new Go(); | ||
vm = await WebAssembly.instantiateStreaming( | ||
fetch("./main.wasm"), | ||
go.importObject | ||
); | ||
go.run(vm.instance); | ||
|
||
// Setup event listener | ||
btn.addEventListener("click", async () => { | ||
// Clear image | ||
outputElement = document.getElementById("output"); | ||
outputElement.src = ""; | ||
|
||
// Check if a file was selected | ||
if (fileInput.files.length === 0) { | ||
alert("No file selected"); | ||
return; | ||
} | ||
reader = new FileReader(); | ||
reader.readAsArrayBuffer(fileInput.files[0]); | ||
reader.Read; | ||
reader.onloadend = async (evt) => { | ||
if (evt.target.readyState === FileReader.DONE) { | ||
const array = new Uint8Array(evt.target.result); | ||
// Wasm magic happens here | ||
ditheredImageData = await DitherNord(array); | ||
document.getElementById("output").src = | ||
"data:image/png;base64," + ditheredImageData; | ||
const fileInput = document.getElementById("source-image"); | ||
const btn = document.getElementById("go-btn"); | ||
const output = document.getElementById("output"); | ||
const outputWrapper = document.getElementById("output-wrapper"); | ||
|
||
function wasmWorker(modulePath) { | ||
// Create an object to later interact with | ||
const proxy = {}; | ||
|
||
// Keep track of the messages being sent | ||
// so we can resolve them correctly | ||
let id = 0; | ||
let idPromises = {}; | ||
|
||
return new Promise((resolve, reject) => { | ||
const worker = new Worker("worker.js"); | ||
worker.postMessage({ eventType: "INITIALISE", eventData: modulePath }); | ||
worker.addEventListener("message", function (event) { | ||
const { eventType, eventData, eventId } = event.data; | ||
|
||
if (eventType === "INITIALISED") { | ||
const methods = event.data.eventData; | ||
methods.forEach((method) => { | ||
proxy[method] = function () { | ||
return new Promise((resolve, reject) => { | ||
worker.postMessage({ | ||
eventType: "CALL", | ||
eventData: { | ||
method: method, | ||
arguments: Array.from(arguments), // arguments is not an array | ||
}, | ||
eventId: id, | ||
}); | ||
|
||
idPromises[id] = { resolve, reject }; | ||
id++; | ||
}); | ||
}; | ||
}); | ||
resolve(proxy); | ||
return; | ||
} else if (eventType === "RESULT") { | ||
if (eventId !== undefined && idPromises[eventId]) { | ||
idPromises[eventId].resolve(eventData); | ||
delete idPromises[eventId]; | ||
} | ||
} else if (eventType === "ERROR") { | ||
if (eventId !== undefined && idPromises[eventId]) { | ||
idPromises[eventId].reject(event.data.eventData); | ||
delete idPromises[eventId]; | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
worker.addEventListener("error", function (error) { | ||
reject(error); | ||
}); | ||
}); | ||
})(); | ||
} | ||
|
||
let workerResolve; | ||
const wasmReady = new Promise((resolve) => { | ||
workerResolve = resolve; | ||
}); | ||
|
||
let workerProxy; | ||
wasmWorker("./main.wasm").then((w) => { | ||
workerProxy = w; | ||
workerResolve(); | ||
btn.removeAttribute("disabled"); | ||
}); | ||
|
||
btn.addEventListener("click", async () => { | ||
// Clear image | ||
output.src = ""; | ||
|
||
// Check if a file was selected | ||
if (fileInput.files.length === 0) { | ||
alert("No file selected"); | ||
return; | ||
} | ||
const reader = new FileReader(); | ||
reader.readAsArrayBuffer(fileInput.files[0]); | ||
reader.onloadend = async (evt) => { | ||
if (evt.target.readyState === FileReader.DONE) { | ||
const imageData = new Uint8Array(evt.target.result); | ||
const ditheredImage = await workerProxy.DitherNord(imageData); | ||
|
||
const outputValue = `data:image/png;base64,${ditheredImage}`; | ||
output.src = outputValue; | ||
outputWrapper.href = outputValue; | ||
} | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
importScripts("./wasm_exec.js"); | ||
|
||
if (!WebAssembly.instantiateStreaming) { | ||
WebAssembly.instantiateStreaming = async (resp, importObject) => { | ||
const source = await (await resp).arrayBuffer(); | ||
return await WebAssembly.instantiate(source, importObject); | ||
}; | ||
} | ||
|
||
// Create promise to handle Worker calls whilst | ||
// module is still initialising | ||
let wasmResolve; | ||
const wasmReady = new Promise((resolve) => { | ||
wasmResolve = resolve; | ||
}); | ||
|
||
const go = new self.Go(); | ||
|
||
addEventListener( | ||
"message", | ||
async (e) => { | ||
const { eventType, eventData, eventId } = e.data; | ||
|
||
if (eventType === "INITIALISE") { | ||
const instantiatedSource = await WebAssembly.instantiateStreaming( | ||
fetch(eventData), | ||
go.importObject | ||
); | ||
go.run(instantiatedSource.instance); | ||
|
||
// Go does nor exposes the exports in the instantiated module :((( | ||
const methods = ["DitherNord"]; | ||
wasmResolve(methods); | ||
postMessage({ | ||
eventType: "INITIALISED", | ||
eventData: methods, | ||
}); | ||
} else if (eventType === "CALL") { | ||
await wasmReady; | ||
try { | ||
const method = self[eventData.method]; | ||
const result = await method.apply(null, eventData.arguments); | ||
self.postMessage({ | ||
eventType: "RESULT", | ||
eventData: result, | ||
eventId: eventId, | ||
}); | ||
} catch (e) { | ||
console.error(e); | ||
self.postMessage({ | ||
eventType: "ERROR", | ||
eventData: | ||
"An error occured executing WASM instance function: " + | ||
error.toString(), | ||
eventId: eventId, | ||
}); | ||
} | ||
} | ||
}, | ||
false | ||
); |