You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, lineRGBColor = "0,200,50", lineWidth = 1, lineSpacing = 4, centerOffsetXRatio = 0, centerOffsetYRatio = 0, distortion = 8, noiseFrequency = 5, minLineOpacity = 0.1, maxLineOpacity = 0.7, imageBlendOpacity = 1.0) {
// Ensure numeric parameters are valid and within sensible ranges
lineWidth = Math.max(0, Number(lineWidth));
lineSpacing = Math.max(1, Number(lineSpacing)); // Must be at least 1
centerOffsetXRatio = Number(centerOffsetXRatio);
centerOffsetYRatio = Number(centerOffsetYRatio);
distortion = Math.max(0, Number(distortion));
noiseFrequency = Number(noiseFrequency); // Can be any number, typically small integers
minLineOpacity = Math.max(0, Math.min(1, Number(minLineOpacity)));
maxLineOpacity = Math.max(0, Math.min(1, Number(maxLineOpacity)));
imageBlendOpacity = Math.max(0, Math.min(1, Number(imageBlendOpacity)));
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
if (canvas.width === 0 || canvas.height === 0) {
return canvas; // Or throw an error for invalid image dimensions
}
// Draw the original image with the specified blend opacity
if (imageBlendOpacity > 0) {
ctx.globalAlpha = imageBlendOpacity;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1.0; // Reset global alpha for subsequent drawing
}
// Create a temporary canvas to get pixel data from the original image
// This is used to determine brightness for modulating line opacity
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCtx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = tempCtx.getImageData(0, 0, canvas.width, canvas.height);
// Parse lineRGBColor string "R,G,B" into numeric R, G, B values
const colorParts = lineRGBColor.split(',');
const r = parseInt(colorParts[0], 10) || 0;
const g = parseInt(colorParts[1], 10) || 0;
const b = parseInt(colorParts[2], 10) || 0;
// Calculate the center of the fingerprint pattern
const actualCenterX = canvas.width / 2 + centerOffsetXRatio * canvas.width;
const actualCenterY = canvas.height / 2 + centerOffsetYRatio * canvas.height;
ctx.lineWidth = lineWidth;
if (lineWidth === 0) { // No lines to draw if lineWidth is 0
return canvas;
}
// Helper function to get brightness (luminance) at a point (0-1 range)
function getBrightness(x, y, imgData, imgWidth, imgHeight) {
const ix = Math.round(x);
const iy = Math.round(y);
// Return 0 for points outside image bounds (treat as dark)
if (ix < 0 || ix >= imgWidth || iy < 0 || iy >= imgHeight) {
return 0;
}
const pixelIdx = (iy * imgWidth + ix) * 4;
const R = imgData.data[pixelIdx];
const G = imgData.data[pixelIdx + 1];
const B = imgData.data[pixelIdx + 2];
// Standard luminance calculation
return (0.299 * R + 0.587 * G + 0.114 * B) / 255;
}
// Determine the maximum radius needed to cover the entire canvas from the fingerprint center
const distToCorner1 = Math.sqrt(actualCenterX**2 + actualCenterY**2);
const distToCorner2 = Math.sqrt((canvas.width - actualCenterX)**2 + actualCenterY**2);
const distToCorner3 = Math.sqrt(actualCenterX**2 + (canvas.height - actualCenterY)**2);
const distToCorner4 = Math.sqrt((canvas.width - actualCenterX)**2 + (canvas.height - actualCenterY)**2);
const maxLoopRadius = Math.max(distToCorner1, distToCorner2, distToCorner3, distToCorner4) + lineSpacing;
// Iterate outwards from the center, drawing concentric, distorted lines
for (let radius = 0; radius < maxLoopRadius; radius += lineSpacing) {
let prevX = NaN;
let prevY = NaN;
// Calculate number of angular steps for this radius.
// More steps for larger radii to maintain segment detail.
const circumference = 2 * Math.PI * radius;
// Target segment length aims to balance detail and performance.
// It's based on distortion (more detail for higher distortion) and lineSpacing.
const targetSegmentLength = Math.max(1, distortion / 2, lineSpacing / 2);
const numAngleSteps = (radius === 0 && circumference === 0) ? 1 : Math.max(20, Math.ceil(circumference / targetSegmentLength));
for (let i = 0; i <= numAngleSteps; i++) {
const angle = (i / numAngleSteps) * 2 * Math.PI;
// Apply sinusoidal distortion to the radius
// The 'radius * 0.1' term makes the pattern evolve with increasing radius
const rOffset = Math.sin(angle * noiseFrequency + radius * 0.1) * distortion * 0.5;
const currentR = radius + rOffset;
// Calculate point coordinates with added random jitter
const x = actualCenterX + currentR * Math.cos(angle) + (Math.random() - 0.5) * distortion * 0.5;
const y = actualCenterY + currentR * Math.sin(angle) + (Math.random() - 0.5) * distortion * 0.5;
if (i > 0 && !isNaN(prevX) && !isNaN(prevY)) {
// Get brightness at the midpoint of the current segment
const midX = (x + prevX) / 2;
const midY = (y + prevY) / 2;
const brightness = getBrightness(midX, midY, imageData, canvas.width, canvas.height);
// Modulate line opacity based on brightness
let alpha = minLineOpacity + (maxLineOpacity - minLineOpacity) * brightness;
alpha = Math.max(0, Math.min(1, alpha)); // Clamp alpha to [0, 1]
// Draw the segment if its alpha is above a small threshold
if (alpha > 0.005) {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`;
ctx.stroke();
}
}
prevX = x;
prevY = y;
}
}
return canvas;
}
Apply Changes