You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, lineColor = '#0000FF', backgroundColor = '#F0F8FF', threshold = 128, invertLines = 0, gridColor = 'rgba(173, 216, 230, 0.5)', gridSize = 20, showGrid = 1) {
// Validate input image
if (!(originalImg instanceof HTMLImageElement) || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
console.error("Invalid image input: Must be a loaded HTMLImageElement with dimensions greater than 0.");
const errorCanvas = document.createElement('canvas');
errorCanvas.width = 300; // Arbitrary size for error message
errorCanvas.height = 100;
const errorCtx = errorCanvas.getContext('2d');
errorCtx.fillStyle = '#FFDDDD'; // Light red background for error
errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
errorCtx.strokeStyle = 'red';
errorCtx.lineWidth = 1;
errorCtx.strokeRect(0, 0, errorCanvas.width, errorCanvas.height);
errorCtx.fillStyle = 'black';
errorCtx.font = '16px Arial';
errorCtx.textAlign = 'center';
errorCtx.textBaseline = 'middle';
errorCtx.fillText('Invalid Image Input', errorCanvas.width / 2, errorCanvas.height / 2);
return errorCanvas;
}
// Normalize parameters
threshold = Math.max(0, Math.min(255, Number(threshold)));
const shouldInvertLines = Number(invertLines) === 1; // Convert 0/1 to boolean logic
gridSize = Math.max(1, Number(gridSize)); // Ensure gridSize is at least 1
const shouldShowGrid = Number(showGrid) === 1; // Convert 0/1 to boolean logic
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const imgWidth = originalImg.naturalWidth;
const imgHeight = originalImg.naturalHeight;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Helper function to parse a color string (hex, rgb, name) to an RGBA object
// This is needed to apply the lineColor with its specific RGBA components to pixel data.
function getRgbaFromColorString(colorStr) {
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 1;
tempCanvas.height = 1;
const tempCtx = tempCanvas.getContext('2d');
// Important: Clear with transparent before filling to get correct alpha for fully transparent colors like 'transparent'
tempCtx.clearRect(0,0,1,1);
tempCtx.fillStyle = colorStr; // Browser parses the color string
tempCtx.fillRect(0, 0, 1, 1);
const colorData = tempCtx.getImageData(0, 0, 1, 1).data;
return { r: colorData[0], g: colorData[1], b: colorData[2], a: colorData[3] };
}
const parsedLineColor = getRgbaFromColorString(lineColor);
// 1. Fill canvas with backgroundColor
// The canvas fillStyle property can directly take CSS color strings.
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, imgWidth, imgHeight);
// 2. Draw optional grid
if (shouldShowGrid && gridSize > 0) {
ctx.strokeStyle = gridColor;
ctx.lineWidth = 0.5; // Use a thin line for the grid
// Draw vertical grid lines
for (let x = 0; x < imgWidth; x += gridSize) {
ctx.beginPath();
ctx.moveTo(x + 0.5, 0); // Adding 0.5 for sharper lines
ctx.lineTo(x + 0.5, imgHeight);
ctx.stroke();
}
// Draw horizontal grid lines
for (let y = 0; y < imgHeight; y += gridSize) {
ctx.beginPath();
ctx.moveTo(0, y + 0.5);
ctx.lineTo(imgWidth, y + 0.5);
ctx.stroke();
}
}
// 3. Process the image to create the schematic effect
// Use a temporary canvas to draw the original image and access its pixel data
const tempCanvasForImage = document.createElement('canvas');
const tempCtxForImage = tempCanvasForImage.getContext('2d');
tempCanvasForImage.width = imgWidth;
tempCanvasForImage.height = imgHeight;
tempCtxForImage.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
let imageData;
try {
imageData = tempCtxForImage.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
console.error("Error getting image data. This can happen with cross-origin images if CORS is not configured on the server.", e);
// If getImageData fails, display an error on the canvas
ctx.clearRect(0,0,canvas.width,canvas.height); // Clear previously drawn background/grid
ctx.fillStyle = '#FFDDDD';
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.strokeStyle = 'red';
ctx.lineWidth = 1;
ctx.strokeRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Error processing image (CORS issue?)', canvas.width/2, canvas.height/2);
return canvas;
}
const data = imageData.data; // Pixel data array: [R,G,B,A, R,G,B,A, ...]
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Convert pixel to grayscale using the luminance formula
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
let isLinePixel;
if (shouldInvertLines) {
// Lighter parts of the original image become lines
isLinePixel = gray > threshold;
} else {
// Darker parts of the original image become lines
isLinePixel = gray < threshold;
}
if (isLinePixel) {
// Set pixel to line color
data[i] = parsedLineColor.r;
data[i + 1] = parsedLineColor.g;
data[i + 2] = parsedLineColor.b;
data[i + 3] = parsedLineColor.a; // Use alpha from parsedLineColor
} else {
// Make non-line parts transparent to let the background and grid show through
data[i + 3] = 0; // Set Alpha to 0 (fully transparent)
}
}
// 4. Draw the processed image data (which now contains the schematic lines) onto the main canvas
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes