Merge pull request #8 from CrispyBaguette/feat/image-blob
feat/image blob
This commit is contained in:
commit
42cf96cb53
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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 (
|
||||||
|
@ -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 (
|
||||||
|
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user