You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, mode = "all") {
// Determine the layout depending on the mode selection
const isAll = mode === "all";
const w = originalImg.width;
const h = originalImg.height;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// For "all" mode, build a 2x2 grid. Otherwise single canvas size.
canvas.width = isAll ? w * 2 : w;
canvas.height = isAll ? h * 2 : h;
// Helper: draw text label at top
function drawLabel(text, x, y) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
const labelHeight = Math.max(30, h * 0.1);
ctx.fillRect(x, y, w, labelHeight);
ctx.fillStyle = 'white';
const fontSize = Math.max(16, h * 0.05);
ctx.font = `${fontSize}px sans-serif`;
ctx.textBaseline = 'middle';
ctx.fillText(text, x + 10, y + labelHeight / 2);
}
// Effect 1: Original / Photo (Фотография)
function drawPhoto(x, y) {
ctx.save();
ctx.beginPath(); ctx.rect(x, y, w, h); ctx.clip();
ctx.filter = 'none';
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(originalImg, x, y, w, h);
drawLabel("Фотография (Photo)", x, y);
ctx.restore();
}
// Effect 2: Drawing / Sketch Edge (Рисунок)
function drawDrawing(x, y) {
ctx.save();
ctx.beginPath(); ctx.rect(x, y, w, h); ctx.clip();
// Base layer: Grayscale
ctx.filter = 'grayscale(100%)';
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(originalImg, x, y, w, h);
// Overlay layer: Grayscale, Inverted, Blurred
const tmpCanvas = document.createElement('canvas');
tmpCanvas.width = w;
tmpCanvas.height = h;
const tmpCtx = tmpCanvas.getContext('2d');
const blurAmount = Math.max(3, w * 0.01); // responsive blur
tmpCtx.filter = `grayscale(100%) invert(100%) blur(${blurAmount}px)`;
tmpCtx.drawImage(originalImg, 0, 0, w, h);
// Blend using color-dodge mapping for a pencil-sketch effect
ctx.globalCompositeOperation = 'color-dodge';
ctx.drawImage(tmpCanvas, 0, 0, w, h, x, y, w, h);
// Render black details to strengthen edges (optional visual boost)
ctx.globalCompositeOperation = 'multiply';
ctx.filter = 'opacity(50%)';
ctx.drawImage(originalImg, x, y, w, h);
ctx.globalCompositeOperation = 'source-over';
ctx.filter = 'none';
drawLabel("Рисунок (Drawing)", x, y);
ctx.restore();
}
// Effect 3: Teeth overlay (Зубы)
function drawTeeth(x, y) {
ctx.save();
ctx.beginPath(); ctx.rect(x, y, w, h); ctx.clip();
ctx.filter = 'none';
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(originalImg, x, y, w, h);
// Calculate a monster mouth in the bottom center
const cx = x + w / 2;
const cy = y + h * 0.75;
const mw = w * 0.3; // Mouth half-width
const mh = h * 0.15; // Mouth half-height
// Draw mouth cavity (black background)
ctx.fillStyle = "black";
ctx.beginPath();
ctx.ellipse(cx, cy, mw, mh, 0, 0, Math.PI * 2);
ctx.fill();
// Draw jagged sharp monster teeth
const numTeeth = 10;
const toothW = (mw * 2) / numTeeth;
ctx.strokeStyle = "#444";
ctx.lineWidth = Math.max(1, w / 400);
for (let i = 0; i < numTeeth; i++) {
const tx = (cx - mw) + (i * toothW);
// Parametric curve factor to align with ellipse arc
const curve1 = Math.sin(Math.PI * (i / numTeeth));
const curve2 = Math.sin(Math.PI * ((i + 1) / numTeeth));
// Top teeth pointing down
const topY1 = cy - (mh * curve1);
const topY2 = cy - (mh * curve2);
ctx.fillStyle = (i % 2 === 0) ? "#ffffe0" : "#fffced";
ctx.beginPath();
ctx.moveTo(tx, topY1);
ctx.lineTo(tx + toothW / 2, cy + (Math.random() * mh * 0.5));
ctx.lineTo(tx + toothW, topY2);
ctx.fill();
ctx.stroke();
// Bottom teeth pointing up
const botY1 = cy + (mh * curve1);
const botY2 = cy + (mh * curve2);
ctx.beginPath();
ctx.moveTo(tx, botY1);
ctx.lineTo(tx + toothW / 2, cy - (Math.random() * mh * 0.5));
ctx.lineTo(tx + toothW, botY2);
ctx.fill();
ctx.stroke();
}
drawLabel("Зубы (Teeth)", x, y);
ctx.restore();
}
// Effect 4: Babaika / Creepy Dark Filter (Бабайка)
function drawBabaika(x, y) {
ctx.save();
ctx.beginPath(); ctx.rect(x, y, w, h); ctx.clip();
// Scary inverted colors with high contrast
ctx.filter = 'saturate(50%) contrast(300%) invert(80%) hue-rotate(180deg) brightness(70%)';
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(originalImg, x, y, w, h);
// Add blurry, glowing red demon eyes
ctx.filter = `blur(${Math.max(2, w / 60)}px)`;
ctx.fillStyle = 'rgba(255, 0, 0, 0.9)';
ctx.beginPath();
// Left eye
ctx.ellipse(x + w * 0.4, y + h * 0.45, w * 0.04, h * 0.02, Math.PI/10, 0, Math.PI * 2);
// Right eye
ctx.ellipse(x + w * 0.6, y + h * 0.45, w * 0.04, h * 0.02, -Math.PI/10, 0, Math.PI * 2);
ctx.fill();
ctx.filter = 'none';
// Add red static noise pattern overlaid via pixel manipulation (if unblocked by CORS)
try {
const imgData = ctx.getImageData(x, y, w, h);
for (let i = 0; i < imgData.data.length; i += 4) {
// ~5% chance to overlay red bleeding noise on pixels
if (Math.random() < 0.05) {
imgData.data[i] = 180 + Math.random() * 75; // Red channel
imgData.data[i+1] = 0; // Green channel
imgData.data[i+2] = 0; // Blue channel
}
}
ctx.putImageData(imgData, x, y);
} catch (e) {
// Context will throw if image is cross-origin taint, graceful degradation
console.warn("Could not apply red noise due to CORS, but filter still applied.");
}
drawLabel("Бабайка (Babaika)", x, y);
ctx.restore();
}
// Render logic depending on selected mode
if (isAll) {
drawPhoto(0, 0); // Top Left
drawDrawing(w, 0); // Top Right
drawTeeth(0, h); // Bottom Left
drawBabaika(w, h); // Bottom Right
} else {
// Individual modes
switch(mode.toLowerCase()) {
case "photo":
drawPhoto(0, 0);
break;
case "drawing":
drawDrawing(0, 0);
break;
case "teeth":
drawTeeth(0, 0);
break;
case "babaika":
drawBabaika(0, 0);
break;
default:
drawPhoto(0, 0); // fallback
}
}
return canvas;
}
Apply Changes