You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, cartoonLevels = 5, edgeThreshold = 60, addTextMode = "bottom_banner", customText = "Big Hero 6: The Series AU") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
// Calculate canvas size based on text mode
let bannerHeight = 0;
if (addTextMode === 'bottom_banner') {
bannerHeight = Math.max(40, Math.floor(height * 0.15));
}
canvas.width = width;
canvas.height = height + bannerHeight;
// Pre-process: increase saturation and contrast before converting to simulate vibrant 2D cartoon style
ctx.filter = 'saturate(1.4) contrast(1.1)';
ctx.drawImage(originalImg, 0, 0);
ctx.filter = 'none';
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
// Convert to grayscale to make edge detection highly sharp
const gray = new Float32Array(width * height);
for (let i = 0; i < data.length; i += 4) {
// Luminance formula
gray[i / 4] = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
}
// Apply Sobel edge detection to draw comic-style outlines
const kernelX = [
-1, 0, 1,
-2, 0, 2,
-1, 0, 1
];
const kernelY = [
-1, -2, -1,
0, 0, 0,
1, 2, 1
];
const edges = new Uint8Array(width * height);
const threshold = Number(edgeThreshold);
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let pixelX = 0;
let pixelY = 0;
for (let j = -1; j <= 1; j++) {
for (let i = -1; i <= 1; i++) {
const val = gray[(y + j) * width + (x + i)];
const kIdx = (j + 1) * 3 + (i + 1);
pixelX += val * kernelX[kIdx];
pixelY += val * kernelY[kIdx];
}
}
const magnitude = Math.sqrt(pixelX * pixelX + pixelY * pixelY);
edges[y * width + x] = magnitude > threshold ? 1 : 0;
}
}
// Apply posterization for "cel-shaded" transition look and integrate edges
const outData = new Uint8ClampedArray(data.length);
const levels = Math.max(2, Number(cartoonLevels));
const factor = 255 / (levels - 1);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = y * width + x;
const colorIdx = idx * 4;
if (edges[idx] === 1) {
// Detected edge => render as a black cartoon outline
outData[colorIdx] = 0;
outData[colorIdx + 1] = 0;
outData[colorIdx + 2] = 0;
outData[colorIdx + 3] = data[colorIdx + 3];
} else {
// Posterize color layers (reducing color space)
outData[colorIdx] = Math.round(data[colorIdx] / factor) * factor;
outData[colorIdx + 1] = Math.round(data[colorIdx + 1] / factor) * factor;
outData[colorIdx + 2] = Math.round(data[colorIdx + 2] / factor) * factor;
outData[colorIdx + 3] = data[colorIdx + 3];
}
}
}
const outImgData = new ImageData(outData, width, height);
ctx.putImageData(outImgData, 0, 0);
// Apply text styling/watermarking based on selected mode
if (addTextMode === 'bottom_banner') {
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(0, height, width, bannerHeight);
ctx.fillStyle = '#e50914'; // Theme red
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const fontSize = Math.floor(bannerHeight * 0.5);
ctx.font = `bold ${fontSize}px sans-serif`;
// Slight shadow drop
ctx.shadowColor = '#000000';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(customText, width / 2, height + (bannerHeight / 2));
} else if (addTextMode === 'overlay') {
const fontSize = Math.max(24, Math.floor(width * 0.05));
ctx.font = `bold ${fontSize}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.lineWidth = Math.max(3, fontSize * 0.15);
ctx.strokeStyle = '#000000';
ctx.strokeText(customText, width / 2, height - (fontSize * 0.5));
ctx.fillStyle = '#e50914';
ctx.fillText(customText, width / 2, height - (fontSize * 0.5));
}
return canvas;
}
Apply Changes