Mask the puzzle piece

This commit is contained in:
Alex Gleason 2024-10-03 20:15:16 -05:00
parent 707674db7c
commit 03c9340eb2
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
2 changed files with 27 additions and 3 deletions

BIN
captcha/puzzle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -8,12 +8,13 @@ import { aesEncrypt } from '@/utils/aes.ts';
export const captchaController: AppController = async (c) => { export const captchaController: AppController = async (c) => {
const { puzzle, piece, solution } = await generateCaptcha( const { puzzle, piece, solution } = await generateCaptcha(
await Deno.readFile(new URL('../../../captcha/tj-holowaychuk.jpg', import.meta.url)), await Deno.readFile(new URL('../../../captcha/tj-holowaychuk.jpg', import.meta.url)),
await Deno.readFile(new URL('../../../captcha/puzzle.png', import.meta.url)),
{ {
cw: 300, cw: 300,
ch: 300, ch: 300,
pw: 50, pw: 50,
ph: 50, ph: 50,
alpha: 0.6, alpha: 0.8,
}, },
); );
@ -41,6 +42,7 @@ interface Point {
async function generateCaptcha( async function generateCaptcha(
from: Uint8Array, from: Uint8Array,
mask: Uint8Array,
opts: { opts: {
pw: number; pw: number;
ph: number; ph: number;
@ -54,12 +56,34 @@ async function generateCaptcha(
const ctx = puzzle.getContext('2d'); const ctx = puzzle.getContext('2d');
const image = await loadImage(from); const image = await loadImage(from);
ctx.drawImage(image, 0, 0, image.width(), image.height(), 0, 0, cw, ch); ctx.drawImage(image, 0, 0, image.width(), image.height(), 0, 0, cw, ch);
const piece = createCanvas(pw, ph); const piece = createCanvas(pw, ph);
const pctx = piece.getContext('2d'); const pctx = piece.getContext('2d');
const solution = getPieceCoords(puzzle.width, puzzle.height, pw, ph); const solution = getPieceCoords(puzzle.width, puzzle.height, pw, ph);
// Draw the piece onto the puzzle piece canvas but only where the mask allows
const maskImage = await loadImage(mask);
pctx.globalCompositeOperation = 'source-over';
pctx.drawImage(maskImage, 0, 0, pw, ph);
pctx.globalCompositeOperation = 'source-in';
pctx.drawImage(puzzle, solution.x, solution.y, pw, ph, 0, 0, pw, ph); pctx.drawImage(puzzle, solution.x, solution.y, pw, ph, 0, 0, pw, ph);
ctx.fillStyle = `rgba(0, 0, 0, ${alpha})`;
ctx.fillRect(solution.x, solution.y, pw, ph); // Reset composite operation
pctx.globalCompositeOperation = 'source-over';
// Create a temporary canvas to draw the darkened shape
const tempCanvas = createCanvas(pw, ph);
const tempCtx = tempCanvas.getContext('2d');
// Draw the darkened shape onto the temporary canvas but only where the mask allows
tempCtx.fillStyle = `rgba(0, 0, 0, ${alpha})`;
tempCtx.fillRect(0, 0, pw, ph);
tempCtx.globalCompositeOperation = 'destination-in';
tempCtx.drawImage(maskImage, 0, 0, pw, ph);
// Draw the temporary canvas onto the puzzle at the piece's location
ctx.drawImage(tempCanvas, solution.x, solution.y, pw, ph);
return { return {
puzzle, puzzle,