You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, neonPinkStr = "#FF00FF", neonCyanStr = "#00FFFF", shadowColorStr = "#1A0033", contrastVal = 1.8, saturationBoost = 1.8, brightnessThreshold = 0.55) {
// Helper function: Parse Hex color string to RGB object
function parseHex(hex) {
hex = hex.replace(/^#/, '');
const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return { r, g, b };
}
// Helper function: Convert RGB to HSL
// r, g, b are 0-255. h is 0-360. s, l are 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, 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;
}
if (d === 0) h = 0; // Should be covered by max === min, but defensive
else h /= 6;
}
return { h: h * 360, s: s, l: l };
}
// Helper function: Convert HSL to RGB
// h is 0-360. s, l are 0-1. r, g, b are 0-255.
function hslToRgb(h, s, l) {
let r, g, b;
h /= 360; // Normalize h to 0-1
if (s === 0) {
r = g = b = l; // achromatic
} else {
const 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;
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) };
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
if (!originalImg || !originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
console.error("Image is not loaded, invalid, or has zero dimensions.");
canvas.width = 300; canvas.height = 100;
ctx.fillStyle = "rgb(200,200,200)";
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText("Error: Image not loaded or invalid.", canvas.width/2, canvas.height/2 + 8);
return canvas;
}
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
try {
ctx.drawImage(originalImg, 0, 0);
} catch (e) {
console.error("Error drawing image to canvas:", e);
// Typically happens for security reasons with cross-origin images without CORS.
// Draw an error message ON the canvas if possible as fallback.
ctx.fillStyle = "rgb(200,200,200)";
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.font = `bold ${Math.min(24, canvas.width/15)}px Arial`;
ctx.fillStyle = "red";
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, canvas.width, canvas.height);
} catch (e) {
console.error("Cannot get image data (tainted canvas from cross-origin image?):", e);
// If canvas is tainted, we can't modify it. Return the canvas with the original image.
// The user will see the original image unchanged.
return canvas;
}
const data = imageData.data;
const neonPinkRgb = parseHex(neonPinkStr);
const neonCyanRgb = parseHex(neonCyanStr);
const shadowRgb = parseHex(shadowColorStr);
const pinkHsl = rgbToHsl(neonPinkRgb.r, neonPinkRgb.g, neonPinkRgb.b);
const cyanHsl = rgbToHsl(neonCyanRgb.r, neonCyanRgb.g, neonCyanRgb.b);
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];
// 1. Apply Contrast
let r_contrast = (r_orig / 255 - 0.5) * contrastVal + 0.5;
let g_contrast = (g_orig / 255 - 0.5) * contrastVal + 0.5;
let b_contrast = (b_orig / 255 - 0.5) * contrastVal + 0.5;
r_contrast = Math.max(0, Math.min(1, r_contrast)) * 255;
g_contrast = Math.max(0, Math.min(1, g_contrast)) * 255;
b_contrast = Math.max(0, Math.min(1, b_contrast)) * 255;
// 2. Get HSL of the contrasted pixel
const currentHsl = rgbToHsl(r_contrast, g_contrast, b_contrast);
const l_norm = currentHsl.l; // Lightness (0-1) of contrasted pixel
let final_r, final_g, final_b;
if (l_norm > brightnessThreshold) { // Highlight processing
let { h: h_current, s: s_current } = currentHsl;
// Boost saturation, ensuring it's high for neon effect
let s_new = Math.min(1, s_current * saturationBoost);
s_new = Math.max(0.85, s_new); // Ensure high saturation for neon glow
// Adjust HSL lightness to be consistently bright but retain some variation from original lightness
let l_new = Math.min(1.0, 0.6 + l_norm * 0.5); // Scale toward bright end
// Determine target hue based on contrasted pixel's dominant color (warm vs cool leaning)
// Simple decision: if red component is stronger than blue, lean towards pink neon. Otherwise, cyan.
let targetHue = (r_contrast > b_contrast) ? pinkHsl.h : cyanHsl.h;
const newRgb = hslToRgb(targetHue, s_new, l_new);
final_r = newRgb.r;
final_g = newRgb.g;
final_b = newRgb.b;
} else { // Shadow/Midtone processing
// Tint towards shadow color
// tintFactor = 0 at l_norm = brightnessThreshold, 1 at l_norm = 0
let tintFactor = (brightnessThreshold - l_norm) / brightnessThreshold;
tintFactor = Math.max(0, Math.min(1, tintFactor)); // Clamp due to potential floating point issues
tintFactor = Math.pow(tintFactor, 1.5); // Stronger tint for darker areas
final_r = r_contrast * (1 - tintFactor) + shadowRgb.r * tintFactor;
final_g = g_contrast * (1 - tintFactor) + shadowRgb.g * tintFactor;
final_b = b_contrast * (1 - tintFactor) + shadowRgb.b * tintFactor;
}
data[i] = Math.round(Math.max(0, Math.min(255, final_r)));
data[i + 1] = Math.round(Math.max(0, Math.min(255, final_g)));
data[i + 2] = Math.round(Math.max(0, Math.min(255, final_b)));
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes