Skip to content

Commit

Permalink
[Data URL] Add Output Format and Quality to the Image (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
kriserickson authored Nov 14, 2023
1 parent add8ca1 commit e3b86ed
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 4 deletions.
86 changes: 83 additions & 3 deletions app/components/read-file.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,72 @@ interface Props {
readonly accept: string;
readonly onLoad: (value: string) => void;
readonly type?: "text" | "dataURL";
readonly format?: string;
readonly quality?: string;
}

export default function ReadFile({ accept, onLoad, type = "text" }: Props) {
const imageFormats: Record<string, string> = {
"image/jpeg": "jpg",
"image/png": "png",
"image/webp": "webp",
};

const formatImage: Record<string, string> = {
jpg: "image/jpeg",
png: "image/png",
webp: "image/webp",
};

function convertToFileFormat(
file: File,
format: string,
quality: number,
): Promise<string> {
return new Promise(
(
resolve: (dataUrl: string) => void,
reject: (error: string) => void,
): void => {
const img = new Image();

// Set up an onload event handler to execute the conversion when the image is loaded
img.onload = () => {
// Create a canvas element
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;

// Get the 2D context of the canvas
const ctx = canvas.getContext("2d");

if (ctx) {
// Draw the image onto the canvas
ctx.drawImage(img, 0, 0);
} else {
reject("Unable to get context");
}

// Convert the canvas content to a data URL (PNG format)
if (quality !== 0) {
resolve(canvas.toDataURL(formatImage[format], quality));
} else {
resolve(canvas.toDataURL(formatImage[format]));
}
};

// Set the source of the image to the JPEG file
img.src = URL.createObjectURL(file);
},
);
}

export default function ReadFile({
accept,
onLoad,
type = "text",
format = "jpg",
quality = "0",
}: Props) {
const onButtonClick = useCallback(
() => document.getElementById("file-input")?.click(),
[],
Expand Down Expand Up @@ -42,11 +105,28 @@ export default function ReadFile({ accept, onLoad, type = "text" }: Props) {
reader.readAsText(file);
break;
case "dataURL":
reader.readAsDataURL(file);
const fileFormat = imageFormats[file.type];
if (
(fileFormat && fileFormat !== format) ||
(quality != "0" && format !== "png")
) {
convertToFileFormat(file, format || "jpg", parseFloat(quality))
.then((dataUrl) => {
onLoad(dataUrl);
})
.catch((_) => {
console.error(
"Something went wrong, trying plain old read as dataurl",
);
reader.readAsDataURL(file);
});
} else {
reader.readAsDataURL(file);
}
break;
}
},
[onLoad, type],
[onLoad, type, format, quality],
);

return (
Expand Down
30 changes: 29 additions & 1 deletion app/routes/dataurl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ async function display(text: string): Promise<string> {

export default function DataUrl() {
const [input, setInput] = useState("");
const [format, setFormat] = useState("jpg");
const [quality, setQuality] = useState("0");
const [output, setOutput] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);

Expand Down Expand Up @@ -92,7 +94,33 @@ export default function DataUrl() {

<BoxButtons>
<div>
<ReadFile accept="image/*" onLoad={onLoad} type="dataURL" />
<ReadFile
accept="image/*"
onLoad={onLoad}
type="dataURL"
format={format}
quality={quality}
/>
<select onChange={(e) => setFormat(e.target.value)} value={format}>
<option value="jpg">Jpeg</option>
<option value="png">Png</option>
<option value="webp">Webp</option>
</select>
{format !== "png" ? (
<select
onChange={(e) => setQuality(e.target.value)}
value={quality.toString()}
>
<option value="0">Default</option>
<option value="1">Full (100%)</option>
<option value=".9">Very High (90%)</option>
<option value=".8">High (80%)</option>
<option value=".75">Good (75%)</option>
<option value=".6">Medium (60%)</option>
<option value=".5">Low (50%)</option>
<option value=".25">Poor (25%)</option>
</select>
) : null}
</div>
<div className="flex gap-x-2">
<Button onClick={onClick} label="Display" />
Expand Down

0 comments on commit e3b86ed

Please sign in to comment.