mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
import { createCanvas, type EmulatedCanvas2D } from '@gfx/canvas-wasm';
|
|
|
|
import { addNoise } from './canvas.ts';
|
|
import { areIntersecting, type Dimensions, type Point } from './geometry.ts';
|
|
|
|
import type { CaptchaImages } from './assets.ts';
|
|
|
|
/** Generate a puzzle captcha, returning canvases for the board and piece. */
|
|
export function generateCaptcha(
|
|
{ bgImages, puzzleMask, puzzleHole }: CaptchaImages,
|
|
bgSize: Dimensions,
|
|
puzzleSize: Dimensions,
|
|
): {
|
|
bg: EmulatedCanvas2D;
|
|
puzzle: EmulatedCanvas2D;
|
|
solution: Point;
|
|
} {
|
|
const bg = createCanvas(bgSize.w, bgSize.h);
|
|
const puzzle = createCanvas(puzzleSize.w, puzzleSize.h);
|
|
|
|
const ctx = bg.getContext('2d');
|
|
const pctx = puzzle.getContext('2d');
|
|
|
|
const solution = generateSolution(bgSize, puzzleSize);
|
|
const bgImage = bgImages[Math.floor(Math.random() * bgImages.length)];
|
|
|
|
// Draw the background image.
|
|
ctx.drawImage(bgImage, 0, 0, bg.width, bg.height);
|
|
addNoise(ctx, bg.width, bg.height);
|
|
|
|
// Draw the puzzle piece.
|
|
pctx.drawImage(puzzleMask, 0, 0, puzzle.width, puzzle.height);
|
|
pctx.globalCompositeOperation = 'source-in';
|
|
pctx.drawImage(bg, solution.x, solution.y, puzzle.width, puzzle.height, 0, 0, puzzle.width, puzzle.height);
|
|
|
|
// Draw the hole.
|
|
ctx.globalCompositeOperation = 'source-atop';
|
|
ctx.drawImage(puzzleHole, solution.x, solution.y, puzzle.width, puzzle.height);
|
|
|
|
return {
|
|
bg,
|
|
puzzle,
|
|
solution,
|
|
};
|
|
}
|
|
|
|
export function verifyCaptchaSolution(puzzleSize: Dimensions, point: Point, solution: Point): boolean {
|
|
return areIntersecting(
|
|
{ ...point, ...puzzleSize },
|
|
{ ...solution, ...puzzleSize },
|
|
);
|
|
}
|
|
|
|
/** Random coordinates such that the piece fits within the canvas. */
|
|
function generateSolution(bgSize: Dimensions, puzzleSize: Dimensions): Point {
|
|
return {
|
|
x: Math.floor(Math.random() * (bgSize.w - puzzleSize.w)),
|
|
y: Math.floor(Math.random() * (bgSize.h - puzzleSize.h)),
|
|
};
|
|
}
|