You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
text = "CREATIVE STUDIO",
iconShape = "hexagon", // Options: 'square', 'circle', 'rounded', 'hexagon'
layout = "image-top", // Options: 'image-top', 'image-left', 'overlap', 'image-only'
bgColor = "#2c2f33",
textColor = "#ffffff",
borderWidth = "12",
borderColor = "#ff4757",
outputSize = "512"
) {
// 1. Normalize parameters
borderWidth = Number(borderWidth) || 0;
outputSize = Number(outputSize) || 512;
borderWidth = Math.min(borderWidth, outputSize * 0.2); // Cap the border width to a reasonable size
// 2. Setup Canvas
const canvas = document.createElement('canvas');
canvas.width = outputSize;
canvas.height = outputSize;
const ctx = canvas.getContext('2d');
// 3. Build Background Shape Path
ctx.beginPath();
const half = outputSize / 2;
if (iconShape === 'circle') {
ctx.arc(half, half, half, 0, Math.PI * 2);
} else if (iconShape === 'rounded') {
const radius = outputSize * 0.18;
const w = outputSize, h = outputSize;
ctx.moveTo(radius, 0);
ctx.lineTo(w - radius, 0);
ctx.quadraticCurveTo(w, 0, w, radius);
ctx.lineTo(w, h - radius);
ctx.quadraticCurveTo(w, h, w - radius, h);
ctx.lineTo(radius, h);
ctx.quadraticCurveTo(0, h, 0, h - radius);
ctx.lineTo(0, radius);
ctx.quadraticCurveTo(0, 0, radius, 0);
} else if (iconShape === 'hexagon') {
ctx.translate(half, half);
// Drawing a pointy-top hexagon
for (let i = 0; i < 6; i++) {
const angle = (i * Math.PI / 3) - (Math.PI / 6);
const px = half * Math.cos(angle);
const py = half * Math.sin(angle);
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.translate(-half, -half);
} else {
// default to square
ctx.rect(0, 0, outputSize, outputSize);
}
ctx.closePath();
// 4. Handle Clipping & Background Color
ctx.save();
// Fill Base Color
ctx.fillStyle = bgColor;
ctx.fill();
// Fill subtle 3D lighting gradient (common for icons/logos)
const shadowGradient = ctx.createLinearGradient(0, 0, 0, outputSize);
shadowGradient.addColorStop(0, 'rgba(255, 255, 255, 0.15)');
shadowGradient.addColorStop(1, 'rgba(0, 0, 0, 0.25)');
ctx.fillStyle = shadowGradient;
ctx.fill();
// Apply clip to ensure all inner elements stay within the boundary limits
ctx.clip();
// 5. Calculate Inner Bounding Box based on shape to prevent overlap text/image cutoffs
let basePadding = outputSize * 0.05;
if (iconShape === 'circle') basePadding = outputSize * 0.15;
if (iconShape === 'hexagon') basePadding = outputSize * 0.15;
const padding = borderWidth + basePadding;
const ix = padding;
const iy = padding;
const iw = outputSize - padding * 2;
const ih = outputSize - padding * 2;
// 6. Draw Helpers
function drawImageContained(img, bx, by, bw, bh) {
const scale = Math.min(bw / img.width, bh / img.height);
const w = img.width * scale;
const h = img.height * scale;
const x = bx + (bw - w) / 2;
const y = by + (bh - h) / 2;
ctx.save();
ctx.shadowColor = 'rgba(0, 0, 0, 0.35)'; // Give logo imagery a nice lift off the background
ctx.shadowBlur = 15;
ctx.shadowOffsetY = 10;
ctx.drawImage(img, x, y, w, h);
ctx.restore();
}
function drawImageCover(img, bx, by, bw, bh) {
const scale = Math.max(bw / img.width, bh / img.height);
const w = img.width * scale;
const h = img.height * scale;
const x = bx + (bw - w) / 2;
const y = by + (bh - h) / 2;
ctx.drawImage(img, x, y, w, h);
}
function drawText(txt, bx, by, bw, bh) {
if (!txt || txt.trim() === '') return;
ctx.save();
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
let fontSize = bh * 0.6;
const fontFamily = `'Arial Black', Impact, 'Segoe UI Black', sans-serif`;
ctx.font = `900 ${fontSize}px ${fontFamily}`;
let textWidth = ctx.measureText(txt).width;
// Wrap logic for 2 lines if very long
const words = txt.split(' ');
if (words.length >= 2 && textWidth > bw * 1.5) {
const halfIndex = Math.ceil(words.length / 2);
const line1 = words.slice(0, halfIndex).join(' ');
const line2 = words.slice(halfIndex).join(' ');
fontSize = bh * 0.4; // Scale down for dual lines
ctx.font = `900 ${fontSize}px ${fontFamily}`;
const w1 = ctx.measureText(line1).width;
const w2 = ctx.measureText(line2).width;
const maxW = Math.max(w1, w2);
if (maxW > bw * 0.95) {
fontSize *= (bw * 0.95) / maxW;
ctx.font = `900 ${fontSize}px ${fontFamily}`;
}
ctx.shadowColor = 'rgba(0,0,0,0.6)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 4;
ctx.fillText(line1, bx + bw / 2, by + bh / 2 - fontSize * 0.6);
ctx.fillText(line2, bx + bw / 2, by + bh / 2 + fontSize * 0.6);
} else {
// Single Line Shrink-to-fit
if (textWidth > bw * 0.95) {
fontSize *= (bw * 0.95) / textWidth;
ctx.font = `900 ${fontSize}px ${fontFamily}`;
}
ctx.shadowColor = 'rgba(0,0,0,0.6)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 4;
ctx.fillText(txt, bx + bw / 2, by + bh / 2);
}
ctx.restore();
}
// 7. Render Chosen Layout
if (layout === 'image-top') {
const imgH = ih * 0.6;
const textH = ih * 0.4;
drawImageContained(originalImg, ix, iy, iw, imgH);
drawText(text, ix, iy + imgH, iw, textH);
} else if (layout === 'image-left') {
const imgW = iw * 0.45;
const textW = iw * 0.5;
drawImageContained(originalImg, ix, iy, imgW, ih);
drawText(text, ix + iw * 0.5, iy, textW, ih);
} else if (layout === 'overlap') {
// Image spans the whole icon
drawImageCover(originalImg, 0, 0, outputSize, outputSize);
// Dark translucent studio banner wrapper at the bottom
const bannerHeight = ih * 0.45;
const bannerY = outputSize - bannerHeight - padding * 0.5;
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
// Draw banner stretching across the entire width of the clipped icon
ctx.fillRect(0, bannerY, outputSize, bannerHeight);
drawText(text, ix, bannerY, iw, bannerHeight);
} else { // 'image-only'
drawImageContained(originalImg, ix, iy, iw, ih);
}
// 8. Draw Border
if (borderWidth > 0) {
// Set double line width because half will be painted outside the clipping mask
ctx.lineWidth = borderWidth * 2;
ctx.strokeStyle = borderColor;
ctx.stroke();
}
ctx.restore(); // Restores context to un-clipped canvas bounds
return canvas;
}
Apply Changes