aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/test/canvas/CanvasCopyLib.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/test/canvas/CanvasCopyLib.ts')
-rw-r--r--src/lib/test/canvas/CanvasCopyLib.ts113
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);
+ }
+};