You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, numKnights = "12", enableCaseStamp = "1", tableColor = "#3E2723", shieldColor = "#B71C1C") {
// Parse parameters
const knightsCount = parseInt(numKnights, 10) || 12;
const showStamp = parseInt(enableCaseStamp, 10) === 1;
// Set canvas dimensions based on the shortest side of the original image to make a perfect square
const size = Math.min(originalImg.width, originalImg.height);
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
const cx = size / 2;
const cy = size / 2;
const borderWidth = size * 0.05;
const knightRadius = size * 0.04;
const radius = (size / 2) - borderWidth - knightRadius * 1.5;
// 1. Draw "Case File" background (Parchment/Dark paper style)
ctx.fillStyle = '#1a1816'; // Very dark vintage background
ctx.fillRect(0, 0, size, size);
// Draw subtle grid/folder lines for the "Case File" feel
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
for(let i = 0; i <= size; i += size / 20) {
ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, size); ctx.stroke();
ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(size, i); ctx.stroke();
}
// 2. Prepare the "Round Table" mask and crop original image into it
ctx.save();
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
// Source cropping to center the original image
const sx = (originalImg.width - size) / 2;
const sy = (originalImg.height - size) / 2;
ctx.drawImage(originalImg, sx, sy, size, size, 0, 0, size, size);
// Apply a vintage/mystery sepiatone filter to the image segment
try {
const imgData = ctx.getImageData(0, 0, size, size);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i]; const g = data[i + 1]; const b = data[i + 2];
// Vintage / mystery sepia
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189)); // red
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168)); // green
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131)); // blue
}
ctx.putImageData(imgData, 0, 0);
} catch (e) {
// Fallback or ignore if canvas is tainted by CORS
console.warn("Could not apply sepia filter due to CORS restrictions:", e);
}
// Add a dark inner vignette to the image
const gradient = ctx.createRadialGradient(cx, cy, radius * 0.6, cx, cy, radius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.8)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, size, size);
ctx.restore();
// 3. Draw the "Round Table" border
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.lineWidth = borderWidth;
ctx.strokeStyle = tableColor;
ctx.stroke();
// Metallic gold rings on outer and inner edge of table
ctx.lineWidth = size * 0.008;
ctx.strokeStyle = '#D4AF37'; // Gold
ctx.beginPath();
ctx.arc(cx, cy, radius - borderWidth / 2, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, radius + borderWidth / 2, 0, Math.PI * 2);
ctx.stroke();
// 4. Draw the Knights (Shields around the table)
for (let k = 0; k < knightsCount; k++) {
const angle = (k * Math.PI * 2) / knightsCount - Math.PI / 2;
const dist = radius + borderWidth / 2 + knightRadius * 0.8;
const kx = cx + Math.cos(angle) * dist;
const ky = cy + Math.sin(angle) * dist;
// Base Shield background
ctx.beginPath();
ctx.arc(kx, ky, knightRadius, 0, Math.PI * 2);
ctx.fillStyle = shieldColor;
ctx.fill();
ctx.lineWidth = size * 0.005;
ctx.strokeStyle = '#D4AF37'; // Gold border
ctx.stroke();
// Shield Cross Symbol
ctx.beginPath();
ctx.moveTo(kx, ky - knightRadius * 0.6);
ctx.lineTo(kx, ky + knightRadius * 0.6);
ctx.moveTo(kx - knightRadius * 0.6, ky);
ctx.lineTo(kx + knightRadius * 0.6, ky);
ctx.lineWidth = size * 0.006;
ctx.strokeStyle = '#FFFFFF';
ctx.stroke();
// Optional: Radial lines linking shields to the center (table divisions)
ctx.beginPath();
ctx.moveTo(cx + Math.cos(angle) * (radius - borderWidth/2), cy + Math.sin(angle) * (radius - borderWidth/2));
ctx.lineTo(cx + Math.cos(angle) * (radius + borderWidth/2), cy + Math.sin(angle) * (radius + borderWidth/2));
ctx.lineWidth = size * 0.004;
ctx.strokeStyle = '#111'; // Dark crack/division in wood
ctx.stroke();
}
// 5. Apply the "Case" Stamp (Дело) for the thematic touch
if (showStamp) {
ctx.save();
// Position stamp in the top left corner
ctx.translate(size * 0.15, size * 0.15);
ctx.rotate(-Math.PI / 8);
const fontSize = Math.floor(size * 0.06);
ctx.font = `bold ${fontSize}px Courier New, Courier, monospace`;
ctx.fillStyle = "rgba(180, 20, 20, 0.85)";
ctx.strokeStyle = "rgba(180, 20, 20, 0.85)";
ctx.textBaseline = "top";
ctx.lineWidth = Math.max(2, size * 0.004);
const stampText = "ДЕЛО № " + knightsCount;
const metrics = ctx.measureText(stampText);
const padding = size * 0.02;
// Draw distressed stamp border
ctx.strokeRect(-padding, -padding, metrics.width + padding * 2, fontSize + padding * 2);
ctx.strokeRect(-padding - 4, -padding - 4, metrics.width + padding * 2 + 8, fontSize + padding * 2 + 8);
// Draw text
ctx.fillText(stampText, 0, 0);
ctx.restore();
}
return canvas;
}
Apply Changes