Compare commits

..

No commits in common. "b798a7062aae4aed606dcd62934c6f78ad70f2df" and "03ede2c02397c2c9a3770e25c406ed08b2879d03" have entirely different histories.

7 changed files with 51 additions and 138 deletions

View file

@ -1,38 +0,0 @@
import type { Cookies } from "@sveltejs/kit";
export class CookieAuthentication {
private readonly cookieValue: string;
private readonly cookieValueArray: string[];
public static cookieName = 'auth'
public static adminAuthRole = 'admin'
constructor(
private readonly cookies: Cookies,
){
this.cookieValue = this.cookies.get(CookieAuthentication.cookieName) ?? ''
this.cookieValueArray = this.cookieValue.split(',')
}
public get isAuthdAsAdmin(): boolean {
let isAuthdAsAdmin = false;
if (this.cookieValueArray.includes(CookieAuthentication.adminAuthRole)) {
isAuthdAsAdmin = true
}
return isAuthdAsAdmin
}
public setAdminAuthentication(isAuthd: boolean) {
let value = this.cookieValue
if (isAuthd) {
value = Array.from(new Set([...this.cookieValueArray, CookieAuthentication.adminAuthRole])).join(',')
} else {
value = this.cookieValueArray.filter((i) => i !== CookieAuthentication.adminAuthRole).join(',')
}
this.cookies.set(CookieAuthentication.cookieName, value, { path: '/'})
}
}

View file

@ -1,16 +0,0 @@
import { redirect } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types.js";
import { CookieAuthentication } from "$lib/blog/auth/CookieAuthentication.js";
export const load: LayoutServerLoad = ({ cookies, route }) => {
const auth = new CookieAuthentication(cookies)
const isAuthd = auth.isAuthdAsAdmin
if (route.id === '/admin/login' && isAuthd) {
return redirect(307, '/admin')
} else if (!isAuthd && route.id !== '/admin/login') {
return redirect(307, '/admin/login')
}
return {}
}

View file

@ -1 +0,0 @@
<h1>Welcome home, Wilson</h1>

View file

@ -1,25 +0,0 @@
import { PRIVATE_ADMIN_AUTH_TOKEN } from "$env/static/private";
import { redirect } from "@sveltejs/kit";
import type { Actions} from "./$types.js";
import { CookieAuthentication } from "$lib/blog/auth/CookieAuthentication.js";
export const actions = {
default: async ({cookies, request}) => {
const formData = await request.formData()
const token = formData.get('token')
const isAuthd = PRIVATE_ADMIN_AUTH_TOKEN === token;
const auth = new CookieAuthentication(cookies)
auth.setAdminAuthentication(isAuthd)
if (isAuthd) {
return redirect(307, '/admin')
}
return {
isAuthd
}
}
} satisfies Actions

View file

@ -1,11 +0,0 @@
<h1>Admin login</h1>
<form method="POST">
<div class="field">
<label for="token">API Token</label>
<input type="password" id="token" name="token" />
</div>
<div class="field">
<input type="submit" value="Login">
</div>
</form>

View file

@ -4,47 +4,32 @@
import { onMount } from 'svelte';
const DITHERING_ALGORITHMS = {
atkinson: (imageData: ImageData) => Dither.atkinson(imageData),
bayer: (imageData: ImageData) => Dither.bayer(imageData, 170),
floydSteinberg: (imageData: ImageData) => Dither.floydsteinberg(imageData),
threshold: (imageData: ImageData) => Dither.threshold(imageData, 2),
atkinson: (imageData) => Dither.atkinson(imageData),
bayer: (imageData) => Dither.bayer(imageData, 170),
floydSteinberg: (imageData) => Dither.floydsteinberg(imageData),
threshold: (imageData) => Dither.threshold(imageData, 2),
}
let canvas: HTMLCanvasElement;
let offscreenCanvas: OffscreenCanvas;
let ctx = $derived(canvas.getContext('2d'))
let offscreenCtx: OffscreenCanvasRenderingContext2D;
let selectedImage: HTMLImageElement | null = $state(null)
let imgObjectUrl = $state('')
let errorMessage = $state('')
let selectedAlgorithm = $state('atkinson')
let maxImageWidth = $state(0)
let imageHeightRatio = $state(0)
let imageWidthPc = $state(100)
onMount(() => {
offscreenCanvas = new OffscreenCanvas(1000, 1000)
offscreenCtx = offscreenCanvas.getContext('2d')
})
$effect(() => {
if (!selectedImage) {
console.log(`Selected Image is null, terminating effect...`)
return;
const doesCtxExist = () => {
if (!ctx) {
errorMessage = "Problem finding context for on-screen canvas"
return false
}
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)
})
return true;
}
const handleImageChange = async (event: any) => {
const file = event.target.files[0];
@ -53,8 +38,18 @@
return
}
const imgObjectUrl = URL.createObjectURL(file);
selectedImage = await createImage(imgObjectUrl);
imgObjectUrl = URL.createObjectURL(file);
const image = 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> {
@ -68,7 +63,30 @@
});
}
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() {
if (!canvas) return;
const link = document.createElement('a');
link.download = 'dithered-image.png';
link.href = canvas.toDataURL();
@ -77,7 +95,7 @@
</script>
<ErrorMessage errorMessage={errorMessage} />
<ErrorMessage message={errorMessage} />
<h2 class="page-dither__subtitle">1: Select a file</h2>
@ -90,9 +108,9 @@
<h2 class="page-dither__subtitle">2: Select a dithering algorithm</h2>
<ul class="algorithms">
{#each Object.keys(DITHERING_ALGORITHMS) as key}
{#each Object.entries(DITHERING_ALGORITHMS) as [key, value]}
<li class="algorithms__item">
<button class="algorithms__button" onclick={() => selectedAlgorithm = key}>{key}</button>
<button class="algorithms__button" onclick={applyDitheringAlgorithm(value)}>{key}</button>
</li>
{/each}
</ul>
@ -102,20 +120,9 @@
<canvas id="photo-canvas" height="250" bind:this={canvas} onload={() => ctx = canvas.getContext('2d')}></canvas>
</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">
4: Save the file
3: Save the file
</h2>
<button onclick={saveImage}>Save Image</button>

View file

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