You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts an image into a blueprint-style architectural rendering.
*
* @param {HTMLImageElement} originalImg The original image element.
* @param {string} lineColor The color of the lines and text in hex format.
* @param {string} backgroundColor The background color of the blueprint in hex format.
* @param {number} threshold The sensitivity for edge detection. Lower values detect more faint edges.
* @param {number} gridSize The size of the grid squares in pixels. Set to 0 to disable the grid.
* @returns {HTMLCanvasElement} A canvas element with the blueprint rendering.
*/
async function processImage(originalImg, lineColor = '#a0c8d7', backgroundColor = '#152b44', threshold = 50, gridSize = 30) {
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
// Create the final output canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 1. Draw the blueprint background color
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, width, height);
// 2. Draw a faint grid
if (gridSize > 0) {
ctx.strokeStyle = lineColor;
ctx.lineWidth = 0.5;
ctx.globalAlpha = 0.2; // Make grid lines faint
for (let x = 0; x < width; x += gridSize) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
for (let y = 0; y < height; y += gridSize) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
ctx.globalAlpha = 1.0; // Reset alpha for subsequent drawing
}
// 3. Process the image for edge detection
// Create a temporary canvas to get image data
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(originalImg, 0, 0);
const imageData = tempCtx.getImageData(0, 0, width, height);
const data = imageData.data;
// Convert image to grayscale for easier processing
const grayscaleData = [];
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
grayscaleData.push(gray);
}
// Apply Sobel filter for edge detection
ctx.fillStyle = lineColor;
const sobelX = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const sobelY = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
// Loop through each pixel (skipping the border)
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let gx = 0;
let gy = 0;
// Apply 3x3 convolution kernel
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const idx = (y + ky) * width + (x + kx);
const gray = grayscaleData[idx];
gx += gray * sobelX[ky + 1][kx + 1];
gy += gray * sobelY[ky + 1][kx + 1];
}
}
const magnitude = Math.sqrt(gx * gx + gy * gy);
// If the magnitude is above the threshold, draw a pixel
if (magnitude > threshold) {
ctx.fillRect(x, y, 1, 1);
}
}
}
// 4. Add professional-looking border and title block
const margin = Math.min(width, height) * 0.03;
const innerMargin = margin * 0.5;
// Outer border
ctx.strokeStyle = lineColor;
ctx.lineWidth = Math.max(1, Math.round(margin * 0.1));
ctx.strokeRect(margin, margin, width - 2 * margin, height - 2 * margin);
// Inner border
ctx.lineWidth = Math.max(1, Math.round(margin * 0.05));
ctx.strokeRect(margin + innerMargin, margin + innerMargin, width - 2 * (margin + innerMargin), height - 2 * (margin + innerMargin));
// Title Block Area
const tbWidth = Math.min(width * 0.3, 400);
const tbHeight = Math.min(height * 0.15, 150);
const tbX = width - margin - innerMargin - tbWidth;
const tbY = height - margin - innerMargin - tbHeight;
// Erase content behind title block and draw its border
ctx.fillStyle = backgroundColor;
ctx.fillRect(tbX, tbY, tbWidth, tbHeight);
ctx.strokeRect(tbX, tbY, tbWidth, tbHeight);
// Title block text
ctx.fillStyle = lineColor;
const fontSize = Math.max(10, Math.round(tbHeight * 0.1));
ctx.font = `${fontSize}px 'Courier New', monospace`;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
const textPadding = tbWidth * 0.05;
const lineHeight = fontSize * 1.5;
ctx.fillText("PROJ. NO: 2024-A1", tbX + textPadding, tbY + textPadding);
ctx.fillText("DRAWN BY: AI-ARCH", tbX + textPadding, tbY + textPadding + lineHeight);
ctx.fillText(`DATE: ${new Date().toLocaleDateString()}`, tbX + textPadding, tbY + textPadding + 2 * lineHeight);
ctx.fillText("SCALE: N.T.S", tbX + textPadding, tbY + textPadding + 3 * lineHeight);
// Dividing lines in the title block
ctx.beginPath();
ctx.moveTo(tbX, tbY + lineHeight * 1.25);
ctx.lineTo(tbX + tbWidth, tbY + lineHeight * 1.25);
ctx.moveTo(tbX, tbY + lineHeight * 2.5);
ctx.lineTo(tbX + tbWidth, tbY + lineHeight * 2.5);
ctx.moveTo(tbX, tbY + lineHeight * 3.75);
ctx.lineTo(tbX + tbWidth, tbY + lineHeight * 3.75);
ctx.stroke();
return canvas;
}
Apply Changes