You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg,
criminalName = "UNKNOWN SUBJECT",
planetOfOrigin = "TERRA PRIME", // Or "Homeworld" in text
threatLevel = "HIGH", // Or "Threat Assessment" in text
wantedTextColor = "#FF0000",
infoTextColor = "#00FF00",
backgroundColor1 = "#111122",
backgroundColor2 = "#333344",
fontFamily = "Orbitron, sans-serif") {
const canvasWidth = 600;
const canvasHeight = 800;
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
// --- Font Loading ---
// Extracts the primary font name (e.g., "Orbitron" from "Orbitron, sans-serif")
const primaryFont = fontFamily.split(',')[0].trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1');
if (primaryFont === "Orbitron") { // Specific handling for loading Orbitron from CDN
const FONT_URL_ORBITRON = 'https://fonts.gstatic.com/s/orbitron/v25/yMJRMIlzdpvBhQQL_Qq7dy0.woff2'; // Orbitron Regular 400
try {
// Check if the font is already loaded or available in the system
// Need to use quotes for font names with spaces, but Orbitron doesn't have them.
// Using `primaryFont` directly if it doesn't contain spaces.
const fontCheckString = primaryFont.includes(" ") ? `12px "${primaryFont}"` : `12px ${primaryFont}`;
if (!document.fonts.check(fontCheckString)) {
const fontFace = new FontFace(primaryFont, `url(${FONT_URL_ORBITRON})`, { style: 'normal', weight: '400' });
await fontFace.load();
document.fonts.add(fontFace);
// console.log(`${primaryFont} font loaded successfully.`);
} else {
// console.log(`${primaryFont} font already available.`);
}
} catch (error) {
console.error(`Failed to load font '${primaryFont}' from CDN:`, error);
// Browser will use fallback fonts specified in the fontFamily string (e.g., ", sans-serif")
}
}
// For other fonts, it's assumed they are system fonts or user handles loading.
// 1. --- Draw Background ---
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
gradient.addColorStop(0, backgroundColor1);
gradient.addColorStop(1, backgroundColor2);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// 2. --- Process and Draw Mugshot Image ---
const tempCanvas = document.createElement('canvas');
tempCanvas.width = originalImg.width;
tempCanvas.height = originalImg.height;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); // Hint for performance
tempCtx.drawImage(originalImg, 0, 0); // Draw original image first
try {
// Attempt Grayscale Conversion
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Luminosity formula for grayscale: Y = 0.299R + 0.587G + 0.114B
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
data[i] = gray; // Red
data[i + 1] = gray; // Green
data[i + 2] = gray; // Blue
// Alpha (data[i+3]) is preserved
}
tempCtx.putImageData(imageData, 0, 0);
} catch (e) {
console.warn("Could not process image for grayscale (image might be CORS tainted or other error). Using original colors.", e);
// If getImageData fails (e.g. CORS), tempCanvas still holds the original colored image.
// No need to redraw, as it was drawn before the try block.
}
// Calculate display dimensions for the image
const imgAspectRatio = originalImg.width / originalImg.height;
let imgDisplayWidth, imgDisplayHeight;
const mugshotAreaWidth = canvasWidth * 0.75;
const mugshotAreaHeight = canvasHeight * 0.45;
if (mugshotAreaWidth / mugshotAreaHeight > imgAspectRatio) {
imgDisplayHeight = mugshotAreaHeight;
imgDisplayWidth = imgDisplayHeight * imgAspectRatio;
} else {
imgDisplayWidth = mugshotAreaWidth;
imgDisplayHeight = imgDisplayWidth / imgAspectRatio;
}
const imgX = (canvasWidth - imgDisplayWidth) / 2;
const imgY = canvasHeight * 0.18;
// Draw processed image (grayscale or original color if grayscale failed)
ctx.drawImage(tempCanvas, imgX, imgY, imgDisplayWidth, imgDisplayHeight);
// Draw border around the image
ctx.strokeStyle = infoTextColor;
ctx.lineWidth = 4;
// Stroke a rect slightly larger so border is outside the image
ctx.strokeRect(
imgX - ctx.lineWidth / 2,
imgY - ctx.lineWidth / 2,
imgDisplayWidth + ctx.lineWidth,
imgDisplayHeight + ctx.lineWidth
);
// 3. --- Draw Text Overlays ---
ctx.fillStyle = infoTextColor; // Default for most info text
// "WANTED" Text
ctx.font = `bold 64px ${fontFamily}`; // Use the full font-family string
ctx.fillStyle = wantedTextColor;
ctx.textAlign = 'center';
ctx.fillText("WANTED", canvasWidth / 2, 70);
// Criminal Name
ctx.font = `bold 32px ${fontFamily}`;
ctx.fillStyle = infoTextColor; // Back to info color
ctx.fillText(criminalName.toUpperCase(), canvasWidth / 2, imgY + imgDisplayHeight + 45);
// Details Section
ctx.font = `18px ${fontFamily}`;
ctx.textAlign = 'left';
let textY = imgY + imgDisplayHeight + 90;
const lineSpacing = 28;
const textX = canvasWidth * 0.1;
const textMaxWidth = canvasWidth * 0.8;
const criminalID = `XG-${Math.random().toString(36).substring(2, 6).toUpperCase()}-${Math.floor(Math.random() * 9000 + 1000)}`;
ctx.fillText(`IDENTIFICATION: ${criminalID}`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`CLASSIFICATION: EXTRATERRESTRIAL HOSTILE`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`HOMEWORLD: ${planetOfOrigin.toUpperCase()}`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`THREAT ASSESSMENT: ${threatLevel.toUpperCase()}`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`KNOWN ASSOCIATES: CLASSIFIED / NONE`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`LAST KNOWN QUADRANT: ZETA-9 (FORBIDDEN ZONE)`, textX, textY, textMaxWidth);
textY += lineSpacing;
ctx.fillText(`BOUNTY: 7,500,000 GALACTIC CREDITS (ALIVE ONLY)`, textX, textY, textMaxWidth);
// Footer Text
ctx.textAlign = 'center';
ctx.font = `italic 16px ${fontFamily}`;
ctx.fillStyle = 'rgba(150, 255, 150, 0.7)'; // Dimmer green for less important text
ctx.fillText("--- INTERGALACTIC CRIMINAL DATABASE // OFFICIAL USE ONLY ---", canvasWidth / 2, canvasHeight - 25);
// 4. --- Optional Effects (Scanlines) ---
ctx.strokeStyle = 'rgba(100, 255, 100, 0.08)'; // Faint green scanlines
ctx.lineWidth = 1;
for (let yScan = 0; yScan < canvasHeight; yScan += 3) {
ctx.beginPath();
ctx.moveTo(0, yScan);
ctx.lineTo(canvasWidth, yScan);
ctx.stroke();
}
// 5. --- Overall Border for the canvas ---
const mainBorderWidth = 6;
ctx.strokeStyle = infoTextColor;
ctx.lineWidth = mainBorderWidth;
// Draw border inset so it's fully visible
ctx.strokeRect(
mainBorderWidth / 2,
mainBorderWidth / 2,
canvasWidth - mainBorderWidth,
canvasHeight - mainBorderWidth
);
return canvas;
}
Apply Changes