You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, boardColorStr = "darkgreen", traceColorStr = "#B87333", componentColorStr = "dimgray", edgeThreshold = 200, componentLuminanceThreshold = 128) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Use naturalWidth/Height to ensure the image is loaded and get correct dimensions
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
console.error("Image not loaded or dimensions are zero.");
// Return a minimal canvas to avoid errors downstream
canvas.width = 1;
canvas.height = 1;
ctx.fillStyle = 'red'; // Indicate error
ctx.fillRect(0,0,1,1);
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) {
// This can happen due to tainted canvas (CORS) if image is cross-origin
console.error("Could not get image data for processing (CORS issue?):", e);
// Return the canvas with the original image drawn if processing is not possible
return canvas;
}
const data = imageData.data; // Source pixel data
const width = imageData.width;
const height = imageData.height;
const outputImageData = ctx.createImageData(width, height);
const outputData = outputImageData.data; // Target pixel data
// Helper function to parse color strings (e.g., "red", "#FF0000", "rgb(255,0,0)")
function parseColor(colorStr) {
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 1;
tempCanvas.height = 1;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.fillStyle = colorStr;
tempCtx.fillRect(0, 0, 1, 1);
const d = tempCtx.getImageData(0, 0, 1, 1).data;
return { r: d[0], g: d[1], b: d[2] };
}
const finalBoardColor = parseColor(boardColorStr);
const finalTraceColor = parseColor(traceColorStr);
const finalComponentColor = parseColor(componentColorStr);
// Helper to get grayscale value of a pixel from imageData, handling boundaries by clamping
function getGrayscale(sourceImageData, x, y, w, h) {
const currentX = Math.max(0, Math.min(x, w - 1)); // Clamp x
const currentY = Math.max(0, Math.min(y, h - 1)); // Clamp y
const i = (currentY * w + currentX) * 4;
// Standard NTSC/PAL luminance formula
return 0.299 * sourceImageData.data[i] + 0.587 * sourceImageData.data[i + 1] + 0.114 * sourceImageData.data[i + 2];
}
// Sobel operator kernels
const Gx = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const Gy = [
[-1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]
];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let gradX = 0;
let gradY = 0;
// Apply Sobel operator centered at (x,y)
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const kernelGxVal = Gx[ky + 1][kx + 1];
const kernelGyVal = Gy[ky + 1][kx + 1];
const grayVal = getGrayscale(imageData, x + kx, y + ky, width, height);
gradX += kernelGxVal * grayVal;
gradY += kernelGyVal * grayVal;
}
}
// Calculate magnitude of the gradient
const magnitude = Math.sqrt(gradX * gradX + gradY * gradY);
const outputIndex = (y * width + x) * 4;
if (magnitude > edgeThreshold) { // This pixel is an "edge" -> trace
outputData[outputIndex] = finalTraceColor.r;
outputData[outputIndex + 1] = finalTraceColor.g;
outputData[outputIndex + 2] = finalTraceColor.b;
outputData[outputIndex + 3] = 255; // Alpha
} else { // Not an edge -> board or component
const originalPixelIndex = (y * width + x) * 4;
const r_orig = data[originalPixelIndex];
const g_orig = data[originalPixelIndex + 1];
const b_orig = data[originalPixelIndex + 2];
// Luminance of the original pixel
const originalLuminance = 0.299 * r_orig + 0.587 * g_orig + 0.114 * b_orig;
let targetColor;
let baseLuminanceFactor;
if (originalLuminance > componentLuminanceThreshold) { // Brighter non-edge areas -> component like
targetColor = finalComponentColor;
// Modulate component color brightness slightly by original luminance (80%-100% range)
baseLuminanceFactor = 0.8 + 0.2 * (originalLuminance / 255.0);
} else { // Darker non-edge areas -> board like
targetColor = finalBoardColor;
// Modulate board color brightness by original luminance (50%-100% range) to give texture
baseLuminanceFactor = 0.5 + 0.5 * (originalLuminance / 255.0);
}
outputData[outputIndex] = Math.min(255, Math.max(0, Math.round(targetColor.r * baseLuminanceFactor)));
outputData[outputIndex + 1] = Math.min(255, Math.max(0, Math.round(targetColor.g * baseLuminanceFactor)));
outputData[outputIndex + 2] = Math.min(255, Math.max(0, Math.round(targetColor.b * baseLuminanceFactor)));
outputData[outputIndex + 3] = 255; // Alpha
}
}
}
ctx.putImageData(outputImageData, 0, 0);
return canvas;
}
Apply Changes