'use strict'; // Generates PNG icons with a Joker card motif on dark green background. const fs = require('fs'); const path = require('path'); const zlib = require('zlib'); const outDir = path.join(__dirname, 'public', 'icons'); if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true }); function writePNG(filePath, size) { const channels = 4; const row = size * channels; const raw = Buffer.alloc(size * row); // Background: dark forest green #133025 const bgR = 0x13, bgG = 0x30, bgB = 0x25; // Gold color for Joker diamond: #f5c518 const gR = 0xf5, gG = 0xc5, gB = 0x18; const cx = size / 2, cy = size / 2; const dh = size * 0.35; // half-height of diamond const dw = size * 0.28; // half-width for (let y = 0; y < size; y++) { for (let x = 0; x < size; x++) { const px = (y * size + x) * channels; const nx = x + 0.5, ny = y + 0.5; // Rounded corners const cr = size * 0.2; const inCorner = (nx < cr && ny < cr && Math.hypot(nx - cr, ny - cr) > cr) || (nx > size - cr && ny < cr && Math.hypot(nx - (size - cr), ny - cr) > cr) || (nx < cr && ny > size - cr && Math.hypot(nx - cr, ny - (size - cr)) > cr) || (nx > size - cr && ny > size - cr && Math.hypot(nx - (size - cr), ny - (size - cr)) > cr); if (inCorner) { raw[px + 3] = 0; continue; } // Draw a diamond / rhombus shape (Joker card symbol) const dx = Math.abs(nx - cx) / dw; const dy = Math.abs(ny - cy) / dh; const inDiamond = dx + dy <= 1; if (inDiamond) { raw[px] = gR; raw[px + 1] = gG; raw[px + 2] = gB; raw[px + 3] = 255; } else { raw[px] = bgR; raw[px + 1] = bgG; raw[px + 2] = bgB; raw[px + 3] = 255; } } } const chunks = []; chunks.push(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])); function crc32(buf) { let c = 0xffffffff; const table = crc32.table || (crc32.table = (() => { const t = new Uint32Array(256); for (let i = 0; i < 256; i++) { let v = i; for (let k = 0; k < 8; k++) v = v & 1 ? 0xedb88320 ^ (v >>> 1) : v >>> 1; t[i] = v; } return t; })()); for (let i = 0; i < buf.length; i++) c = table[(c ^ buf[i]) & 0xff] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; } function chunk(type, data) { const len = Buffer.alloc(4); len.writeUInt32BE(data.length); const tp = Buffer.from(type); const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(Buffer.concat([tp, data]))); return Buffer.concat([len, tp, data, crc]); } const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(size, 0); ihdr.writeUInt32BE(size, 4); ihdr[8] = 8; ihdr[9] = 6; chunks.push(chunk('IHDR', ihdr)); const filtered = Buffer.alloc(size * (row + 1)); for (let y = 0; y < size; y++) { filtered[y * (row + 1)] = 0; raw.copy(filtered, y * (row + 1) + 1, y * row, (y + 1) * row); } chunks.push(chunk('IDAT', zlib.deflateSync(filtered))); chunks.push(chunk('IEND', Buffer.alloc(0))); fs.writeFileSync(filePath, Buffer.concat(chunks)); console.log(`${path.basename(filePath)} written (${size}×${size})`); } writePNG(path.join(outDir, 'icon-192.png'), 192); writePNG(path.join(outDir, 'icon-512.png'), 512); // SVG icon (scalable, used as favicon) const svg = ` J `; fs.writeFileSync(path.join(outDir, 'icon.svg'), svg); console.log('icon.svg written'); console.log('Icons generated.');