captcha: use an already transparent png with stroke to make the hole

This commit is contained in:
Alex Gleason 2024-10-04 15:10:42 -05:00
parent 030bfa94d6
commit cdf727e5c7
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
4 changed files with 8 additions and 21 deletions

BIN
captcha/puzzle-hole.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
captcha/puzzle-mask.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -22,13 +22,13 @@ const captchas = new TTLCache<string, Point>();
export const captchaController: AppController = async (c) => { export const captchaController: AppController = async (c) => {
const { bg, puzzle, solution } = await generateCaptcha( const { bg, puzzle, 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)), await Deno.readFile(new URL('../../../captcha/puzzle-mask.png', import.meta.url)),
await Deno.readFile(new URL('../../../captcha/puzzle-hole.png', import.meta.url)),
{ {
cw: 370, cw: 370,
ch: 400, ch: 400,
pw: 65, pw: 65,
ph: 65, ph: 65,
alpha: 0.8,
}, },
); );
@ -52,15 +52,15 @@ export const captchaController: AppController = async (c) => {
async function generateCaptcha( async function generateCaptcha(
from: Uint8Array, from: Uint8Array,
mask: Uint8Array, mask: Uint8Array,
hole: Uint8Array,
opts: { opts: {
pw: number; pw: number;
ph: number; ph: number;
cw: number; cw: number;
ch: number; ch: number;
alpha: number;
}, },
) { ) {
const { pw, ph, cw, ch, alpha } = opts; const { pw, ph, cw, ch } = opts;
const bg = createCanvas(cw, ch); const bg = createCanvas(cw, ch);
const ctx = bg.getContext('2d'); const ctx = bg.getContext('2d');
const image = await loadImage(from); const image = await loadImage(from);
@ -71,28 +71,15 @@ async function generateCaptcha(
const solution = getPieceCoords(bg.width, bg.height, pw, ph); const solution = getPieceCoords(bg.width, bg.height, pw, ph);
// Draw the piece onto the puzzle piece canvas but only where the mask allows
const maskImage = await loadImage(mask); const maskImage = await loadImage(mask);
pctx.globalCompositeOperation = 'source-over'; const holeImage = await loadImage(hole);
pctx.drawImage(maskImage, 0, 0, pw, ph); pctx.drawImage(maskImage, 0, 0, pw, ph);
pctx.globalCompositeOperation = 'source-in'; pctx.globalCompositeOperation = 'source-in';
pctx.drawImage(bg, solution.x, solution.y, pw, ph, 0, 0, pw, ph); pctx.drawImage(bg, solution.x, solution.y, pw, ph, 0, 0, pw, ph);
// Reset composite operation ctx.globalCompositeOperation = 'source-atop';
pctx.globalCompositeOperation = 'source-over'; ctx.drawImage(holeImage, solution.x, solution.y, pw, ph);
// 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 {
bg, bg,