You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, leakColorStr = "255,100,50", leakOpacity = 0.6, blendMode = "screen", leakPosition = "random-corner", leakSize = 0.8, spread = 0.3) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Default values for fallbacks if Number(param) results in NaN or if parsing fails
const DEFAULT_LEAK_OPACITY = 0.6;
const DEFAULT_LEAK_SIZE = 0.8;
const DEFAULT_SPREAD = 0.3;
const DEFAULT_LEAK_COLOR_RGB_ARRAY = [255, 100, 50]; // A common orange-reddish leak color
const DEFAULT_BLEND_MODE = "screen";
const DEFAULT_LEAK_POSITION = "random-corner";
const w = originalImg.width;
const h = originalImg.height;
if (w === 0 || h === 0) {
// Handle cases where image might not be loaded or has zero dimensions
console.error("Image has zero dimensions. Cannot process.");
// Return an empty canvas or throw an error
canvas.width = 1; // Avoid issues with zero-size canvas
canvas.height = 1;
return canvas;
}
canvas.width = w;
canvas.height = h;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// 1. Parameter Validation and Sanitization
let r, g, b;
try {
const colors = String(leakColorStr).split(',').map(val => Number(String(val).trim()));
if (colors.length === 3 && colors.every(c => !isNaN(c) && c >= 0 && c <= 255)) {
[r, g, b] = colors.map(c => Math.floor(c)); // Ensure integer RGB values
} else {
[r, g, b] = DEFAULT_LEAK_COLOR_RGB_ARRAY;
}
} catch (e) {
console.warn("Error parsing leakColorStr, using default.", e);
[r, g, b] = DEFAULT_LEAK_COLOR_RGB_ARRAY;
}
const finalLeakColorRgb = `rgb(${r},${g},${b})`;
const finalTransparentLeakColorRgba = `rgba(${r},${g},${b},0)`;
let numLeakOpacity = Number(leakOpacity);
const finalOpacity = !isNaN(numLeakOpacity) ? Math.max(0, Math.min(1, numLeakOpacity)) : DEFAULT_LEAK_OPACITY;
const validBlendModes = [
"screen", "lighter", "color-dodge", "overlay", "soft-light", "hard-light",
"difference", "exclusion", "hue", "saturation", "color", "luminosity",
"source-over", "multiply" // Added a few more common ones
];
const finalBlendMode = typeof blendMode === 'string' && validBlendModes.includes(blendMode) ? blendMode : DEFAULT_BLEND_MODE;
let numLeakSize = Number(leakSize);
const finalLeakSize = !isNaN(numLeakSize) ? Math.max(0.05, Math.min(3.0, numLeakSize)) : DEFAULT_LEAK_SIZE;
let numSpread = Number(spread);
// Spread should be between 0 and 1 (exclusive of 1 for some interpretations, but 0.99 is practically 1 for color stops)
const finalSpread = !isNaN(numSpread) ? Math.max(0.0, Math.min(0.999, numSpread)) : DEFAULT_SPREAD;
// 2. Determine Leak Center (cx, cy)
let cx, cy;
let actualPosition = String(leakPosition); // Ensure it's a string for reliable switch/includes
const validPositions = ["top-left", "top-right", "bottom-left", "bottom-right", "center", "random-corner", "random-on-canvas"];
if (!validPositions.includes(actualPosition)) {
actualPosition = DEFAULT_LEAK_POSITION; // Default if an invalid string is provided
}
if (actualPosition === "random-corner") {
const corners = ["top-left", "top-right", "bottom-left", "bottom-right"];
actualPosition = corners[Math.floor(Math.random() * corners.length)];
}
// cornerOffsetFactor determines how far "off-screen" corner leaks originate.
// A value of 0 means at the exact corner. Positive values move it further outside.
const cornerOffsetFactor = 0.1;
switch (actualPosition) {
case "top-left":
cx = -w * cornerOffsetFactor; cy = -h * cornerOffsetFactor;
break;
case "top-right":
cx = w * (1 + cornerOffsetFactor); cy = -h * cornerOffsetFactor;
break;
case "bottom-left":
cx = -w * cornerOffsetFactor; cy = h * (1 + cornerOffsetFactor);
break;
case "bottom-right":
cx = w * (1 + cornerOffsetFactor); cy = h * (1 + cornerOffsetFactor);
break;
case "center":
cx = w / 2; cy = h / 2;
break;
case "random-on-canvas":
default: // Includes fallback for safety, or if actualPosition resolved to an unexpected value
cx = Math.random() * w;
cy = Math.random() * h;
break;
}
// 3. Configure Canvas Context for Drawing the Leak Effect
ctx.globalCompositeOperation = finalBlendMode;
ctx.globalAlpha = finalOpacity;
// 4. Create and Apply Radial Gradient for the Light Leak
const diagonal = Math.sqrt(w * w + h * h);
const outerRadius = diagonal * finalLeakSize;
// Radial gradients in HTML Canvas are defined from an inner circle (cx, cy, innerRadius)
// to an outer circle (cx, cy, outerRadius). For a typical light bloom, innerRadius is 0.
const innerRadius = 0;
const gradient = ctx.createRadialGradient(cx, cy, innerRadius, cx, cy, outerRadius);
// Defines the color gradient progression:
// - Starts at `finalLeakColorRgb` at the center (0.0 point of the gradient).
// - Stays solid `finalLeakColorRgb` up to `finalSpread` fraction of the radius.
// - Fades from `finalLeakColorRgb` to `finalTransparentLeakColorRgba` between `finalSpread` and 1.0 (outer edge of gradient).
gradient.addColorStop(0, finalLeakColorRgb);
gradient.addColorStop(finalSpread, finalLeakColorRgb);
gradient.addColorStop(1, finalTransparentLeakColorRgba);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h); // Apply the gradient over the entire canvas
// 5. Reset Canvas Context Properties to Defaults
// Good practice for functions that modify global canvas state.
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes