You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, catSize = "20", backgroundColor = "#000000") {
let resolution = parseInt(catSize, 10);
if (isNaN(resolution) || resolution < 8) resolution = 8; // Ensure a minimum size to prevent lag
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Scale down image if it's too large to prevent canvas rendering freezes
const MAX_DIMENSION = 1600;
let scale = 1;
if (width > MAX_DIMENSION || height > MAX_DIMENSION) {
scale = Math.min(MAX_DIMENSION / width, MAX_DIMENSION / height);
}
const targetWidth = Math.floor(width * scale);
const targetHeight = Math.floor(height * scale);
if (targetWidth === 0 || targetHeight === 0) return canvas;
canvas.width = targetWidth;
canvas.height = targetHeight;
// Draw the original image to read pixel data
ctx.drawImage(originalImg, 0, 0, targetWidth, targetHeight);
let imgData;
try {
imgData = ctx.getImageData(0, 0, targetWidth, targetHeight);
} catch (e) {
// Fallback for tainted canvas/CORS edge-cases
return canvas;
}
const data = imgData.data;
// Clear and fill the background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, targetWidth, targetHeight);
// Pre-calculate the cat silhouette path (on a 24x24 pixel grid scale)
const catPath = new Path2D();
catPath.moveTo(3, 11);
// left side to chin to right side
catPath.bezierCurveTo(3, 19, 7, 23, 12, 23);
catPath.bezierCurveTo(17, 23, 21, 19, 21, 11);
// right outer ear
catPath.lineTo(23, 2);
// right inner ear base
catPath.lineTo(16, 7);
// top of head dip between ears
catPath.bezierCurveTo(14, 5.5, 10, 5.5, 8, 7);
// left inner ear base to tip to start
catPath.lineTo(1, 2);
catPath.lineTo(3, 11);
catPath.closePath();
// Loop through the image in grid blocks
for (let y = 0; y < targetHeight; y += resolution) {
for (let x = 0; x < targetWidth; x += resolution) {
let r = 0, g = 0, b = 0, a = 0;
let count = 0;
// Sample a consistent sub-grid within the block for performance
const step = Math.max(1, Math.floor(resolution / 4));
for (let sy = 0; sy < resolution; sy += step) {
for (let sx = 0; sx < resolution; sx += step) {
const py = y + sy;
const px = x + sx;
if (px < targetWidth && py < targetHeight) {
const index = (py * targetWidth + px) * 4;
r += data[index];
g += data[index + 1];
b += data[index + 2];
a += data[index + 3];
count++;
}
}
}
// Process block only if it has minimum opacity
if (count > 0 && (a / count) > 10) {
r = Math.floor(r / count);
g = Math.floor(g / count);
b = Math.floor(b / count);
let alpha = (a / count) / 255;
ctx.save();
// Align block to center of scaled grid cell
ctx.translate(x + resolution / 2, y + resolution / 2);
ctx.scale(resolution / 24, resolution / 24);
ctx.translate(-12, -12.5);
// Draw solid colored cat head
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`;
ctx.fill(catPath);
// Determine facial details contrast (dark vs light)
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
const detailColor = brightness > 120 ? `rgba(0, 0, 0, ${alpha * 0.7})` : `rgba(255, 255, 255, ${alpha * 0.8})`;
ctx.fillStyle = detailColor;
ctx.beginPath();
// Left & Right eyes
ctx.ellipse(8, 13, 1.5, 2.5, 0, 0, Math.PI * 2);
ctx.ellipse(16, 13, 1.5, 2.5, 0, 0, Math.PI * 2);
ctx.fill();
// Little nose
ctx.beginPath();
ctx.moveTo(11, 16.5);
ctx.lineTo(13, 16.5);
ctx.lineTo(12, 18);
ctx.fill();
// Whiskers
ctx.beginPath();
// Left whiskers
ctx.moveTo(7, 15.5); ctx.lineTo(1, 14);
ctx.moveTo(7.5, 17); ctx.lineTo(1, 17);
ctx.moveTo(7, 18.5); ctx.lineTo(1, 20);
// Right whiskers
ctx.moveTo(17, 15.5); ctx.lineTo(23, 14);
ctx.moveTo(16.5, 17); ctx.lineTo(23, 17);
ctx.moveTo(17, 18.5); ctx.lineTo(23, 20);
ctx.lineWidth = 1;
ctx.lineCap = "round";
ctx.strokeStyle = detailColor;
ctx.stroke();
ctx.restore();
}
}
}
return canvas;
}
Apply Changes