You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, numLevels = 5, sensitivity = 5, lineColor = "black", backgroundColor = "white") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// Ensure width and height are not zero to prevent errors
if (canvas.width === 0 || canvas.height === 0) {
// Return an empty canvas or handle as an error
console.warn("Image has zero width or height.");
return canvas;
}
// Draw original image to get its pixel data
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 if the image is tainted (e.g., cross-origin)
console.error("Could not get image data: ", e);
// Draw a placeholder or return the original (or an empty canvas)
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas
ctx.fillStyle = "red";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText("Error: Cannot process cross-origin image.", canvas.width / 2, canvas.height / 2);
return canvas;
}
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
const outputImageData = ctx.createImageData(width, height);
const outputData = outputImageData.data;
// Helper to parse color string to [r, g, b, a] array
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);
return tempCtx.getImageData(0, 0, 1, 1).data; // Returns Uint8ClampedArray [r, g, b, a]
}
const [lr, lg, lb, la] = parseColor(lineColor);
const [br, bg, bb, ba] = parseColor(backgroundColor);
// 1. Grayscale conversion
const grayscaleData = new Uint8ClampedArray(width * height);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const grayscale = 0.299 * r + 0.587 * g + 0.114 * b;
grayscaleData[i / 4] = grayscale;
}
// 2. Quantization and Contour Detection
const safeNumLevels = Math.max(1, numLevels); // Ensure numLevels is at least 1
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const currentIndex = y * width + x;
const currentGrayscale = grayscaleData[currentIndex];
const currentLevel = Math.floor(currentGrayscale * safeNumLevels / 256);
let isContour = false;
// Compare with right neighbor
if (x + 1 < width) {
const rightGrayscale = grayscaleData[currentIndex + 1];
const rightLevel = Math.floor(rightGrayscale * safeNumLevels / 256);
if (currentLevel !== rightLevel && Math.abs(currentGrayscale - rightGrayscale) >= sensitivity) {
isContour = true;
}
}
// Compare with bottom neighbor (if not already a contour)
if (!isContour && y + 1 < height) {
const bottomGrayscale = grayscaleData[currentIndex + width];
const bottomLevel = Math.floor(bottomGrayscale * safeNumLevels / 256);
if (currentLevel !== bottomLevel && Math.abs(currentGrayscale - bottomGrayscale) >= sensitivity) {
isContour = true;
}
}
const outputPixelIndex = currentIndex * 4;
if (isContour) {
outputData[outputPixelIndex] = lr;
outputData[outputPixelIndex + 1] = lg;
outputData[outputPixelIndex + 2] = lb;
outputData[outputPixelIndex + 3] = la;
} else {
outputData[outputPixelIndex] = br;
outputData[outputPixelIndex + 1] = bg;
outputData[outputPixelIndex + 2] = bb;
outputData[outputPixelIndex + 3] = ba;
}
}
}
ctx.putImageData(outputImageData, 0, 0);
return canvas;
}
Apply Changes