You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, contrastLevel = 70, noiseLevel = 25, tintColorStr = "150,160,50", tintStrength = 0.3) {
// 1. Canvas setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Ensure image is loaded for naturalWidth/naturalHeight, otherwise use width/height
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
// Image has no dimensions, return an empty canvas or indicate error
console.warn("Input image has zero width or height.");
canvas.width = 100; // Default small size for error message
canvas.height = 50;
ctx.font = "12px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "center";
ctx.fillText("Invalid image dimensions.", canvas.width / 2, canvas.height / 2);
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
try {
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
} catch (e) {
console.error("Error drawing image onto canvas:", e);
ctx.font = "16px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "center";
ctx.fillText("Error: Could not draw image.", canvas.width / 2, canvas.height / 2);
return canvas;
}
// 2. Get imageData
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// This can happen if the image is from a different origin and canvas is tainted
console.error("Could not get image data (cross-origin issue?):", e);
// Clear canvas and draw an error message
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = "16px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "center";
ctx.fillText("Error: Cannot process cross-origin image.", canvas.width / 2, canvas.height / 2);
return canvas;
}
const data = imageData.data;
// Parse tintColorStr and validate parameters
let [tintR, tintG, tintB] = tintColorStr.split(',').map(s => parseInt(s, 10));
if (isNaN(tintR) || isNaN(tintG) || isNaN(tintB) || tintColorStr.split(',').length !== 3) {
[tintR, tintG, tintB] = [150, 160, 50]; // Default fallback tint (sickly green-yellow)
}
tintR = Math.max(0, Math.min(255, tintR));
tintG = Math.max(0, Math.min(255, tintG));
tintB = Math.max(0, Math.min(255, tintB));
// Ensure numeric types and clamp values for other parameters
contrastLevel = Number(contrastLevel);
noiseLevel = Number(noiseLevel);
tintStrength = Number(tintStrength);
// Clamp contrastLevel: The formula used works well with -255 to 255.
// Values outside this range can lead to factors that are too extreme or problematic.
contrastLevel = Math.max(-255, Math.min(255, contrastLevel));
// Clamp tintStrength to [0, 1] (0 = no tint, 1 = full tint)
tintStrength = Math.max(0, Math.min(1, tintStrength));
// Noise level should be non-negative
noiseLevel = Math.max(0, noiseLevel);
// 3. Iterate pixel by pixel
for (let i = 0; i < data.length; i += 4) {
let r_orig = data[i];
let g_orig = data[i + 1];
let b_orig = data[i + 2];
// a. Grayscale (Luminosity method: 0.299R + 0.587G + 0.114B)
let L = 0.299 * r_orig + 0.587 * g_orig + 0.114 * b_orig;
// b. Contrast
// Formula: factor = (259 * (C + 255)) / (255 * (259 - C))
// AdjustedPixel = factor * (L_original_component - 128) + 128
// C is contrastLevel, clamped between -255 and 255.
// If C = 0, factor = 1 (no change).
// If C = 255, factor yields high contrast. If C = -255, factor = 0 (results in gray 128).
const contrastFactor = (259 * (contrastLevel + 255)) / (255 * (259 - contrastLevel));
L = contrastFactor * (L - 128) + 128;
L = Math.max(0, Math.min(255, L)); // Clamp Luminance to [0, 255]
// c. Tint application
// Linearly interpolate between the grayscale value (L) and the tint color
let R_final = L * (1 - tintStrength) + tintR * tintStrength;
let G_final = L * (1 - tintStrength) + tintG * tintStrength;
let B_final = L * (1 - tintStrength) + tintB * tintStrength;
// d. Noise application
// Add random noise, distributed around 0, scaled by noiseLevel
const noise = (Math.random() - 0.5) * 2 * noiseLevel;
R_final += noise;
G_final += noise;
B_final += noise;
// Clamp final RGB values to [0, 255]
data[i] = Math.max(0, Math.min(255, R_final));
data[i + 1] = Math.max(0, Math.min(255, G_final));
data[i + 2] = Math.max(0, Math.min(255, B_final));
// Alpha channel (data[i+3]) remains unchanged
}
// 4. Put modified imageData back to canvas
ctx.putImageData(imageData, 0, 0);
// 5. Return the canvas element
return canvas;
}
Apply Changes