You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, contrast = 1.5, saturation = 1.4, vignetteStrength = 0.6, vignetteFeather = 2.5, redMultiplier = 1.05, greenMultiplier = 1.1, blueMultiplier = 0.9) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize frequent getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// If canvas dimensions are zero, return empty canvas
if (canvas.width === 0 || canvas.height === 0) {
return canvas;
}
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
const centerX = width / 2;
const centerY = height / 2;
// Calculate max distance from center to a corner, ensure it's not zero for tiny images
const maxDist = Math.sqrt(centerX * centerX + centerY * centerY) || 1;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// 1. Apply Color Channel Multipliers (Lomo color tinting)
if (redMultiplier !== 1.0 || greenMultiplier !== 1.0 || blueMultiplier !== 1.0) {
r *= redMultiplier;
g *= greenMultiplier;
b *= blueMultiplier;
// Clamp after tinting
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 2. Contrast
// Formula: New = (Old/255 - 0.5) * Factor + 0.5, then scale back to [0, 255]
// This centers the contrast effect around 128 (or 0.5 in normalized space)
if (contrast !== 1.0) {
r = (r / 255 - 0.5) * contrast + 0.5;
g = (g / 255 - 0.5) * contrast + 0.5;
b = (b / 255 - 0.5) * contrast + 0.5;
r *= 255;
g *= 255;
b *= 255;
// Clamp after contrast
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 3. Saturation
// L = 0.299R + 0.587G + 0.114B (luminance)
// NewColor = L + SaturationFactor * (OldColor - L)
if (saturation !== 1.0) {
const lum = 0.299 * r + 0.587 * g + 0.114 * b;
r = lum + saturation * (r - lum);
g = lum + saturation * (g - lum);
b = lum + saturation * (b - lum);
// Clamp after saturation
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 4. Vignetting
if (vignetteStrength > 0) {
const x = (i / 4) % width;
const y = Math.floor((i / 4) / width);
const dx = x - centerX;
const dy = y - centerY;
const dist = Math.sqrt(dx * dx + dy * dy);
// Normalized distance from center [0, 1]
const normDist = dist / maxDist;
// vignetteFeather controls the sharpness of the vignette edge.
// Higher values create a more abrupt edge.
const vigPower = Math.pow(normDist, vignetteFeather);
const vigFactor = Math.max(0, 1.0 - (vignetteStrength * vigPower));
r *= vigFactor;
g *= vigFactor;
b *= vigFactor;
// No need to clamp here if vigFactor is always <=1,
// but final clamp below handles it anyway.
}
// Final Clamp to ensure values are strictly within [0, 255]
data[i] = Math.max(0, Math.min(255, r));
data[i+1] = Math.max(0, Math.min(255, g));
data[i+2] = Math.max(0, Math.min(255, b));
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes