You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, size = 256, shape = "rounded", tvEffect = "false") {
// Validate size parameter (standard ICO size cap is 256)
let s = parseInt(size, 10);
if (isNaN(s)) s = 256;
s = Math.min(Math.max(s, 16), 256);
// Initial setup
const canvas = document.createElement('canvas');
canvas.width = s;
canvas.height = s;
const ctx = canvas.getContext('2d');
// Make borders visually crisp when scaling
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = "high";
// Mathematical center crop preparation
const sizeOrig = Math.min(originalImg.width, originalImg.height);
const startX = (originalImg.width - sizeOrig) / 2;
const startY = (originalImg.height - sizeOrig) / 2;
// Optional clipping for standardized icon shapes
if (shape === "circle" || shape === "round") {
ctx.beginPath();
ctx.arc(s / 2, s / 2, s / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
} else if (shape === "rounded") {
const radius = Math.max(s * 0.225, 1); // standard ~22.5% rounded corner
ctx.beginPath();
ctx.moveTo(radius, 0);
ctx.lineTo(s - radius, 0);
ctx.quadraticCurveTo(s, 0, s, radius);
ctx.lineTo(s, s - radius);
ctx.quadraticCurveTo(s, s, s - radius, s);
ctx.lineTo(radius, s);
ctx.quadraticCurveTo(0, s, 0, s - radius);
ctx.lineTo(0, radius);
ctx.quadraticCurveTo(0, 0, radius, 0);
ctx.closePath();
ctx.clip();
}
// Draw and format image
ctx.drawImage(originalImg, startX, startY, sizeOrig, sizeOrig, 0, 0, s, s);
// Apply optional "Icon TV" effect if explicitly flagged
if (String(tvEffect).toLowerCase() === "true") {
// Overlay a slight green phosphor color tint
ctx.fillStyle = 'rgba(0, 255, 0, 0.05)';
ctx.fillRect(0, 0, s, s);
// Subtly inject CRT scanlines
ctx.fillStyle = 'rgba(255, 255, 255, 0.08)';
for (let y = 0; y < s; y += 4) {
ctx.fillRect(0, y, s, 1.5);
}
ctx.fillStyle = 'rgba(0, 0, 0, 0.12)';
for (let y = 2; y < s; y += 4) {
ctx.fillRect(0, y, s, 1.5);
}
// Deep vignette edge framing
const gradient = ctx.createRadialGradient(s / 2, s / 2, s * 0.4, s / 2, s / 2, s * 0.9);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.65)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, s, s);
}
// Convert Canvas data output to raw bytes in PNG format
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
const arrayBuffer = await blob.arrayBuffer();
const pngBytes = new Uint8Array(arrayBuffer);
// Initialize formatting details to construct an authorized .ICO file buffer
// 6 Bytes (ICONDIR) + 16 Bytes (ICONDIRENTRY) + Base PNG Data Length
const totalSize = 6 + 16 + pngBytes.length;
const icoBuffer = new ArrayBuffer(totalSize);
const view = new DataView(icoBuffer);
// ICONDIR configuration
view.setUint16(0, 0, true); // Header reserved must be 0
view.setUint16(2, 1, true); // Set Type to 1 (ICO marker)
view.setUint16(4, 1, true); // Set Count to 1 (Total images present)
// ICONDIRENTRY configuration
const w = s === 256 ? 0 : s; // Dimensions up to 255 limit (256 wraps to 0 per spec)
const h = s === 256 ? 0 : s;
view.setUint8(6, w); // Pixel width
view.setUint8(7, h); // Pixel height
view.setUint8(8, 0); // Max colors count (0 indicates full color scale)
view.setUint8(9, 0); // Reserved format flag
view.setUint16(10, 1, true); // Color planes layout must be 1
view.setUint16(12, 32, true); // Strict 32 bits per pixel marker (including alpha opacity)
view.setUint32(14, pngBytes.length, true); // Document dimensions mapping (in raw byte count)
view.setUint32(18, 22, true); // Exact initial chunk mapping offset (6+16=22)
// Inject raw file stream
const icoBytes = new Uint8Array(icoBuffer);
icoBytes.set(pngBytes, 22);
// Provide dual-use download protocols correctly compiled natively
const icoFileBlob = new Blob([icoBuffer], { type: 'image/x-icon' });
const icoUrl = URL.createObjectURL(icoFileBlob);
const pngUrl = canvas.toDataURL('image/png');
// Create a DOM container to display results beautifully and reliably
const container = document.createElement('div');
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.justifyContent = 'center';
container.style.padding = '25px';
container.style.gap = '20px';
container.style.fontFamily = 'Arial, sans-serif';
container.style.backgroundColor = '#f4f5f7';
container.style.borderRadius = '10px';
container.style.boxShadow = '0 4px 15px rgba(0,0,0,0.1)';
container.style.maxWidth = '450px';
container.style.margin = '0 auto';
container.style.boxSizing = 'border-box';
const headerText = document.createElement('h3');
headerText.textContent = 'Icon Generated Successfully';
headerText.style.margin = '0';
headerText.style.color = '#333';
headerText.style.fontSize = '18px';
container.appendChild(headerText);
// Apply an underlying checkerboard format transparent box overlay for preview UI clarity
const imgWrapper = document.createElement('div');
imgWrapper.style.width = `${s}px`;
imgWrapper.style.height = `${s}px`;
imgWrapper.style.background = 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAH0lEQVQ4T2N89uzZfwY8QFJSEp80A+OoAcMiDP7//wEA02Qk1Wk8bFAAAAAASUVORK5CYII=") repeat';
imgWrapper.style.border = '1px solid #d1d1d1';
imgWrapper.style.borderRadius = '4px';
imgWrapper.style.boxShadow = '0 8px 24px rgba(0,0,0,0.12)';
// Utilize Standard PNG encoding strictly for secure canvas display (avoids partial ICO HTML preview failures)
const previewImg = new Image();
previewImg.src = pngUrl;
previewImg.style.width = '100%';
previewImg.style.height = '100%';
previewImg.style.display = 'block';
imgWrapper.appendChild(previewImg);
container.appendChild(imgWrapper);
const btnContainer = document.createElement('div');
btnContainer.style.display = 'flex';
btnContainer.style.gap = '15px';
btnContainer.style.flexWrap = 'wrap';
btnContainer.style.justifyContent = 'center';
const createDownloadButton = (text, url, filename, baseColor, hoverColor) => {
const btn = document.createElement('a');
btn.href = url;
btn.download = filename;
btn.textContent = text;
btn.style.textDecoration = 'none';
btn.style.padding = '12px 20px';
btn.style.backgroundColor = baseColor;
btn.style.color = '#fff';
btn.style.borderRadius = '6px';
btn.style.fontWeight = 'bold';
btn.style.fontSize = '14px';
btn.style.border = 'none';
btn.style.cursor = 'pointer';
btn.style.transition = 'background-color 0.2s';
btn.onmouseover = () => btn.style.backgroundColor = hoverColor;
btn.onmouseout = () => btn.style.backgroundColor = baseColor;
return btn;
};
const dlIcoBtn = createDownloadButton('Download .ICO', icoUrl, 'icon.ico', '#2563eb', '#1d4ed8');
const dlPngBtn = createDownloadButton('Download .PNG', pngUrl, 'icon.png', '#16a34a', '#15803d');
btnContainer.appendChild(dlIcoBtn);
btnContainer.appendChild(dlPngBtn);
container.appendChild(btnContainer);
// Return HTML container explicitly tailored to support `.html()` loading injections safely.
return container;
}
Apply Changes