2021-12-11 15:51:45 +00:00
|
|
|
import React from "react";
|
2021-12-11 15:19:48 +00:00
|
|
|
import "./App.css";
|
2021-12-11 13:21:08 +00:00
|
|
|
|
|
|
|
function App() {
|
2021-12-11 15:19:48 +00:00
|
|
|
const [imageSrc, setImageSrc] = React.useState("");
|
|
|
|
const fileInput = React.useRef<HTMLInputElement>(null);
|
|
|
|
|
|
|
|
const handleClick = async () => {
|
|
|
|
if (!fileInput.current) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const workerProxy: any = await wasmWorker("main.wasm");
|
|
|
|
|
|
|
|
setImageSrc("");
|
|
|
|
|
|
|
|
// Check if a file was selected
|
|
|
|
if (fileInput.current.files!.length === 0) {
|
|
|
|
alert("No file selected");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.readAsArrayBuffer(fileInput.current.files![0]);
|
|
|
|
reader.onloadend = async (evt) => {
|
|
|
|
if (evt.target!.readyState === FileReader.DONE) {
|
|
|
|
const imageData = new Uint8Array(evt.target!.result as ArrayBuffer);
|
|
|
|
const ditheredImage = await workerProxy.DitherNord(imageData);
|
|
|
|
const outputValue = `data:image/png;base64,${ditheredImage}`;
|
|
|
|
setImageSrc(outputValue);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-12-11 13:21:08 +00:00
|
|
|
return (
|
2021-12-11 15:19:48 +00:00
|
|
|
<div className="App">
|
|
|
|
<input
|
|
|
|
type="file"
|
|
|
|
id="source-image"
|
|
|
|
ref={fileInput}
|
|
|
|
accept="image/png, image/jpeg"
|
|
|
|
/>
|
|
|
|
<button id="go-btn" type="button" onClick={handleClick}>
|
|
|
|
Go
|
|
|
|
</button>
|
|
|
|
<a id="output-wrapper" download="output.png" href={imageSrc}>
|
|
|
|
<div className="container">
|
|
|
|
<img
|
|
|
|
id="output"
|
|
|
|
alt="dithering output"
|
|
|
|
src={imageSrc}
|
2021-12-11 15:51:45 +00:00
|
|
|
style={{ display: imageSrc !== "" ? "block" : "none" }}
|
2021-12-11 15:19:48 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</div>
|
2021-12-11 13:21:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-12-11 15:19:48 +00:00
|
|
|
function wasmWorker(modulePath: string) {
|
|
|
|
// Create an object to later interact with
|
|
|
|
const proxy: any = {};
|
|
|
|
|
|
|
|
// Keep track of the messages being sent
|
|
|
|
// so we can resolve them correctly
|
|
|
|
let id = 0;
|
|
|
|
let idPromises: any = {};
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const worker = new Worker("./worker.js");
|
|
|
|
worker.postMessage({ eventType: "INITIALISE", eventData: modulePath });
|
|
|
|
worker.addEventListener("message", function (event: any) {
|
|
|
|
const { eventType, eventData, eventId } = event.data;
|
|
|
|
|
|
|
|
if (eventType === "INITIALISED") {
|
|
|
|
const methods = event.data.eventData;
|
|
|
|
|
|
|
|
methods.forEach((method: any) => {
|
|
|
|
proxy[method] = (...args: any[]) => {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
worker.postMessage({
|
|
|
|
eventType: "CALL",
|
|
|
|
eventData: {
|
|
|
|
method: method,
|
|
|
|
arguments: Array.from(args),
|
|
|
|
},
|
|
|
|
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: any) {
|
|
|
|
reject(error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-12-11 13:21:08 +00:00
|
|
|
export default App;
|