You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, color = "silver", contrast = 1.5, strength = 0.8) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Opt-in for performance if available
// Ensure originalImg is loaded and has dimensions
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (!imgWidth || !imgHeight) {
console.error("Image has no dimensions or is not loaded properly.");
// Return an empty canvas or draw an error message
canvas.width = 100; // Default error canvas size
canvas.height = 50;
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.fillText('Invalid Image', canvas.width / 2, canvas.height / 2);
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// Handle potential security errors (e.g., cross-origin image)
console.error("Error getting ImageData:", e);
// Return canvas with an error message drawn
ctx.clearRect(0,0, canvas.width, canvas.height); // Clear drawn image if it was from a tainted source
ctx.fillStyle = 'lightgray';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
let msg = 'Error: Could not process image.';
if (e.name === 'SecurityError') {
msg = 'Error: Cross-origin image security.';
}
const lines = msg.split('\n');
for(let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], canvas.width / 2, canvas.height / 2 - (lines.length-1)*8 + i*16);
}
return canvas;
}
const data = imageData.data;
let tintR = 1.0, tintG = 1.0, tintB = 1.0; // Default for "silver"
const lowerCaseColor = String(color).toLowerCase();
if (lowerCaseColor === "gold") {
tintR = 1.0; // R: 255
tintG = 215/255; // G: 215
tintB = 0/255; // B: 0
} else if (lowerCaseColor === "copper") {
tintR = 184/255; // R: 184
tintG = 115/255; // G: 115
tintB = 51/255; // B: 51
} else if (lowerCaseColor.startsWith("#")) {
const hex = lowerCaseColor.substring(1);
if (/^[0-9a-f]{6}$/.test(hex)) { // Match #RRGGBB
tintR = parseInt(hex.substring(0, 2), 16) / 255;
tintG = parseInt(hex.substring(2, 4), 16) / 255;
tintB = parseInt(hex.substring(4, 6), 16) / 255;
} else if (/^[0-9a-f]{3}$/.test(hex)) { // Match #RGB
tintR = parseInt(hex.substring(0, 1) + hex.substring(0, 1), 16) / 255;
tintG = parseInt(hex.substring(1, 2) + hex.substring(1, 2), 16) / 255;
tintB = parseInt(hex.substring(2, 3) + hex.substring(2, 3), 16) / 255;
}
// If hex is invalid, tint remains silver (1.0, 1.0, 1.0) as initialized
}
// Clamp parameters to sensible ranges
const effectiveContrast = Math.max(0, contrast); // Contrast factor shouldn't be negative
const effectiveStrength = Math.max(0, Math.min(1, strength)); // Strength must be [0, 1]
for (let i = 0; i < data.length; i += 4) {
const r_orig = data[i];
const g_orig = data[i + 1];
const b_orig = data[i + 2];
// 1. Grayscale conversion (luminance)
const luminance = 0.299 * r_orig + 0.587 * g_orig + 0.114 * b_orig;
// 2. Contrast adjustment
const normLum = luminance / 255; // Normalize to [0, 1]
// Apply contrast formula: y = factor * (x - 0.5) + 0.5
let contrastedLumNorm = (normLum - 0.5) * effectiveContrast + 0.5;
contrastedLumNorm = Math.max(0, Math.min(1, contrastedLumNorm)); // Clamp to [0, 1]
const contrastedLum = contrastedLumNorm * 255; // Scale back to [0, 255]
// 3. Apply tint to the contrasted luminance
const procR = contrastedLum * tintR;
const procG = contrastedLum * tintG;
const procB = contrastedLum * tintB;
// 4. Blend with original pixel based on strength
const finalR = (1 - effectiveStrength) * r_orig + effectiveStrength * procR;
const finalG = (1 - effectiveStrength) * g_orig + effectiveStrength * procG;
const finalB = (1 - effectiveStrength) * b_orig + effectiveStrength * procB;
// 5. Assign clamped values back to imageData
data[i] = Math.max(0, Math.min(255, Math.round(finalR)));
data[i + 1] = Math.max(0, Math.min(255, Math.round(finalG)));
data[i + 2] = Math.max(0, Math.min(255, Math.round(finalB)));
// Alpha channel (data[i + 3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes