You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, amount = 0.75) {
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*/
function rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const 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];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [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 [r * 255, g * 255, b * 255];
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
ctx.drawImage(originalImg, 0, 0);
// If amount is 0, no processing is needed.
if (amount === 0) {
return canvas;
}
const originalImageData = ctx.getImageData(0, 0, width, height);
const processedImageData = ctx.createImageData(width, height);
processedImageData.data.set(originalImageData.data);
const data = processedImageData.data;
// --- Step 1: Auto-Contrast (Levels) ---
let minR = 255, maxR = 0, minG = 255, maxG = 0, minB = 255, maxB = 0;
for (let i = 0; i < data.length; i += 4) {
if (data[i] < minR) minR = data[i];
if (data[i] > maxR) maxR = data[i];
if (data[i + 1] < minG) minG = data[i + 1];
if (data[i + 1] > maxG) maxG = data[i + 1];
if (data[i + 2] < minB) minB = data[i + 2];
if (data[i + 2] > maxB) maxB = data[i + 2];
}
const rRange = maxR - minR;
const gRange = maxG - minG;
const bRange = maxB - minB;
if (rRange > 0 && gRange > 0 && bRange > 0) {
for (let i = 0; i < data.length; i += 4) {
data[i] = ((data[i] - minR) / rRange) * 255;
data[i + 1] = ((data[i + 1] - minG) / gRange) * 255;
data[i + 2] = ((data[i + 2] - minB) / bRange) * 255;
}
}
// --- Step 2: Saturation Boost ---
const saturationBoost = 0.25; // Boost saturation by 25%
for (let i = 0; i < data.length; i += 4) {
const [h, s, l] = rgbToHsl(data[i], data[i + 1], data[i + 2]);
const newS = Math.min(1, s * (1 + saturationBoost));
const [r, g, b] = hslToRgb(h, newS, l);
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
}
// --- Step 3: Sharpening using a convolution kernel ---
const dataBeforeSharpen = Uint8ClampedArray.from(data);
const sharpenKernel = [
0, -1, 0,
-1, 5, -1,
0, -1, 0,
];
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
const i = (y * width + x) * 4;
let sumR = 0, sumG = 0, sumB = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const kernelIndex = (ky + 1) * 3 + (kx + 1);
const weight = sharpenKernel[kernelIndex];
if (weight === 0) continue;
const neighborIndex = ((y + ky) * width + (x + kx)) * 4;
sumR += dataBeforeSharpen[neighborIndex] * weight;
sumG += dataBeforeSharpen[neighborIndex + 1] * weight;
sumB += dataBeforeSharpen[neighborIndex + 2] * weight;
}
}
data[i] = sumR;
data[i + 1] = sumG;
data[i + 2] = sumB;
}
}
// --- Step 4: Final Blend based on 'amount' parameter ---
const originalData = originalImageData.data;
const finalData = processedImageData.data;
for (let i = 0; i < finalData.length; i+=4) {
finalData[i] = originalData[i] * (1 - amount) + finalData[i] * amount;
finalData[i + 1] = originalData[i + 1] * (1 - amount) + finalData[i + 1] * amount;
finalData[i + 2] = originalData[i + 2] * (1 - amount) + finalData[i + 2] * amount;
// Keep original alpha
finalData[i + 3] = originalData[i + 3];
}
ctx.putImageData(processedImageData, 0, 0);
return canvas;
}
Apply Changes