You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
filterType = "greenscale",
pixelationFactor = 0,
noiseAmount = 0.05,
timestampText = "auto",
timestampColor = "rgba(255, 220, 0, 0.9)",
timestampFontSize = 16,
timestampPosition = "bottom-right",
vignetteStrength = 0.3
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Ensure originalImg dimensions are available and valid
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
console.error("Image has zero dimensions or is not fully loaded.");
// Return a minimal canvas to avoid breaking downstream processing if any
canvas.width = 1;
canvas.height = 1;
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
// 1. Initial Draw / Pixelation
// Ensure_pixelationFactor_is_a_number_for_comparison
const pFactor = typeof pixelationFactor === 'number' ? pixelationFactor : 0;
if (pFactor > 1) {
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
const smallWidth = Math.max(1, Math.floor(canvas.width / pFactor));
const smallHeight = Math.max(1, Math.floor(canvas.height / pFactor));
tempCanvas.width = smallWidth;
tempCanvas.height = smallHeight;
tempCtx.drawImage(originalImg, 0, 0, smallWidth, smallHeight);
ctx.imageSmoothingEnabled = false;
// For broad compatibility, though modern browsers handle `imageSmoothingEnabled`
// ctx.mozImageSmoothingEnabled = false;
// ctx.webkitImageSmoothingEnabled = false;
// ctx.msImageSmoothingEnabled = false;
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
ctx.imageSmoothingEnabled = true; // Reset for other drawings
} else {
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
}
// 2. Color Filter and Noise
if (canvas.width > 0 && canvas.height > 0) { // Check needed if img dimensions could be 0
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const numNoiseAmount = typeof noiseAmount === 'number' ? noiseAmount : 0;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// Apply Color Filter
if (filterType === "greenscale") {
const gray = (r * 0.299 + g * 0.587 + b * 0.114);
data[i] = gray * 0.2;
data[i+1] = Math.min(255, gray * 1.2 + 30);
data[i+2] = gray * 0.2;
} else if (filterType === "grayscale") {
const gray = (r * 0.299 + g * 0.587 + b * 0.114);
data[i] = data[i+1] = data[i+2] = gray;
}
// If other filter types are added, handle them here.
// If filterType is none or unknown, original colors are kept (post-pixelation).
// Apply Noise
if (numNoiseAmount > 0) {
const noise = (Math.random() - 0.5) * 255 * numNoiseAmount;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i+1] = Math.max(0, Math.min(255, data[i+1] + noise));
data[i+2] = Math.max(0, Math.min(255, data[i+2] + noise));
}
}
ctx.putImageData(imageData, 0, 0);
}
// 3. Vignette Effect
const numVignetteStrength = typeof vignetteStrength === 'number' ? vignetteStrength : 0;
if (numVignetteStrength > 0) {
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
// innerRadiusStop defines how large the transparent center is.
// strength=0 => 0.7 (large clear area), strength=1 => 0.3 (small clear area)
const innerRadiusStop = Math.max(0, Math.min(1, 0.7 - (numVignetteStrength * 0.4)));
const gradient = ctx.createRadialGradient(
centerX, centerY, outerRadius * innerRadiusStop,
centerX, centerY, outerRadius
);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
// Darkness of vignette edges. Max opacity of ~0.85 at full strength.
gradient.addColorStop(1, `rgba(0,0,0,${Math.min(1, numVignetteStrength * 0.85)})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// 4. Timestamp
if (timestampText && typeof timestampText === 'string' && timestampText.trim() !== "") {
let textToDisplay = timestampText;
if (timestampText.toLowerCase() === "auto") {
const d = new Date();
const pad = (num) => num.toString().padStart(2, '0');
textToDisplay = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
if (textToDisplay) { // Ensure text is valid after auto logic
const FONT_SIZE = typeof timestampFontSize === 'number' && timestampFontSize > 0 ? timestampFontSize : 16;
const COLOR = typeof timestampColor === 'string' ? timestampColor : "rgba(255, 220, 0, 0.9)";
ctx.fillStyle = COLOR;
ctx.font = `${FONT_SIZE}px monospace`; // Monospace often used in camera overlays
const padding = Math.round(FONT_SIZE / 2.5);
let x, y;
if (timestampPosition === "bottom-left") {
ctx.textAlign = "left";
ctx.textBaseline = "bottom";
x = padding;
y = canvas.height - padding;
} else if (timestampPosition === "top-left") {
ctx.textAlign = "left";
ctx.textBaseline = "top";
x = padding;
y = padding;
} else if (timestampPosition === "top-right") {
ctx.textAlign = "right";
ctx.textBaseline = "top";
x = canvas.width - padding;
y = padding;
} else { // Default: "bottom-right"
ctx.textAlign = "right";
ctx.textBaseline = "bottom";
x = canvas.width - padding;
y = canvas.height - padding;
}
ctx.fillText(textToDisplay, x, y);
}
}
return canvas;
}
Apply Changes