Chore: improve the /dither page

This commit is contained in:
wilson 2026-03-09 21:05:49 +00:00
parent 03ede2c023
commit 6343bddd18
2 changed files with 43 additions and 47 deletions

View file

@ -14,22 +14,37 @@
let offscreenCanvas: OffscreenCanvas; let offscreenCanvas: OffscreenCanvas;
let ctx = $derived(canvas.getContext('2d')) let ctx = $derived(canvas.getContext('2d'))
let offscreenCtx: OffscreenCanvasRenderingContext2D; let offscreenCtx: OffscreenCanvasRenderingContext2D;
let imgObjectUrl = $state('') let selectedImage: HTMLImageElement | null = $state(null)
let errorMessage = $state('') let errorMessage = $state('')
let selectedAlgorithm = $state('atkinson')
let maxImageWidth = $state(0)
let imageHeightRatio = $state(0)
let imageWidthPc = $state(100)
onMount(() => { onMount(() => {
offscreenCanvas = new OffscreenCanvas(1000, 1000) offscreenCanvas = new OffscreenCanvas(1000, 1000)
offscreenCtx = offscreenCanvas.getContext('2d') offscreenCtx = offscreenCanvas.getContext('2d')
}) })
const doesCtxExist = () => { $effect(() => {
if (!ctx) { if (!selectedImage) {
errorMessage = "Problem finding context for on-screen canvas" console.log(`Selected Image is null, terminating effect...`)
return false return;
} }
return true; maxImageWidth = selectedImage.width
} imageHeightRatio = selectedImage.height / selectedImage.width
ctx.clearRect(0, 0, canvas.width, canvas.height)
canvas.width = maxImageWidth * (imageWidthPc/100)
canvas.height = imageHeightRatio * canvas.width
ctx.drawImage(selectedImage, 0, 0, canvas.width, canvas.height)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
ctx.putImageData(DITHERING_ALGORITHMS[selectedAlgorithm](imageData), 0, 0)
})
const handleImageChange = async (event: any) => { const handleImageChange = async (event: any) => {
const file = event.target.files[0]; const file = event.target.files[0];
@ -38,18 +53,8 @@
return return
} }
imgObjectUrl = URL.createObjectURL(file); const imgObjectUrl = URL.createObjectURL(file);
const image = await createImage(imgObjectUrl); selectedImage = await createImage(imgObjectUrl);
// Change the sizes of canvases
canvas.width = image.width
canvas.height = image.height
offscreenCanvas.height = image.height
offscreenCanvas.width = image.width
// Now draw the images
drawImage(image)
} }
async function createImage(objectUrl: string): Promise<HTMLImageElement> { async function createImage(objectUrl: string): Promise<HTMLImageElement> {
@ -63,30 +68,7 @@
}); });
} }
const clearCanvases = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
}
const drawImage = (theImage: HTMLImageElement) => {
if (!doesCtxExist()) return
clearCanvases()
ctx.drawImage(theImage, 0, 0, canvas.width, canvas.height);
offscreenCtx.drawImage(theImage, offscreenCanvas.width, offscreenCanvas.height)
}
function applyDitheringAlgorithm(algorithm: (imageData: ImageData) => ImageData) {
return async () => {
const freshImage = await createImage(imgObjectUrl);
drawImage(freshImage)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const ditheredImageData = algorithm(imageData);
ctx.putImageData(ditheredImageData, 0, 0);
}
}
function saveImage() { function saveImage() {
if (!canvas) return;
const link = document.createElement('a'); const link = document.createElement('a');
link.download = 'dithered-image.png'; link.download = 'dithered-image.png';
link.href = canvas.toDataURL(); link.href = canvas.toDataURL();
@ -95,7 +77,7 @@
</script> </script>
<ErrorMessage message={errorMessage} /> <ErrorMessage errorMessage={errorMessage} />
<h2 class="page-dither__subtitle">1: Select a file</h2> <h2 class="page-dither__subtitle">1: Select a file</h2>
@ -108,9 +90,9 @@
<h2 class="page-dither__subtitle">2: Select a dithering algorithm</h2> <h2 class="page-dither__subtitle">2: Select a dithering algorithm</h2>
<ul class="algorithms"> <ul class="algorithms">
{#each Object.entries(DITHERING_ALGORITHMS) as [key, value]} {#each Object.keys(DITHERING_ALGORITHMS) as key}
<li class="algorithms__item"> <li class="algorithms__item">
<button class="algorithms__button" onclick={applyDitheringAlgorithm(value)}>{key}</button> <button class="algorithms__button" onclick={() => selectedAlgorithm = key}>{key}</button>
</li> </li>
{/each} {/each}
</ul> </ul>
@ -120,9 +102,20 @@
<canvas id="photo-canvas" height="250" bind:this={canvas} onload={() => ctx = canvas.getContext('2d')}></canvas> <canvas id="photo-canvas" height="250" bind:this={canvas} onload={() => ctx = canvas.getContext('2d')}></canvas>
</div> </div>
<h2 class="page-dither__subtitle">
3: Remix
</h2>
<form>
<label for="resolution">
Image Ratio
<input type="range" min="1" max="100" step="5" bind:value={imageWidthPc} width="100%"/>
</label>
<p>{maxImageWidth * imageWidthPc / 100}</p>
</form>
<h2 class="page-dither__subtitle"> <h2 class="page-dither__subtitle">
3: Save the file 4: Save the file
</h2> </h2>
<button onclick={saveImage}>Save Image</button> <button onclick={saveImage}>Save Image</button>

View file

@ -1,5 +1,8 @@
<script lang="ts"> <script lang="ts">
const { errorMessage } = $props() interface Props {
errorMessage: string;
}
const { errorMessage }: Props = $props()
</script> </script>
{#if errorMessage} {#if errorMessage}