You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, numDrips = 20, maxDripLengthPercent = 0.4, minDripLengthPercent = 0.1, maxDripWidth = 30, minDripWidth = 8, dripColor = "rgb(60,30,10)", chocolateBaseColorFactorR = 0.8, chocolateBaseColorFactorG = 0.5, chocolateBaseColorFactorB = 0.25, dripTopOffset = 5) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
if (canvas.width === 0 || canvas.height === 0) {
// Handle cases where image dimensions are not yet available or invalid
// This might happen if the Image object isn't fully loaded.
// A black 1x1 canvas is returned in such error cases or if the image truly is 0x0.
console.error("Image dimensions are invalid or image not loaded. Returning empty canvas.");
canvas.width = Math.max(1, canvas.width); // Ensure canvas is at least 1x1
canvas.height = Math.max(1, canvas.height);
return canvas;
}
// Draw the original image
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Apply chocolate base tone by adjusting RGB channels
if (chocolateBaseColorFactorR !== 1 || chocolateBaseColorFactorG !== 1 || chocolateBaseColorFactorB !== 1) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
data[i] = Math.min(255, r * chocolateBaseColorFactorR);
data[i + 1] = Math.min(255, g * chocolateBaseColorFactorG);
data[i + 2] = Math.min(255, b * chocolateBaseColorFactorB);
}
ctx.putImageData(imageData, 0, 0);
}
// Ensure min/max parameters are logical (min <= max)
const actualMinDLP = Math.min(minDripLengthPercent, maxDripLengthPercent);
const actualMaxDLP = Math.max(minDripLengthPercent, maxDripLengthPercent);
const actualMinDW = Math.min(minDripWidth, maxDripWidth);
const actualMaxDW = Math.max(minDripWidth, maxDripWidth);
ctx.fillStyle = dripColor;
for (let i = 0; i < numDrips; i++) {
// Determine the horizontal starting center for the drip
const x = Math.random() * canvas.width;
// Determine drip width
const widthRange = actualMaxDW - actualMinDW;
const randomWidthFactor = (widthRange > 0) ? Math.random() * widthRange : 0;
const width = actualMinDW + randomWidthFactor;
if (width <= 0) continue; // Skip if width is non-positive
// Determine drip length
const lengthRange = actualMaxDLP - actualMinDLP;
const randomLengthFactor = (lengthRange > 0) ? Math.random() * lengthRange : 0;
const dripLengthPercent = actualMinDLP + randomLengthFactor;
const length = dripLengthPercent * canvas.height;
// `dripTopOffset` determines how far down from the image top the drips appear to start
const topY = Math.max(0, dripTopOffset);
// Ensure drip doesn't go beyond canvas height and has a minimal sensible length relative to its width
const availableHeightForDrip = canvas.height - topY - (width * 0.2); // Reserve some space for the tip
const actualLength = Math.min(length, availableHeightForDrip);
// Skip if drip is too short to form a meaningful shape or doesn't fit
if (actualLength <= width * 0.1 || actualLength <= 0) continue;
const bottomY = topY + actualLength;
ctx.beginPath();
// Top left of drip body
ctx.moveTo(x - width / 2, topY);
// Left side curve parameters
// Control Point 1 (influences curve near the top): typically bulges outwards
const cp1X_L = x - width / 2 - Math.random() * width * 0.3 + (Math.random() - 0.5) * width * 0.1;
const cp1Y_L = topY + actualLength * (0.2 + Math.random() * 0.2); // 20-40% down
// Control Point 2 (influences curve towards the bottom): typically curves inwards to the tip
const cp2X_L = x - Math.random() * width * 0.2 + (Math.random() - 0.5) * width * 0.2;
const cp2Y_L = topY + actualLength * (0.7 + Math.random() * 0.2); // 70-90% down
// Tip of the drip (can be slightly off-center from x)
const tipX = x + (Math.random() - 0.5) * width * 0.4;
ctx.bezierCurveTo(cp1X_L, cp1Y_L, cp2X_L, cp2Y_L, tipX, bottomY);
// Right side curve parameters (from tip, up to top right)
// Symmetrical logic to the left side for a balanced drip shape
// Control Point 1 (near tip, for upward curve to the right)
const cp1X_R = x + Math.random() * width * 0.2 - (Math.random() - 0.5) * width * 0.2;
const cp1Y_R = topY + actualLength * (0.7 + Math.random() * 0.2);
// Control Point 2 (near top-right, for upward curve)
const cp2X_R = x + width / 2 + Math.random() * width * 0.3 - (Math.random() - 0.5) * width * 0.1;
const cp2Y_R = topY + actualLength * (0.2 + Math.random() * 0.2);
// Top right of drip body
ctx.bezierCurveTo(cp1X_R, cp1Y_R, cp2X_R, cp2Y_R, x + width / 2, topY);
ctx.closePath();
ctx.fill();
}
return canvas;
}
Apply Changes