You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, brickSize = 20) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Ensure brickSize is a positive integer
brickSize = Math.max(1, Math.floor(brickSize));
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// Create a source canvas to draw the original image and get pixel data
// This avoids issues with tainted canvases or directly reading from Image objects
const sourceCanvas = document.createElement('canvas');
sourceCanvas.width = canvas.width;
sourceCanvas.height = canvas.height;
const sourceCtx = sourceCanvas.getContext('2d');
sourceCtx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
for (let y = 0; y < canvas.height; y += brickSize) {
for (let x = 0; x < canvas.width; x += brickSize) {
// Determine the actual size of the current block (handles edges correctly)
const blockW = Math.min(brickSize, canvas.width - x);
const blockH = Math.min(brickSize, canvas.height - y);
if (blockW <= 0 || blockH <= 0) continue;
// Get the pixel data for the current block from the source canvas
const imageData = sourceCtx.getImageData(x, y, blockW, blockH);
const data = imageData.data;
let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
let numPixels = 0;
for (let i = 0; i < data.length; i += 4) {
sumR += data[i];
sumG += data[i+1];
sumB += data[i+2];
sumA += data[i+3];
numPixels++;
}
if (numPixels === 0) continue;
const avgR = Math.floor(sumR / numPixels);
const avgG = Math.floor(sumG / numPixels);
const avgB = Math.floor(sumB / numPixels);
const avgA = Math.floor(sumA / numPixels);
// Draw the "brick" base color
ctx.fillStyle = `rgba(${avgR}, ${avgG}, ${avgB}, ${avgA / 255})`;
ctx.fillRect(x, y, blockW, blockH);
// Draw a border around the brick to give definition
const borderWidth = Math.max(1, Math.floor(brickSize * 0.04)); // Relative to brick size
if (blockW > borderWidth * 2 && blockH > borderWidth * 2) { // Ensure space for border
ctx.strokeStyle = `rgba(0, 0, 0, 0.15)`; // Semi-transparent dark border
ctx.lineWidth = borderWidth;
// Draw stroke inside the filled rectangle
ctx.strokeRect(x + borderWidth / 2, y + borderWidth / 2, blockW - borderWidth, blockH - borderWidth);
}
// Add "stud" effect if the brick is large enough
const studRenderThreshold = 10; // Minimum brickSize to draw a stud
if (brickSize >= studRenderThreshold && blockW >= studRenderThreshold && blockH >= studRenderThreshold) {
const studRadiusRatio = 0.28; // Radius of the stud relative to brickSize
const studActualRadius = brickSize * studRadiusRatio;
const studCenterX = x + blockW / 2;
const studCenterY = y + blockH / 2;
// Offset for pseudo-3D effect (shadow/highlight)
// Relative to stud's radius for scalability
const offsetAmount = studActualRadius * 0.1;
// Stud shadow (darker part)
ctx.fillStyle = `rgba(${Math.max(0, avgR - 35)}, ${Math.max(0, avgG - 35)}, ${Math.max(0, avgB - 35)}, ${avgA / 255})`;
ctx.beginPath();
ctx.arc(studCenterX + offsetAmount, studCenterY + offsetAmount, studActualRadius, 0, Math.PI * 2);
ctx.fill();
// Stud highlight (lighter part)
ctx.fillStyle = `rgba(${Math.min(255, avgR + 35)}, ${Math.min(255, avgG + 35)}, ${Math.min(255, avgB + 35)}, ${avgA / 255})`;
ctx.beginPath();
ctx.arc(studCenterX - offsetAmount, studCenterY - offsetAmount, studActualRadius, 0, Math.PI * 2);
ctx.fill();
// Main stud body (slightly brighter than base brick color, drawn on top)
const studBodyR = Math.min(255, avgR + 10);
const studBodyG = Math.min(255, avgG + 10);
const studBodyB = Math.min(255, avgB + 10);
ctx.fillStyle = `rgba(${studBodyR}, ${studBodyG}, ${studBodyB}, ${avgA / 255})`;
ctx.beginPath();
// Make main body slightly smaller so shadow/highlight create an edge
ctx.arc(studCenterX, studCenterY, studActualRadius * 0.92, 0, Math.PI * 2);
ctx.fill();
}
}
}
return canvas;
}
Apply Changes