You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, contrastLevel = 1.5, noiseAmount = 20, sharpenIntensity = 0.5, desaturationLevel = 0.2) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
if (width === 0 || height === 0) {
console.warn("Image has zero width or height. Returning an empty canvas.");
canvas.width = 0;
canvas.height = 0;
return canvas;
}
canvas.width = width;
canvas.height = height;
try {
ctx.drawImage(originalImg, 0, 0, width, height);
} catch (e) {
console.error("Error drawing image onto canvas. Returning an empty or partially drawn canvas.", e);
// Depending on the error, canvas might be blank or partially drawn.
return canvas;
}
let imageData;
try {
imageData = ctx.getImageData(0, 0, width, height);
} catch (e) {
console.error("Could not get ImageData, possibly due to CORS or other security restrictions. Original image drawn on canvas is returned.", e);
// Canvas has the original image drawn, but processing cannot continue.
return canvas;
}
const data = imageData.data; // This is a Uint8ClampedArray
// 1. Desaturation
// Ensure desaturationLevel is within [0, 1]
const actualDesaturationLevel = Math.max(0, Math.min(1, desaturationLevel));
if (actualDesaturationLevel > 0) {
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Standard luminance calculation
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
// Interpolate between original color and grayscale
data[i] = Math.round(r * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
data[i + 1] = Math.round(g * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
data[i + 2] = Math.round(b * (1 - actualDesaturationLevel) + gray * actualDesaturationLevel);
// Alpha (data[i+3]) remains unchanged
}
}
// 2. Contrast Adjustment
// Ensure contrastLevel is non-negative. 1.0 is no change.
const actualContrastLevel = Math.max(0, contrastLevel);
if (actualContrastLevel !== 1.0) {
// Adjust pixel value: factor * (value - 128) + 128
// This pushes values away from or towards 128 (mid-gray)
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.round(actualContrastLevel * (data[i] - 128) + 128);
data[i + 1] = Math.round(actualContrastLevel * (data[i + 1] - 128) + 128);
data[i + 2] = Math.round(actualContrastLevel * (data[i + 2] - 128) + 128);
}
}
// 3. Sharpening
// Ensure sharpenIntensity is non-negative. 0 means no sharpening.
const actualSharpenIntensity = Math.max(0, sharpenIntensity);
if (actualSharpenIntensity > 0) {
// Create a copy of the data array for reading, as convolution needs original neighbor values
const sourceDataForSharpen = new Uint8ClampedArray(data);
// Sharpening kernel: enhances edges. Sum of weights is 1.
const kernel = [
0, -actualSharpenIntensity, 0,
-actualSharpenIntensity, 1 + 4 * actualSharpenIntensity, -actualSharpenIntensity,
0, -actualSharpenIntensity, 0
];
const side = 3; // Kernel is 3x3
const halfSide = 1; // Math.floor(side / 2)
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let r_sum = 0, g_sum = 0, b_sum = 0;
for (let ky = 0; ky < side; ky++) {
for (let kx = 0; kx < side; kx++) {
// Clamp coordinates to be within image bounds
const pixelY = Math.min(height - 1, Math.max(0, y + ky - halfSide));
const pixelX = Math.min(width - 1, Math.max(0, x + kx - halfSide));
const srcOffset = (pixelY * width + pixelX) * 4;
const weight = kernel[ky * side + kx];
if (weight === 0) continue; // Optimization for sparse kernels
r_sum += sourceDataForSharpen[srcOffset] * weight;
g_sum += sourceDataForSharpen[srcOffset + 1] * weight;
b_sum += sourceDataForSharpen[srcOffset + 2] * weight;
}
}
const dstOffset = (y * width + x) * 4;
data[dstOffset] = Math.round(r_sum);
data[dstOffset + 1] = Math.round(g_sum);
data[dstOffset + 2] = Math.round(b_sum);
// Alpha (data[dstOffset + 3]) is preserved from before this sharpening step
}
}
}
// 4. Adding Noise
// Ensure noiseAmount is non-negative
const actualNoiseAmount = Math.max(0, noiseAmount);
if (actualNoiseAmount > 0) {
for (let i = 0; i < data.length; i += 4) {
// Generate noise in the range [-actualNoiseAmount, actualNoiseAmount]
const noise = (Math.random() - 0.5) * 2 * actualNoiseAmount;
data[i] = Math.round(data[i] + noise);
data[i + 1] = Math.round(data[i + 1] + noise);
data[i + 2] = Math.round(data[i + 2] + noise);
// Alpha (data[i+3]) remains unchanged
}
}
// Put the modified pixel data back onto the canvas
// The Uint8ClampedArray automatically handles clamping values to [0, 255]
// and rounding upon assignment if Math.round wasn't used. Explicit Math.round is often clearer.
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes