2021-12-11 15:51:45 +00:00
|
|
|
import React from "react";
|
2021-12-12 16:50:58 +00:00
|
|
|
import ImageInput from "./ImageInput";
|
|
|
|
import ImageOutput from "./ImageOutput";
|
2021-12-12 17:27:19 +00:00
|
|
|
import Ditherer from "./lib/Ditherer";
|
2021-12-12 16:50:58 +00:00
|
|
|
import ImagePreview from "./ImagePreview";
|
|
|
|
import Header from "./Header";
|
2021-12-13 22:15:11 +00:00
|
|
|
import Palette from "./Palette";
|
2021-12-12 16:50:58 +00:00
|
|
|
|
|
|
|
enum AppState {
|
|
|
|
NO_IMAGE,
|
|
|
|
IMAGE_LOADED,
|
|
|
|
IMAGE_PROCESSED,
|
|
|
|
}
|
2021-12-11 13:21:08 +00:00
|
|
|
|
|
|
|
function App() {
|
2021-12-13 12:54:54 +00:00
|
|
|
const [baseImage, setBaseImage] = React.useState<Blob>();
|
|
|
|
const [ditheredImage, setDitheredImage] = React.useState<Blob>();
|
2021-12-12 16:50:58 +00:00
|
|
|
const [appState, setAppState] = React.useState<AppState>(AppState.NO_IMAGE);
|
|
|
|
|
2021-12-13 22:15:11 +00:00
|
|
|
const handleImageSubmit = async (data: Blob, palette: Palette) => {
|
2021-12-12 16:50:58 +00:00
|
|
|
setBaseImage(data);
|
|
|
|
setAppState(AppState.IMAGE_LOADED);
|
2021-12-12 17:27:19 +00:00
|
|
|
|
|
|
|
try {
|
2021-12-13 12:54:54 +00:00
|
|
|
const imageArray = new Uint8ClampedArray(await data.arrayBuffer());
|
2021-12-13 22:15:11 +00:00
|
|
|
const ditheredImage = await new Ditherer().dither(imageArray, palette);
|
2021-12-13 12:54:54 +00:00
|
|
|
setDitheredImage(new Blob([ditheredImage], { type: "image/png" }));
|
2021-12-12 17:27:19 +00:00
|
|
|
setAppState(AppState.IMAGE_PROCESSED);
|
2023-10-08 09:25:41 +00:00
|
|
|
|
|
|
|
if ("umami" in window) {
|
|
|
|
(window as any).umami.track("image processed");
|
|
|
|
}
|
2021-12-12 17:27:19 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
window.alert("Something went wrong. Please try again.");
|
2023-10-08 09:25:41 +00:00
|
|
|
if ("umami" in window) {
|
|
|
|
(window as any).umami.track("processing error");
|
|
|
|
}
|
2021-12-12 17:27:19 +00:00
|
|
|
}
|
2021-12-11 15:19:48 +00:00
|
|
|
};
|
|
|
|
|
2021-12-11 13:21:08 +00:00
|
|
|
return (
|
2022-05-08 13:16:39 +00:00
|
|
|
<div className="bg-nord-6 dark:bg-nord-0 text-nord-0 dark:text-nord-6 min-h-screen">
|
2021-12-12 16:50:58 +00:00
|
|
|
<Header />
|
2021-12-13 22:15:11 +00:00
|
|
|
<main className="container mx-auto pb-5">
|
|
|
|
<article className="text-xl leading-relaxed max-w-prose space-y-2 mx-auto pb-5 px-2">
|
2021-12-12 16:50:58 +00:00
|
|
|
<h1 className="text-3xl text-center pb-3">
|
2024-01-15 17:59:40 +00:00
|
|
|
Palette Switcher
|
2021-12-12 16:50:58 +00:00
|
|
|
</h1>
|
|
|
|
<p>
|
2021-12-13 22:15:11 +00:00
|
|
|
Load an image, select a palette, click Go and wait for the image to
|
|
|
|
be processed using the Floyd-Steinberg algorithm.
|
2021-12-12 16:50:58 +00:00
|
|
|
</p>
|
|
|
|
<p>
|
2021-12-13 22:15:11 +00:00
|
|
|
The preview image is scaled using the nearest-neighbor algorithm,
|
|
|
|
which might cause artifacts in some cases. Download the image for
|
|
|
|
the best experience.
|
2021-12-12 16:50:58 +00:00
|
|
|
</p>
|
|
|
|
</article>
|
|
|
|
<ImageInput onImageSubmit={handleImageSubmit}></ImageInput>
|
2021-12-13 12:54:54 +00:00
|
|
|
{appState === AppState.IMAGE_LOADED && baseImage && (
|
|
|
|
<ImagePreview imageData={baseImage} />
|
2021-12-12 16:50:58 +00:00
|
|
|
)}
|
2021-12-13 12:54:54 +00:00
|
|
|
{appState === AppState.IMAGE_PROCESSED && ditheredImage && (
|
|
|
|
<ImageOutput imageData={ditheredImage} />
|
2021-12-12 16:50:58 +00:00
|
|
|
)}
|
|
|
|
</main>
|
2021-12-11 15:19:48 +00:00
|
|
|
</div>
|
2021-12-11 13:21:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default App;
|