You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies various special effects to an image.
*
* @param {Image} originalImg The original javascript Image object.
* @param {string} effectType The type of effect to apply. Possible values are: 'grayscale', 'sepia', 'invert', 'solarize', 'posterize', 'blur', 'sharpen', 'emboss', 'edge'. Defaults to 'grayscale'.
* @param {number} blurAmount The amount of blur to apply (in pixels) for the 'blur' effect. Defaults to 4.
* @param {number} posterizeLevels The number of color levels for the 'posterize' effect. Should be >= 2. Defaults to 4.
* @param {number} solarizeThreshold The threshold (0-255) for the 'solarize' effect. Pixels below this value are inverted. Defaults to 128.
* @returns {HTMLCanvasElement} A canvas element with the processed image.
*/
function processImage(originalImg, effectType = 'grayscale', blurAmount = 4, posterizeLevels = 4, solarizeThreshold = 128) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const {
width,
height
} = originalImg;
canvas.width = width;
canvas.height = height;
// Helper function to apply a convolution kernel matrix to image data.
const applyKernel = (kernel, offset = 0) => {
const src = ctx.getImageData(0, 0, width, height);
const srcData = src.data;
const dst = ctx.createImageData(width, height);
const dstData = dst.data;
const kernelSize = Math.round(Math.sqrt(kernel.length));
const half = Math.floor(kernelSize / 2);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const dstOff = (y * width + x) * 4;
let r = 0, g = 0, b = 0;
for (let cy = 0; cy < kernelSize; cy++) {
for (let cx = 0; cx < kernelSize; cx++) {
const scy = Math.min(height - 1, Math.max(0, y + cy - half));
const scx = Math.min(width - 1, Math.max(0, x + cx - half));
const srcOff = (scy * width + scx) * 4;
const wt = kernel[cy * kernelSize + cx];
r += srcData[srcOff] * wt;
g += srcData[srcOff + 1] * wt;
b += srcData[srcOff + 2] * wt;
}
}
dstData[dstOff] = r + offset;
dstData[dstOff + 1] = g + offset;
dstData[dstOff + 2] = b + offset;
dstData[dstOff + 3] = srcData[dstOff + 3]; // copy alpha
}
}
ctx.putImageData(dst, 0, 0);
};
switch (effectType.toLowerCase()) {
case 'blur':
ctx.filter = `blur(${Math.max(0, blurAmount)}px)`;
ctx.drawImage(originalImg, 0, 0);
break;
case 'sharpen':
ctx.drawImage(originalImg, 0, 0);
applyKernel([
0, -1, 0,
-1, 5, -1,
0, -1, 0
]);
break;
case 'emboss':
ctx.drawImage(originalImg, 0, 0);
applyKernel([
-2, -1, 0,
-1, 1, 1,
0, 1, 2
], 128);
break;
case 'edge':
ctx.drawImage(originalImg, 0, 0);
applyKernel([
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
]);
break;
case 'sepia':
case 'invert':
case 'solarize':
case 'posterize':
case 'grayscale':
default:
ctx.drawImage(originalImg, 0, 0);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
switch (effectType.toLowerCase()) {
case 'sepia':
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
break;
case 'invert':
data[i] = 255 - r;
data[i + 1] = 255 - g;
data[i + 2] = 255 - b;
break;
case 'solarize':
const threshold = Math.max(0, Math.min(255, solarizeThreshold));
if (r < threshold) data[i] = 255 - r;
if (g < threshold) data[i + 1] = 255 - g;
if (b < threshold) data[i + 2] = 255 - b;
break;
case 'posterize':
const levels = Math.max(2, Math.floor(posterizeLevels));
const step = 255 / (levels - 1);
data[i] = Math.round(r / step) * step;
data[i + 1] = Math.round(g / step) * step;
data[i + 2] = Math.round(b / step) * step;
break;
case 'grayscale':
default:
const avg = 0.299 * r + 0.587 * g + 0.114 * b;
data[i] = avg;
data[i + 1] = avg;
data[i + 2] = avg;
break;
}
}
ctx.putImageData(imageData, 0, 0);
break;
}
// Reset filter in case it was used
ctx.filter = 'none';
return canvas;
}
Apply Changes