You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts an image into a clean CAD-style blueprint.
* This is achieved by performing edge detection on the source image to create a line drawing,
* which is then rendered over a blue, gridded background.
*
* @param {HTMLImageElement} originalImg The original image object to process.
* @param {string} [backgroundColor='#0A3D91'] The hex color code for the blueprint background.
* @param {string} [lineColor='#FFFFFF'] The hex color code for the detected lines.
* @param {number} [detailThreshold=50] A threshold (0-255) for edge detection. Higher values result in fewer, more prominent lines.
* @param {number} [gridSpacing=25] The spacing in pixels for the background grid. Set to 0 to disable the grid.
* @returns {HTMLCanvasElement} A new canvas element displaying the blueprint image.
*/
function processImage(originalImg, backgroundColor = '#0A3D91', lineColor = '#FFFFFF', detailThreshold = 50, gridSpacing = 25) {
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
// 1. Create a processing canvas to get image data and perform calculations
const processCanvas = document.createElement('canvas');
processCanvas.width = width;
processCanvas.height = height;
const processCtx = processCanvas.getContext('2d', {
willReadFrequently: true
});
processCtx.drawImage(originalImg, 0, 0);
// 2. Get image data and convert to grayscale for simpler processing
const imageData = processCtx.getImageData(0, 0, width, height);
const data = imageData.data;
const grayData = new Uint8ClampedArray(width * height);
for (let i = 0; i < data.length; i += 4) {
// Use the luminosity method for better visual grayscale representation
const gray = 0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2];
grayData[i / 4] = gray;
}
// 3. Apply the Sobel operator to detect edges
const gradientData = new Float32Array(width * height);
const sobelX = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const sobelY = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
let maxMagnitude = 0;
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let gx = 0;
let gy = 0;
// Apply 3x3 kernel
for (let j = -1; j <= 1; j++) {
for (let i = -1; i <= 1; i++) {
const grayValue = grayData[(y + j) * width + (x + i)];
gx += grayValue * sobelX[j + 1][i + 1];
gy += grayValue * sobelY[j + 1][i + 1];
}
}
const magnitude = Math.sqrt(gx * gx + gy * gy);
const index = y * width + x;
gradientData[index] = magnitude;
if (magnitude > maxMagnitude) {
maxMagnitude = magnitude;
}
}
}
// 4. Create the final output canvas
const outputCanvas = document.createElement('canvas');
outputCanvas.width = width;
outputCanvas.height = height;
const outCtx = outputCanvas.getContext('2d');
// 5. Draw the blueprint background
outCtx.fillStyle = backgroundColor;
outCtx.fillRect(0, 0, width, height);
// 6. Draw the grid if enabled
if (gridSpacing > 0) {
outCtx.strokeStyle = 'rgba(255, 255, 255, 0.15)'; // A subtle grid color
outCtx.lineWidth = 0.5;
for (let i = gridSpacing; i < width; i += gridSpacing) {
outCtx.beginPath();
outCtx.moveTo(i, 0);
outCtx.lineTo(i, height);
outCtx.stroke();
}
for (let i = gridSpacing; i < height; i += gridSpacing) {
outCtx.beginPath();
outCtx.moveTo(0, i);
outCtx.lineTo(width, i);
outCtx.stroke();
}
}
// 7. Create a transparent image data layer to draw the lines on
const lineImageData = outCtx.createImageData(width, height);
const lineData = lineImageData.data;
// Parse the hex line color to RGB components for efficiency
const rLine = parseInt(lineColor.substring(1, 3), 16);
const gLine = parseInt(lineColor.substring(3, 5), 16);
const bLine = parseInt(lineColor.substring(5, 7), 16);
// 8. Draw the detected edges onto the line layer
for (let i = 0; i < gradientData.length; i++) {
// Normalize the magnitude to a 0-255 range for consistent thresholding
const normalizedMagnitude = (gradientData[i] / maxMagnitude) * 255;
if (normalizedMagnitude > detailThreshold) {
const index = i * 4;
lineData[index] = rLine;
lineData[index + 1] = gLine;
lineData[index + 2] = bLine;
lineData[index + 3] = 255; // Solid opaque lines for a clean look
}
}
// 9. Composite the line layer onto the canvas
outCtx.putImageData(lineImageData, 0, 0);
return outputCanvas;
}
Apply Changes