diff options
feat: figured i'd revisit this code i wrote as a shitpost, it seems quite decent actually
Diffstat (limited to 'src/lib/test/canvas/CanvasCopyLib.ts')
| -rw-r--r-- | src/lib/test/canvas/CanvasCopyLib.ts | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/lib/test/canvas/CanvasCopyLib.ts b/src/lib/test/canvas/CanvasCopyLib.ts new file mode 100644 index 0000000..96d4e3d --- /dev/null +++ b/src/lib/test/canvas/CanvasCopyLib.ts @@ -0,0 +1,113 @@ +import { detect2dCanvasBlockOnCanvas } from './detect-canvas-block'; + +export const shuffleInPlace = <T extends any[]>(a: T): T => { + a.sort(() => Math.random() - 0.5); + return a; +}; +export const canvascopy = async ( + image: HTMLImageElement, + artifacts: 'fix' | 'keep' = 'fix', + getBlobURL: () => string | undefined, + setBlobURL: (url: string | undefined) => void, + tick: () => Promise<void>, + newBlob: (blob: string) => void, + getAndRemoveRandomBlob: () => string, + betweenSegments: undefined | ((ourPromise: Promise<string>) => Promise<void> | void) +) => { + console.debug('[canvascopy] Entering canvascopy'); + const canvas = document.createElement('canvas'); + const canvasOpts = { + desynchronized: true, + alpha: true, + willReadFrequently: true, + // change to unorm8 to use 8-bit colours (will break HDR images) + colorType: 'float16' as const, + // change to srgb if colours look off + colorSpace: 'display-p3' as const, + } as const; + const e = detect2dCanvasBlockOnCanvas(canvas, false, { + ...canvasOpts, + willReadFrequently: false, + }); + if (e) throw e; + console.debug('[canvascopy] Canvas OK'); + try { + // See https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/naturalHeight + const canvasWidth = (canvas.width = image.naturalWidth); + const canvasHeight = (canvas.height = image.naturalHeight); + + const ctx = canvas.getContext('2d', canvasOpts)!; + + const doIntermittentBlob = true; + + if (doIntermittentBlob) { + ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight); + // First, convert to blob, make sure that goes well + setBlobURL(URL.createObjectURL( + await new Promise<Blob>((rs, rj) => + canvas.toBlob((value) => + value ? rs(value) : rj(new Error('No blob')), + ), + ), + )); + + await tick(); + } + + // TODO: For GNOME Web, we need to check if drawImage is implemented + + const oldBlob = doIntermittentBlob ? getBlobURL() : void 0; + + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + + const segmentPromises = [] as Promise<void>[]; + + const xSegments = 8, ySegments = 4; + const xSegmentWidth = canvasWidth / xSegments, ySegmentHeight = canvasHeight / ySegments; + const pad = artifacts === 'fix' ? 4 : 0; + for (let x = 0; x < xSegments; x++) + for (let y = 0; y < ySegments; y++) { + const padLeft = x === 0 ? 0 : pad; + const padRight = x === xSegments - 1 ? 0 : pad; + const padTop = y === 0 ? 0 : pad; + const padBottom = y === ySegments - 1 ? 0 : pad; + const xStart = x * xSegmentWidth - padLeft, + xWidth = xSegmentWidth + padLeft + padRight, + yStart = y * ySegmentHeight - padTop, + yHeight = ySegmentHeight + padTop + padBottom; + ctx.drawImage( + image, + xStart, + yStart, + xWidth, + yHeight, + xStart, + yStart, + xWidth, + yHeight, + ); + const segmentPromise = new Promise<string>((rs, rj) => + canvas.toBlob((value) => + value + ? rs(URL.createObjectURL(value)) + : rj(new Error('No blob')), + ), + ); + segmentPromises.push(segmentPromise.then(v => newBlob(v))); + console.debug('[canvascopy] Push overlayed blob'); + if (betweenSegments) await betweenSegments(segmentPromise) + ctx.clearRect(xStart, yStart, xWidth, yHeight); + } + + await Promise.all(segmentPromises) + + if (doIntermittentBlob) await tick(); + + setBlobURL(getAndRemoveRandomBlob()); + if (doIntermittentBlob) URL.revokeObjectURL(oldBlob!); + + console.debug('[canvascopy] Done!'); + } catch (error) { + console.error('[canvascopy] Failed to get canvas data:', error); + } +}; |