You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies an animal-themed pattern overlay to an image.
* Given the tool name "Image Animals Gallery", this function interprets the task
* as adding an "animal print" filter to the original image, making it part of a
* virtual "gallery" of animal-themed images.
*
* @param {Image} originalImg The original javascript Image object.
* @param {string} [pattern='leopard'] The animal pattern to apply. Options: 'leopard', 'zebra', 'tiger', 'giraffe', 'dalmatian'.
* @param {number} [scale=1.0] A scaling factor for the pattern size.
* @param {string} [color='#000000'] The color of the pattern overlay.
* @param {number} [opacity=0.4] The opacity of the pattern overlay (0.0 to 1.0).
* @param {string} [blendMode='overlay'] The canvas blend mode (globalCompositeOperation) to use.
* @returns {HTMLCanvasElement} A new canvas element with the animal pattern applied.
*/
function processImage(originalImg, pattern = 'leopard', scale = 1.0, color = '#000000', opacity = 0.4, blendMode = 'overlay') {
// Helper function to draw a Leopard-style pattern
const drawLeopardPattern = (ctx, w, h, s) => {
const spotCount = Math.floor((w * h) / (2500 * s * s));
const baseRadius = 18 * s;
for (let i = 0; i < spotCount; i++) {
const x = Math.random() * w;
const y = Math.random() * h;
const radius = baseRadius * (0.6 + Math.random() * 0.8);
ctx.lineWidth = Math.max(1, radius / 2.5 * (0.8 + Math.random() * 0.4));
const numArcs = 3 + Math.floor(Math.random() * 3);
const startAngle = Math.random() * Math.PI * 2;
const angleStep = (Math.PI * 1.7) / numArcs; // Leave a gap
for (let j = 0; j < numArcs; j++) {
const angle1 = startAngle + j * angleStep + (Math.random() - 0.5) * 0.5;
const angle2 = angle1 + angleStep * (0.4 + Math.random() * 0.4);
ctx.beginPath();
ctx.arc(x, y, radius, angle1, angle2);
ctx.stroke();
}
}
};
// Helper function to draw a Zebra-style pattern
const drawZebraPattern = (ctx, w, h, s) => {
const stripeWidth = 50 * s;
for (let x = -stripeWidth; x < w + stripeWidth; x += stripeWidth * (0.8 + Math.random() * 0.4)) {
ctx.beginPath();
let startX = x + (Math.random() - 0.5) * stripeWidth * 0.5;
ctx.moveTo(startX, -20);
ctx.lineWidth = Math.max(2, stripeWidth * (0.4 + Math.random() * 0.4));
let amplitude = 25 * s * (0.5 + Math.random());
let frequency = (0.005 + Math.random() * 0.005) / s;
for (let y = -20; y < h + 20; y++) {
const offsetX = amplitude * Math.sin(y * frequency + x * 0.01);
ctx.lineTo(startX + offsetX, y);
}
ctx.stroke();
}
};
// Helper function for Tiger pattern
const drawTigerPattern = (ctx, w, h, s) => {
const stripeBaseWidth = 35 * s;
const numColumns = Math.ceil(w / (stripeBaseWidth * 3));
for (let i = 0; i < numColumns; i++) {
const colX = (i / numColumns) * w + (Math.random() - 0.5) * stripeBaseWidth;
const numStripes = 3 + Math.floor(Math.random() * 4);
for (let j = 0; j < numStripes; j++) {
const y = Math.random() * h;
const stripeHeight = h * (0.1 + Math.random() * 0.15);
ctx.lineWidth = Math.max(2, stripeBaseWidth * (0.2 + Math.random() * 0.4));
ctx.beginPath();
const startX = colX + (Math.random() - 0.5) * stripeBaseWidth;
ctx.moveTo(startX, y);
const endX = colX + (Math.random() - 0.5) * stripeBaseWidth;
const endY = y + stripeHeight;
const cp1x = startX + (Math.random() - 0.5) * stripeBaseWidth * 2;
const cp1y = y + stripeHeight * 0.33;
const cp2x = endX + (Math.random() - 0.5) * stripeBaseWidth * 2;
const cp2y = y + stripeHeight * 0.66;
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
ctx.stroke();
}
}
};
// Helper function to draw a Giraffe-style pattern
const drawGiraffePattern = (ctx, w, h, s) => {
const patchSize = 100 * s;
const cols = Math.ceil(w / patchSize) + 1;
const rows = Math.ceil(h / patchSize) + 1;
for (let i = -1; i < rows; i++) {
for (let j = -1; j < cols; j++) {
const x = j * patchSize + (Math.random() - 0.5) * patchSize * 0.6;
const y = i * patchSize + (Math.random() - 0.5) * patchSize * 0.6;
const sizeX = patchSize * (0.7 + Math.random() * 0.5);
const sizeY = patchSize * (0.7 + Math.random() * 0.5);
ctx.save();
ctx.translate(x + sizeX / 2, y + sizeY / 2);
ctx.rotate(Math.random() * Math.PI * 2);
ctx.beginPath();
const points = 5 + Math.floor(Math.random() * 4);
ctx.moveTo(sizeX / 2, 0);
for (let k = 1; k <= points; k++) {
const angle = (k / points) * 2 * Math.PI;
const radiusX = sizeX / 2 * (0.9 + Math.random() * 0.2);
const radiusY = sizeY / 2 * (0.9 + Math.random() * 0.2);
ctx.lineTo(radiusX * Math.cos(angle), radiusY * Math.sin(angle));
}
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
};
// Helper function to draw a Dalmatian-style pattern
const drawDalmatianPattern = (ctx, w, h, s) => {
const spotCount = Math.floor((w * h) / (1800 * s * s));
const baseRadius = 15 * s;
for (let i = 0; i < spotCount; i++) {
const x = Math.random() * w;
const y = Math.random() * h;
const radiusX = baseRadius * (0.7 + Math.random() * 0.6);
const radiusY = baseRadius * (0.7 + Math.random() * 0.6);
const rotation = Math.random() * Math.PI * 2;
ctx.beginPath();
ctx.ellipse(x, y, radiusX, radiusY, rotation, 0, Math.PI * 2);
ctx.fill();
}
};
// --- Main function logic ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// Create a temporary canvas for the pattern
const patternCanvas = document.createElement('canvas');
const pCtx = patternCanvas.getContext('2d');
patternCanvas.width = w;
patternCanvas.height = h;
pCtx.fillStyle = color;
pCtx.strokeStyle = color;
// Draw the selected pattern onto the temporary canvas
switch (pattern.toLowerCase().trim()) {
case 'zebra':
drawZebraPattern(pCtx, w, h, scale);
break;
case 'tiger':
drawTigerPattern(pCtx, w, h, scale);
break;
case 'giraffe':
drawGiraffePattern(pCtx, w, h, scale);
break;
case 'dalmatian':
drawDalmatianPattern(pCtx, w, h, scale);
break;
case 'leopard':
default:
drawLeopardPattern(pCtx, w, h, scale);
break;
}
// 1. Draw the original image onto the main canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// 2. Set blending mode and opacity, then draw the pattern on top
ctx.globalAlpha = opacity;
ctx.globalCompositeOperation = blendMode;
ctx.drawImage(patternCanvas, 0, 0, w, h);
// 3. Reset context properties to default
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes