You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, trackType = "paw", trackColor = "black", trackSize = 30, trackDensity = 0.1, trackOpacity = 0.7, randomRotation = "true") {
// Helper function to draw a paw print
function _drawPaw(ctx, x, y, size, color, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillStyle = color;
// Main pad (rounded rectangle)
const mainPadWidth = size * 0.8;
const mainPadHeight = size * 0.6;
const mainPadYOffset = size * 0.15; // Move main pad down a bit from center
const mainPadRadius = size * 0.15; // Corner radius
ctx.beginPath();
ctx.moveTo(-mainPadWidth / 2 + mainPadRadius, mainPadYOffset - mainPadHeight / 2); // Top-left corner start
// Top edge
ctx.lineTo(mainPadWidth / 2 - mainPadRadius, mainPadYOffset - mainPadHeight / 2);
// Top-right corner
ctx.quadraticCurveTo(mainPadWidth / 2, mainPadYOffset - mainPadHeight / 2, mainPadWidth / 2, mainPadYOffset - mainPadHeight / 2 + mainPadRadius);
// Right edge
ctx.lineTo(mainPadWidth / 2, mainPadYOffset + mainPadHeight / 2 - mainPadRadius);
// Bottom-right corner
ctx.quadraticCurveTo(mainPadWidth / 2, mainPadYOffset + mainPadHeight / 2, mainPadWidth / 2 - mainPadRadius, mainPadYOffset + mainPadHeight / 2);
// Bottom edge
ctx.lineTo(-mainPadWidth / 2 + mainPadRadius, mainPadYOffset + mainPadHeight / 2);
// Bottom-left corner
ctx.quadraticCurveTo(-mainPadWidth / 2, mainPadYOffset + mainPadHeight / 2, -mainPadWidth / 2, mainPadYOffset + mainPadHeight / 2 - mainPadRadius);
// Left edge
ctx.lineTo(-mainPadWidth / 2, mainPadYOffset - mainPadHeight / 2 + mainPadRadius);
// Top-left corner end
ctx.quadraticCurveTo(-mainPadWidth / 2, mainPadYOffset - mainPadHeight / 2, -mainPadWidth / 2 + mainPadRadius, mainPadYOffset - mainPadHeight / 2);
ctx.closePath();
ctx.fill();
// Toes (4, slightly oval)
const toeSize = size * 0.22;
const toeRadiusX = toeSize * 0.8; // Width of toe
const toeRadiusY = toeSize; // Height of toe
const toeY = -size * 0.3; // Y position of toes relative to track center
const toePositions = [
{ dx: -size * 0.33, dy: toeY - size * 0.05, angle: -Math.PI / 12 }, // Outer left
{ dx: -size * 0.11, dy: toeY - size * 0.10, angle: -Math.PI / 24 }, // Inner left
{ dx: size * 0.11, dy: toeY - size * 0.10, angle: Math.PI / 24 }, // Inner right
{ dx: size * 0.33, dy: toeY - size * 0.05, angle: Math.PI / 12 } // Outer right
];
for (const pos of toePositions) {
ctx.save();
ctx.translate(pos.dx, pos.dy);
ctx.rotate(pos.angle);
ctx.beginPath();
ctx.ellipse(0, 0, toeRadiusX, toeRadiusY, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
ctx.restore();
}
// Helper function to draw a hoof print (deer-like, two ellipses)
function _drawHoof(ctx, x, y, size, color, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle); // Overall track rotation
ctx.fillStyle = color;
const toeLength = size * 0.6; // Length of each half of the hoof
const toeWidth = size * 0.25; // Width of each half
const toeAngle = Math.PI / 16; // Slight outward angle for each toe (e.g., ~11 degrees)
const toeSeparation = size * 0.12; // Distance from center to each toe's own center
// Left toe
ctx.save();
ctx.translate(-toeSeparation, 0); // Move to left toe's position
ctx.rotate(-toeAngle); // Rotate left toe slightly outwards
ctx.beginPath();
ctx.ellipse(0, 0, toeWidth / 2, toeLength / 2, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Right toe
ctx.save();
ctx.translate(toeSeparation, 0); // Move to right toe's position
ctx.rotate(toeAngle); // Rotate right toe slightly outwards
ctx.beginPath();
ctx.ellipse(0, 0, toeWidth / 2, toeLength / 2, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
ctx.restore(); // Restores from the main track rotation and translation
}
// Helper function to draw a bird track
function _drawBirdTrack(ctx, x, y, size, color, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
const toeLength = size * 0.45;
const lineWidth = Math.max(1.5, size * 0.1); // Ensure lines are visible
ctx.strokeStyle = color;
ctx.fillStyle = color; // For claws/pads at the end of toes
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
const originOffsetY = 0; // Toes radiate from the center point (x,y)
// Central forward toe
ctx.beginPath();
ctx.moveTo(0, originOffsetY);
ctx.lineTo(0, -toeLength + originOffsetY);
ctx.stroke();
ctx.beginPath(); // "Claw" or "pad"
ctx.arc(0, -toeLength + originOffsetY, lineWidth / 1.5, 0, Math.PI * 2);
ctx.fill();
// Left forward toe (angled)
const sideToeAngle = Math.PI / 5.5; // Angle for side toes (approx 32 degrees)
ctx.beginPath();
ctx.moveTo(0, originOffsetY);
ctx.lineTo(toeLength * Math.sin(-sideToeAngle), -toeLength * Math.cos(-sideToeAngle) + originOffsetY);
ctx.stroke();
ctx.beginPath(); // "Claw" or "pad"
ctx.arc(toeLength * Math.sin(-sideToeAngle), -toeLength * Math.cos(-sideToeAngle) + originOffsetY, lineWidth / 1.5, 0, Math.PI * 2);
ctx.fill();
// Right forward toe (angled)
ctx.beginPath();
ctx.moveTo(0, originOffsetY);
ctx.lineTo(toeLength * Math.sin(sideToeAngle), -toeLength * Math.cos(sideToeAngle) + originOffsetY);
ctx.stroke();
ctx.beginPath(); // "Claw" or "pad"
ctx.arc(toeLength * Math.sin(sideToeAngle), -toeLength * Math.cos(sideToeAngle) + originOffsetY, lineWidth / 1.5, 0, Math.PI * 2);
ctx.fill();
// Backward toe
const backToeLength = toeLength * 0.7;
ctx.beginPath();
ctx.moveTo(0, originOffsetY);
ctx.lineTo(0, backToeLength + originOffsetY); // Points downwards
ctx.stroke();
ctx.beginPath(); // "Claw" or "pad"
ctx.arc(0, backToeLength + originOffsetY, lineWidth / 1.5, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// --- Main processImage function logic ---
// Parameter parsing & validation
const pTrackSize = (Number.isFinite(Number(trackSize)) && Number(trackSize) > 0) ? Number(trackSize) : 30;
const pTrackDensity = (Number.isFinite(Number(trackDensity)) && Number(trackDensity) >= 0) ? Number(trackDensity) : 0.1;
const pTrackOpacity = (Number.isFinite(Number(trackOpacity)) && Number(trackOpacity) >= 0 && Number(trackOpacity) <= 1) ? Number(trackOpacity) : 0.7;
const pRandomRotation = String(randomRotation).toLowerCase() === 'true';
const pTrackColor = String(trackColor); // Default handled by signature
const pTrackType = String(trackType); // Default handled by signature
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth || originalImg.width || 0;
canvas.height = originalImg.naturalHeight || originalImg.height || 0;
if (canvas.width === 0 || canvas.height === 0) {
// Return empty (0x0) canvas if image dimensions are zero
console.warn("Image has zero width or height.");
return canvas;
}
// Draw the original image
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Set track properties for drawing
ctx.globalAlpha = pTrackOpacity;
// Calculate number of tracks based on density
// Density is a factor of how many tracks of size (pTrackSize * pTrackSize) would cover the canvas
const numTracks = Math.max(0, Math.floor(((canvas.width * canvas.height) / (pTrackSize * pTrackSize)) * pTrackDensity));
for (let i = 0; i < numTracks; i++) {
const R_x = Math.random() * canvas.width; // Random x position
const R_y = Math.random() * canvas.height; // Random y position
let R_angle = 0; // Default angle if not random
if (pRandomRotation) {
R_angle = Math.random() * Math.PI * 2; // Random angle
}
// Call the appropriate drawing function based on trackType
if (pTrackType === "paw") {
_drawPaw(ctx, R_x, R_y, pTrackSize, pTrackColor, R_angle);
} else if (pTrackType === "hoof") {
_drawHoof(ctx, R_x, R_y, pTrackSize, pTrackColor, R_angle);
} else if (pTrackType === "bird") {
_drawBirdTrack(ctx, R_x, R_y, pTrackSize, pTrackColor, R_angle);
}
// Add more track types here with else if (pTrackType === "newType") { _drawNewType(...); }
}
ctx.globalAlpha = 1.0; // Reset global alpha to default
return canvas;
}
Apply Changes