You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, posterLevels = 5, distortionAmplitude = 10, distortionFrequency = 0.05, hueShift = 90) {
// --- Helper: rgbToHsl ---
// r, g, b are in [0, 255]
// returns h in [0, 360), s in [0, 1], l in [0, 1]
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s;
const l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s, l: l };
}
// --- Helper: hslToRgb ---
// h in [0, 360), s in [0, 1], l in [0, 1]
// returns r, g, b in [0, 255]
function hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
h /= 360; // normalize h to [0,1] for calculations
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
// Parameter Sanitization
let pLevelsVal = Number(posterLevels);
if (isNaN(pLevelsVal)) pLevelsVal = 5;
const finalPosterLevels = Math.max(2, Math.floor(pLevelsVal));
let dAmplitudeVal = Number(distortionAmplitude);
if (isNaN(dAmplitudeVal)) dAmplitudeVal = 10;
let dFrequencyVal = Number(distortionFrequency);
if (isNaN(dFrequencyVal)) dFrequencyVal = 0.05;
let hShiftVal = Number(hueShift);
if (isNaN(hShiftVal)) hShiftVal = 90;
const finalHueShiftNormalized = (hShiftVal % 360 + 360) % 360;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
if (imgWidth === 0 || imgHeight === 0) {
console.error("Image has zero dimensions. Cannot process.");
ctx.fillStyle = "gray";
ctx.fillRect(0,0, canvas.width || 100, canvas.height || 30); // Draw something on small canvas
ctx.fillStyle = "red";
ctx.font = "12px Arial";
ctx.textAlign = "center";
const message = "Error: Image has zero dimensions.";
if (canvas.width > 0 && canvas.height > 0){
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
} // else nothing can be drawn usefully.
return canvas;
}
try {
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
} catch (e) {
console.error("Error drawing original image: ", e);
ctx.fillStyle = "red";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText("Error: Could not draw image.", canvas.width / 2, canvas.height / 2);
return canvas;
}
let imageData;
try {
imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
console.error("Could not getImageData: ", e);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText("Error: Cross-origin issue or other problem reading image data.", canvas.width / 2, canvas.height / 2, canvas.width * 0.9);
return canvas;
}
const data = imageData.data;
const outputImageData = ctx.createImageData(imgWidth, imgHeight);
const outputData = outputImageData.data;
const posterFactor = 255 / (finalPosterLevels - 1);
for (let y = 0; y < imgHeight; y++) {
for (let x = 0; x < imgWidth; x++) {
// 1. Calculate source coordinates for distortion
let srcX = x + dAmplitudeVal * Math.sin(y * dFrequencyVal + x * dFrequencyVal * 0.1);
let srcY = y + dAmplitudeVal * Math.cos(x * dFrequencyVal - y * dFrequencyVal * 0.1);
// Clamp srcX and srcY to be within image bounds
srcX = Math.max(0, Math.min(imgWidth - 1, srcX));
srcY = Math.max(0, Math.min(imgHeight - 1, srcY));
const iSrcX = Math.round(srcX);
const iSrcY = Math.round(srcY);
// 2. Get original pixel data from (iSrcX, iSrcY)
const originalPixelIndex = (iSrcY * imgWidth + iSrcX) * 4;
let r = data[originalPixelIndex];
let g = data[originalPixelIndex + 1];
let b = data[originalPixelIndex + 2];
const a = data[originalPixelIndex + 3];
// 3. Apply posterization
r = Math.round(r / posterFactor) * posterFactor;
g = Math.round(g / posterFactor) * posterFactor;
b = Math.round(b / posterFactor) * posterFactor;
// 4. Convert to HSL, shift hue, convert back to RGB
let hsl = rgbToHsl(r, g, b);
hsl.h = (hsl.h + finalHueShiftNormalized) % 360;
let rgbShifted = hslToRgb(hsl.h, hsl.s, hsl.l);
r = rgbShifted.r;
g = rgbShifted.g;
b = rgbShifted.b;
// 5. Set the pixel in the output ImageData
const outputPixelIndex = (y * imgWidth + x) * 4;
outputData[outputPixelIndex] = r;
outputData[outputPixelIndex + 1] = g;
outputData[outputPixelIndex + 2] = b;
outputData[outputPixelIndex + 3] = a;
}
}
ctx.putImageData(outputImageData, 0, 0);
return canvas;
}
Apply Changes