You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, amount = 1.0, radius = 3, saturation = 1.2, gamma = 1.0) {
// Helper function to clamp values between min and max
function clamp(value, min, max) {
return Math.max(min, Math.min(value, max));
}
// Helper function to convert RGB to HSL
// r, g, b are in [0, 255]
// returns h, s, l in [0, 1]
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
// Helper function to convert HSL to RGB
// h, s, l are in [0, 1]
// returns r, g, b in [0, 255]
function hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
if (!originalImg || width === 0 || height === 0) {
// Handle cases where image might not be loaded or has no dimensions
const emptyCanvas = document.createElement('canvas');
emptyCanvas.width = 0;
emptyCanvas.height = 0;
return emptyCanvas;
}
// Sanitize parameters
amount = Math.max(0, amount);
radius = Math.max(0, radius);
saturation = Math.max(0, saturation);
// gamma will be checked later to ensure it's > 0 before Math.pow
const resultCanvas = document.createElement('canvas');
resultCanvas.width = width;
resultCanvas.height = height;
const resultCtx = resultCanvas.getContext('2d');
// Draw original image to a temporary working canvas to get its ImageData
const workingCanvas = document.createElement('canvas');
workingCanvas.width = width;
workingCanvas.height = height;
const workingCtx = workingCanvas.getContext('2d', { willReadFrequently: true });
workingCtx.drawImage(originalImg, 0, 0, width, height);
const currentImageData = workingCtx.getImageData(0, 0, width, height);
const pixels = currentImageData.data;
// --- Unsharp Masking (Detail Enhancement) ---
if (amount > 0 && radius > 0) {
const originalPixelsCopy = new Uint8ClampedArray(pixels);
const blurEffectCanvas = document.createElement('canvas');
blurEffectCanvas.width = width;
blurEffectCanvas.height = height;
const blurEffectCtx = blurEffectCanvas.getContext('2d', { willReadFrequently: true });
blurEffectCtx.filter = `blur(${radius}px)`;
blurEffectCtx.drawImage(originalImg, 0, 0, width, height);
const tempCanvasForBlurredData = document.createElement('canvas');
tempCanvasForBlurredData.width = width;
tempCanvasForBlurredData.height = height;
const tempCtxBlurred = tempCanvasForBlurredData.getContext('2d', { willReadFrequently: true });
tempCtxBlurred.drawImage(blurEffectCanvas, 0, 0, width, height);
const blurredPixelsData = tempCtxBlurred.getImageData(0, 0, width, height).data;
for (let i = 0; i < pixels.length; i += 4) {
const detailR = originalPixelsCopy[i] - blurredPixelsData[i];
const detailG = originalPixelsCopy[i + 1] - blurredPixelsData[i + 1];
const detailB = originalPixelsCopy[i + 2] - blurredPixelsData[i + 2];
pixels[i] = clamp(originalPixelsCopy[i] + amount * detailR, 0, 255);
pixels[i + 1] = clamp(originalPixelsCopy[i + 1] + amount * detailG, 0, 255);
pixels[i + 2] = clamp(originalPixelsCopy[i + 2] + amount * detailB, 0, 255);
}
}
// --- Saturation Adjustment ---
if (saturation !== 1.0) { // Avoid processing if no change
for (let i = 0; i < pixels.length; i += 4) {
let r = pixels[i];
let g = pixels[i + 1];
let b = pixels[i + 2];
let [h, s, l] = rgbToHsl(r, g, b);
s = clamp(s * saturation, 0, 1);
[r, g, b] = hslToRgb(h, s, l);
pixels[i] = r;
pixels[i + 1] = g;
pixels[i + 2] = b;
}
}
// --- Gamma Correction ---
if (gamma !== 1.0 && gamma > 0) { // Ensure gamma is positive and there's a change
const invGamma = 1.0 / gamma;
for (let i = 0; i < pixels.length; i += 4) {
pixels[i] = clamp(Math.round(Math.pow(pixels[i] / 255, invGamma) * 255), 0, 255);
pixels[i + 1] = clamp(Math.round(Math.pow(pixels[i + 1] / 255, invGamma) * 255), 0, 255);
pixels[i + 2] = clamp(Math.round(Math.pow(pixels[i + 2] / 255, invGamma) * 255), 0, 255);
}
}
resultCtx.putImageData(currentImageData, 0, 0);
return resultCanvas;
}
Apply Changes