feat/image blob #8

Merged
CrispyBaguette merged 2 commits from feat/image-blob into master 2021-12-13 12:59:30 +00:00
5 changed files with 34 additions and 29 deletions

View File

@ -24,7 +24,7 @@
"web-vitals": "^1.1.2" "web-vitals": "^1.1.2"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "BROWSER=none react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"

View File

@ -12,17 +12,18 @@ enum AppState {
} }
function App() { function App() {
const [baseImage, setBaseImage] = React.useState<Uint8ClampedArray>(); const [baseImage, setBaseImage] = React.useState<Blob>();
const [ditheredImage, setDitheredImage] = React.useState<Uint8ClampedArray>(); const [ditheredImage, setDitheredImage] = React.useState<Blob>();
const [appState, setAppState] = React.useState<AppState>(AppState.NO_IMAGE); const [appState, setAppState] = React.useState<AppState>(AppState.NO_IMAGE);
const handleImageSubmit = async (data: Uint8ClampedArray) => { const handleImageSubmit = async (data: Blob) => {
setBaseImage(data); setBaseImage(data);
setAppState(AppState.IMAGE_LOADED); setAppState(AppState.IMAGE_LOADED);
try { try {
const ditheredImage = await new Ditherer().dither(data); const imageArray = new Uint8ClampedArray(await data.arrayBuffer());
setDitheredImage(ditheredImage); const ditheredImage = await new Ditherer().dither(imageArray);
setDitheredImage(new Blob([ditheredImage], { type: "image/png" }));
setAppState(AppState.IMAGE_PROCESSED); setAppState(AppState.IMAGE_PROCESSED);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -50,11 +51,11 @@ function App() {
</p> </p>
</article> </article>
<ImageInput onImageSubmit={handleImageSubmit}></ImageInput> <ImageInput onImageSubmit={handleImageSubmit}></ImageInput>
{appState === AppState.IMAGE_LOADED && ( {appState === AppState.IMAGE_LOADED && baseImage && (
<ImagePreview imageData={baseImage as Uint8ClampedArray} /> <ImagePreview imageData={baseImage} />
)} )}
{appState === AppState.IMAGE_PROCESSED && ( {appState === AppState.IMAGE_PROCESSED && ditheredImage && (
<ImageOutput imageData={ditheredImage as Uint8ClampedArray} /> <ImageOutput imageData={ditheredImage} />
)} )}
</main> </main>
</div> </div>

View File

@ -1,11 +1,10 @@
import React, { FormEventHandler } from "react"; import React, { FormEventHandler } from "react";
interface Props { interface Props {
onImageSubmit: (image: Uint8ClampedArray) => void; onImageSubmit: (image: Blob) => void;
} }
function ImageInput({ onImageSubmit }: Props) { function ImageInput({ onImageSubmit }: Props) {
let fileReader: FileReader;
const fileInputRef = React.useRef<HTMLInputElement>(null); const fileInputRef = React.useRef<HTMLInputElement>(null);
const handleSubmit: FormEventHandler = (e) => { const handleSubmit: FormEventHandler = (e) => {
@ -18,16 +17,7 @@ function ImageInput({ onImageSubmit }: Props) {
return; return;
} }
fileReader = new FileReader(); onImageSubmit(fileInputRef.current.files[0]);
fileReader.onloadend = handleFileRead;
fileReader.readAsArrayBuffer(fileInputRef.current.files[0]);
};
const handleFileRead: EventListener = (e) => {
if (fileReader.result) {
const image = new Uint8ClampedArray(fileReader.result as ArrayBuffer);
onImageSubmit(image);
}
}; };
return ( return (

View File

@ -1,16 +1,23 @@
import FileSaver from "file-saver"; import FileSaver from "file-saver";
import { useEffect } from "react";
import "./ImageOutput.css"; import "./ImageOutput.css";
interface OutputProps { interface OutputProps {
imageData: Uint8ClampedArray; imageData: Blob;
} }
function ImageOutput({ imageData }: OutputProps) { function ImageOutput({ imageData }: OutputProps) {
const imageBlob = new Blob([imageData], { type: "image/png" }); const imageUrl = URL.createObjectURL(imageData);
const imageUrl = URL.createObjectURL(imageBlob);
useEffect(() => {
return () => {
URL.revokeObjectURL(imageUrl);
}
}, [imageUrl]);
const handleClick = () => { const handleClick = () => {
FileSaver.saveAs(imageBlob, "image.png"); const extension = imageData.type.split("/")[1];
FileSaver.saveAs(imageData, `image.${extension}`);
}; };
return ( return (

View File

@ -1,10 +1,17 @@
import { useEffect } from "react";
interface ImagePreviewProps { interface ImagePreviewProps {
imageData: Uint8ClampedArray; imageData: Blob;
} }
function ImagePreview({ imageData }: ImagePreviewProps) { function ImagePreview({ imageData }: ImagePreviewProps) {
const imageBlob = new Blob([imageData], { type: "image/png" }); const imageUrl = URL.createObjectURL(imageData);
const imageUrl = URL.createObjectURL(imageBlob);
useEffect(() => {
return () => {
URL.revokeObjectURL(imageUrl);
}
}, [imageUrl]);
return ( return (
<div className="blur-sm mx-auto"> <div className="blur-sm mx-auto">