You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg,
noiseAmount = 0.08,
scanlineThickness = 1,
scanlineGap = 1,
scanlineAlpha = 0.1,
showTimestamp = 1, // Use 1 for true, 0 for false
timestampPrefix = "CAM 01 REC ",
timestampFontSize = 14,
timestampFontColor = "rgba(255, 255, 180, 0.7)", // Light yellow, semi-transparent
timestampBgColor = "rgba(0, 0, 0, 0.3)" // Dark semi-transparent background
) {
const canvas = document.createElement('canvas');
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
if (width === 0 || height === 0) {
console.error("Image has zero dimensions.");
// Return a canvas with an error message
const errCanvas = document.createElement('canvas');
errCanvas.width = 150;
errCanvas.height = 30;
const errCtx = errCanvas.getContext('2d');
if (errCtx) {
errCtx.font = '12px sans-serif';
errCtx.fillStyle = 'red';
errCtx.fillText('Invalid image dimensions', 5, 20);
}
return errCanvas;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error("Failed to get 2D context from canvas.");
// Fallback to returning an empty canvas or the original image if it's displayable
return canvas; // Return the empty canvas
}
// 1. Draw original image
ctx.drawImage(originalImg, 0, 0, width, height);
// 2. Apply Grayscale + Noise
try {
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Grayscale (luminosity method)
let gray = 0.299 * r + 0.587 * g + 0.114 * b;
// Add noise
if (noiseAmount > 0) {
// Generate noise in [-1, 1), scale by noiseAmount and 255
const randomNoise = (Math.random() * 2 - 1) * noiseAmount * 255;
gray += randomNoise;
}
// Clamp value to [0, 255]
gray = Math.max(0, Math.min(255, gray));
data[i] = gray;
data[i + 1] = gray;
data[i + 2] = gray;
// Alpha (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
} catch (e) {
console.error("Error processing image data (possibly CORS issue if image is cross-origin and canvas is tainted):", e);
// If we can't process pixels, we might still draw scanlines and timestamp over original.
// For now, we'll continue, effects might be drawn over colored image.
}
// 3. Draw Scanlines
if (scanlineThickness > 0 && scanlineAlpha > 0) {
const totalLineAndGapHeight = scanlineThickness + scanlineGap;
if (totalLineAndGapHeight > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${scanlineAlpha})`;
for (let y = 0; y < height; y += totalLineAndGapHeight) {
ctx.fillRect(0, y, width, scanlineThickness);
}
}
}
// 4. Draw Timestamp
if (showTimestamp === 1) {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
const dateTimeString = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
const fullTimestampText = `${timestampPrefix}${dateTimeString}`;
ctx.font = `${timestampFontSize}px monospace`;
ctx.textBaseline = 'top';
const textMetrics = ctx.measureText(fullTimestampText);
const textWidth = textMetrics.width;
// Approximate text height using font size. For more accuracy, could use
// textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent,
// but timestampFontSize is often sufficient for layout.
const textHeight = timestampFontSize;
// Padding for the text from canvas edge and for background around text
const osdMargin = 5; // Margin of OSD block from canvas edge
const textInternalPadding = Math.max(2, Math.round(timestampFontSize * 0.15)); // Padding around text within its background
const textX = osdMargin;
const textY = osdMargin;
// Draw background for timestamp for better readability
if (timestampBgColor && timestampBgColor !== "transparent" && timestampBgColor !== "none") {
ctx.fillStyle = timestampBgColor;
ctx.fillRect(
textX - textInternalPadding,
textY - textInternalPadding,
textWidth + 2 * textInternalPadding,
textHeight + 2 * textInternalPadding
);
}
// Draw timestamp text
ctx.fillStyle = timestampFontColor;
ctx.fillText(fullTimestampText, textX, textY);
}
return canvas;
}
Apply Changes