Files
inkclip/src/lib/contexts/rendered.svelte.ts
T
daylily ccdb91a243 Multiple refactors
- Use aria-labelledby instead of aria-label where possible
- Replace magic numbers with explicitly defined constants
- Add input textboxes for values previously only changed with sliders
2025-04-03 18:06:00 -04:00

64 lines
1.8 KiB
TypeScript

import { getContext, setContext } from 'svelte'
import type { ImageContext } from './image.svelte'
import { withTransform } from '$lib/image/transform'
import type { ConversionConfig } from './config.svelte'
import { Scaler } from '$lib/image/scaler'
import { Quantizer } from '$lib/image/quantizer'
import { INKCLIP_HEIGHT, INKCLIP_WIDTH } from '$lib/constants'
export interface RenderedContext {
rendered: number[] | null
}
export const RenderedContextToken = Symbol('rendered')
export function getRenderedContext(): Readonly<RenderedContext> {
return getContext(RenderedContextToken)
}
const scaler = new Scaler(INKCLIP_WIDTH, INKCLIP_HEIGHT)
export function createRenderedContext(
imageCtx: Readonly<ImageContext>,
config: ConversionConfig
): Readonly<RenderedContext> {
const ctx: RenderedContext = $state({
rendered: null,
})
const quantizer = $derived(
new Quantizer({
ditheringKernel: config.ditheringKernel,
contrast: config.contrast,
bias: config.bias,
})
)
const canvas = new OffscreenCanvas(INKCLIP_WIDTH, INKCLIP_HEIGHT)
const canvasCtx = canvas.getContext('2d', {
willReadFrequently: true,
})!
$effect(() => {
const bitmap = imageCtx.image
if (bitmap === null) {
ctx.rendered = null
return
}
withTransform(canvasCtx, config.transform, () => {
const bg = config.backgroundColor
canvasCtx.fillStyle = `rgb(${bg} ${bg} ${bg})`
canvasCtx.fillRect(0, 0, INKCLIP_WIDTH, INKCLIP_HEIGHT)
const { dx, dy, dWidth, dHeight } = scaler.scale(config.scaleMode, bitmap)
canvasCtx.drawImage(bitmap, dx, dy, dWidth, dHeight)
})
const quantizedData = quantizer.reduce(canvasCtx)
ctx.rendered = quantizedData
})
return setContext(RenderedContextToken, ctx)
}