You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, urlText = "https://database.local", theme = "light", iconSize = 512) {
// Parameter normalization
urlText = String(urlText);
theme = String(theme).toLowerCase() === 'dark' ? 'dark' : 'light';
iconSize = Number(iconSize) || 512;
const canvas = document.createElement('canvas');
canvas.width = iconSize;
canvas.height = iconSize;
const ctx = canvas.getContext('2d');
// Layout configuration scaled dynamically
const S = iconSize / 512;
const PADDING = 32 * S;
const ICON_SIZE = 448 * S;
const RADIUS = 96 * S;
// Helper to draw the main iOS-style icon body shape
function buildIconPath() {
ctx.beginPath();
ctx.moveTo(PADDING + RADIUS, PADDING);
ctx.lineTo(PADDING + ICON_SIZE - RADIUS, PADDING);
ctx.quadraticCurveTo(PADDING + ICON_SIZE, PADDING, PADDING + ICON_SIZE, PADDING + RADIUS);
ctx.lineTo(PADDING + ICON_SIZE, PADDING + ICON_SIZE - RADIUS);
ctx.quadraticCurveTo(PADDING + ICON_SIZE, PADDING + ICON_SIZE, PADDING + ICON_SIZE - RADIUS, PADDING + ICON_SIZE);
ctx.lineTo(PADDING + RADIUS, PADDING + ICON_SIZE);
ctx.quadraticCurveTo(PADDING, PADDING + ICON_SIZE, PADDING, PADDING + ICON_SIZE - RADIUS);
ctx.lineTo(PADDING, PADDING + RADIUS);
ctx.quadraticCurveTo(PADDING, PADDING, PADDING + RADIUS, PADDING);
ctx.closePath();
}
// --- Draw the external Drop Shadow ---
buildIconPath();
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = 20 * S;
ctx.shadowOffsetY = 10 * S;
ctx.fillStyle = theme === 'dark' ? '#1e1e1e' : '#ffffff';
ctx.fill();
// Reset shadow
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
// --- Clip the drawing context to our new icon shape bounds ---
ctx.save();
buildIconPath();
ctx.clip();
// 1. Draw "Web Interface" Theme Header Title Bar
ctx.fillStyle = theme === 'dark' ? '#2d2d2d' : '#e1e5ea';
ctx.fillRect(PADDING, PADDING, ICON_SIZE, 64 * S);
// 2. Draw Mac OS style window dots
const dotY = PADDING + 32 * S;
const colors = ['#ff5f56', '#ffbd2e', '#27c93f'];
colors.forEach((c, i) => {
ctx.beginPath();
ctx.arc(PADDING + (24 + i * 20) * S, dotY, 6 * S, 0, Math.PI * 2);
ctx.fillStyle = c;
ctx.fill();
ctx.strokeStyle = 'rgba(0,0,0,0.05)';
ctx.lineWidth = 1 * S;
ctx.stroke();
});
// 3. Draw "Website Address" mock URL Bar
const urlX = PADDING + 80 * S;
const urlY = PADDING + 16 * S;
const urlW = ICON_SIZE - 96 * S;
const urlH = 32 * S;
const urlR = 16 * S;
ctx.fillStyle = theme === 'dark' ? '#121212' : '#ffffff';
ctx.beginPath();
ctx.moveTo(urlX + urlR, urlY);
ctx.lineTo(urlX + urlW - urlR, urlY);
ctx.quadraticCurveTo(urlX + urlW, urlY, urlX + urlW, urlY + urlR);
ctx.lineTo(urlX + urlW, urlY + urlH - urlR);
ctx.quadraticCurveTo(urlX + urlW, urlY + urlH, urlX + urlW - urlR, urlY + urlH);
ctx.lineTo(urlX + urlR, urlY + urlH);
ctx.quadraticCurveTo(urlX, urlY + urlH, urlX, urlY + urlH - urlR);
ctx.lineTo(urlX, urlY + urlR);
ctx.quadraticCurveTo(urlX, urlY, urlX + urlR, urlY);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = theme === 'dark' ? '#333333' : '#d1d1d1';
ctx.lineWidth = 1 * S;
ctx.stroke();
// Write URL / Website Address Text
ctx.fillStyle = theme === 'dark' ? '#aaaaaa' : '#666666';
ctx.font = `${14 * S}px sans-serif`;
ctx.textBaseline = 'middle';
ctx.save();
ctx.beginPath();
ctx.rect(urlX + 10 * S, urlY, urlW - 20 * S, urlH);
ctx.clip(); // Ensure text bounds to perfectly fit inside the URL bar
ctx.fillText(urlText, urlX + 16 * S, urlY + urlH / 2 + 1 * S);
ctx.restore();
// 4. Draw & scale the target image via aspect-fit cropping into the icon
const imgX = PADDING;
const imgY = PADDING + 64 * S;
const imgW = ICON_SIZE;
const imgH = ICON_SIZE - 64 * S;
const tr = imgW / imgH;
const sr = originalImg.width / originalImg.height;
let tw, th;
if (sr > tr) {
th = originalImg.height;
tw = th * tr;
} else {
tw = originalImg.width;
th = tw / tr;
}
const sx = (originalImg.width - tw) / 2;
const sy = (originalImg.height - th) / 2;
ctx.drawImage(originalImg, sx, sy, tw, th, imgX, imgY, imgW, imgH);
// Separator Line under Title Bar
ctx.strokeStyle = theme === 'dark' ? '#000000' : '#cccccc';
ctx.lineWidth = 1 * S;
ctx.beginPath();
ctx.moveTo(PADDING, imgY);
ctx.lineTo(PADDING + ICON_SIZE, imgY);
ctx.stroke();
// 5. Draw "Database" Cylinder Stamp at Lower Right Corner
const dbCx = PADDING + ICON_SIZE - 60 * S;
const dbCyCenter = PADDING + ICON_SIZE - 76 * S;
const dbRx = 30 * S;
const dbRy = 10 * S;
const stackH = 16 * S;
const dbBaseColor = '#1c7ed6';
const dbTopColor = '#339af0';
const dbLineColor = '#0b5b9c';
ctx.shadowColor = 'rgba(0,0,0,0.6)';
ctx.shadowBlur = 10 * S;
ctx.shadowOffsetY = 6 * S;
// Generate stacking layers sequentially from bottom to top
for (let i = 1; i >= -1; i--) {
const y = dbCyCenter + i * stackH;
// Cylinder Base
ctx.fillStyle = dbBaseColor;
ctx.fillRect(dbCx - dbRx, y, dbRx * 2, stackH);
// Cylinder Bottom Curve
ctx.beginPath();
ctx.ellipse(dbCx, y + stackH, dbRx, dbRy, 0, 0, Math.PI, false);
ctx.fill();
// Inner Stroke Detail (remove shadows dynamically for pristine edges)
ctx.shadowColor = 'transparent';
ctx.strokeStyle = dbLineColor;
ctx.lineWidth = 1 * S;
ctx.beginPath();
ctx.ellipse(dbCx, y + stackH, dbRx, dbRy, 0, 0, Math.PI, false);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(dbCx - dbRx, y);
ctx.lineTo(dbCx - dbRx, y + stackH);
ctx.moveTo(dbCx + dbRx, y);
ctx.lineTo(dbCx + dbRx, y + stackH);
ctx.stroke();
// Cylinder Top Flat App
ctx.fillStyle = dbTopColor;
ctx.beginPath();
ctx.ellipse(dbCx, y, dbRx, dbRy, 0, 0, Math.PI * 2, false);
ctx.fill();
ctx.stroke();
// Reapply shadow for upper layers
ctx.shadowColor = 'rgba(0,0,0,0.4)';
ctx.shadowBlur = 6 * S;
ctx.shadowOffsetY = 4 * S;
}
ctx.shadowColor = 'transparent';
// 6. Finishing Touches (Borders / Vignettes on whole icon element wrapper)
ctx.restore();
buildIconPath();
ctx.strokeStyle = theme === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.4)';
ctx.lineWidth = 2 * S;
ctx.stroke();
ctx.strokeStyle = theme === 'dark' ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.1)';
ctx.lineWidth = 1 * S;
ctx.stroke();
return canvas;
}
Apply Changes