You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, applyGrayscaleStr = "true", contrastVal = 1.2, numLevels = 4, noiseAmt = 0.05, blurRadiusParam = 0) {
// Parameter parsing and validation
const applyGrayscale = String(applyGrayscaleStr).toLowerCase() === "true";
let contrast = Number(contrastVal);
if (isNaN(contrast)) {
contrast = 1.2; // Default contrast
}
let levels = Number(numLevels);
if (isNaN(levels) || levels < 1) {
levels = 4; // Default levels
}
levels = Math.floor(levels); // Ensure integer
let noiseIntensity = Number(noiseAmt);
if (isNaN(noiseIntensity)) {
noiseIntensity = 0.05; // Default noise intensity
}
noiseIntensity = Math.max(0, Math.min(1, noiseIntensity)); // Clamp between 0 and 1
let blurRadius = Number(blurRadiusParam);
if (isNaN(blurRadius) || blurRadius < 0) {
blurRadius = 0; // Default blur radius (no blur)
}
blurRadius = Math.floor(blurRadius); // Ensure integer
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Optional Blur Pass
if (blurRadius > 0) {
const M = canvas.width;
const N = canvas.height;
const sourceImageData = ctx.getImageData(0, 0, M, N);
const sourceData = sourceImageData.data;
const outputPixelData = new Uint8ClampedArray(sourceData.length);
for (let y = 0; y < N; y++) {
for (let x = 0; x < M; x++) {
let r_sum = 0, g_sum = 0, b_sum = 0;
let count = 0;
for (let dy = -blurRadius; dy <= blurRadius; dy++) {
for (let dx = -blurRadius; dx <= blurRadius; dx++) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < M && ny >= 0 && ny < N) {
const offset = (ny * M + nx) * 4;
r_sum += sourceData[offset];
g_sum += sourceData[offset + 1];
b_sum += sourceData[offset + 2];
count++;
}
}
}
const K = (y * M + x) * 4;
outputPixelData[K] = r_sum / count;
outputPixelData[K + 1] = g_sum / count;
outputPixelData[K + 2] = b_sum / count;
outputPixelData[K + 3] = sourceData[K + 3]; // Preserve alpha
}
}
const blurredImageData = new ImageData(outputPixelData, M, N);
ctx.putImageData(blurredImageData, 0, 0);
}
// Main Pixel Processing
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const quantize = (value, numQuantLevels) => {
if (numQuantLevels <= 1) {
return Math.round(value / 255) * 255; // Results in 0 or 255
}
const step = 255 / (numQuantLevels - 1);
return Math.round(value / step) * step;
};
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Grayscale (operates on potentially blurred data)
if (applyGrayscale) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
r = g = b = gray;
}
// 2. Contrast
if (contrast !== 1.0) { // Apply contrast if it's not neutral
r = contrast * (r - 128) + 128;
g = contrast * (g - 128) + 128;
b = contrast * (b - 128) + 128;
}
// Clamp after contrast and ensure integer values for posterization
r = Math.max(0, Math.min(255, Math.round(r)));
g = Math.max(0, Math.min(255, Math.round(g)));
b = Math.max(0, Math.min(255, Math.round(b)));
// 3. Posterization
r = quantize(r, levels);
g = quantize(g, levels);
b = quantize(b, levels);
// quantize function should already keep values within 0-255 if input is.
// 4. Noise
if (noiseIntensity > 0) {
// Generate noise between -128*intensity and +128*intensity
const noiseVal = (Math.random() - 0.5) * 2 * noiseIntensity * 128;
r += noiseVal;
g += noiseVal;
b += noiseVal;
}
// Final Clamp and store
data[i] = Math.max(0, Math.min(255, Math.round(r)));
data[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
data[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
// Alpha (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes