You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, cellSize = 10, shapeType = "circle", minShapeFactor = 0.1, maxShapeFactor = 0.9, foregroundColor = "black", backgroundColor = "white", invertEffectStr = "false") {
// Parameter validation and type conversion
cellSize = Number(cellSize);
minShapeFactor = Number(minShapeFactor);
maxShapeFactor = Number(maxShapeFactor);
const invertEffect = invertEffectStr.toLowerCase() === 'true';
if (isNaN(cellSize) || cellSize <= 0) {
cellSize = 10;
}
if (isNaN(minShapeFactor) || minShapeFactor < 0 || minShapeFactor > 1) {
minShapeFactor = 0.1;
}
if (isNaN(maxShapeFactor) || maxShapeFactor < 0 || maxShapeFactor > 1) {
maxShapeFactor = 0.9;
}
if (minShapeFactor > maxShapeFactor) {
[minShapeFactor, maxShapeFactor] = [maxShapeFactor, minShapeFactor]; // Swap if min > max
}
if (typeof shapeType !== 'string' || (shapeType !== 'circle' && shapeType !== 'square')) {
shapeType = 'circle'; // Default to circle if invalid
}
if (typeof foregroundColor !== 'string') {
foregroundColor = 'black';
}
if (typeof backgroundColor !== 'string') {
backgroundColor = 'white';
}
const outputCanvas = document.createElement('canvas');
// Ensure canvas dimensions are integers if originalImg dimensions are not
outputCanvas.width = Math.floor(originalImg.width);
outputCanvas.height = Math.floor(originalImg.height);
// Prevent issues if image has 0 width/height
if (outputCanvas.width === 0 || outputCanvas.height === 0) {
return outputCanvas; // Return empty canvas
}
const ctx = outputCanvas.getContext('2d');
// Draw background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
// Create a temporary canvas to get pixel data from originalImg
const tempCanvas = document.createElement('canvas');
tempCanvas.width = outputCanvas.width;
tempCanvas.height = outputCanvas.height;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); // Optimization hint for frequent getImageData
try {
tempCtx.drawImage(originalImg, 0, 0, outputCanvas.width, outputCanvas.height);
} catch (e) {
// Error drawing original image (e.g., if originalImg is not valid or fully loaded)
// Return canvas with background only
return outputCanvas;
}
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
} catch (e) {
// Error getting image data (e.g., tainted canvas due to CORS issues)
// Return canvas with background only
return outputCanvas;
}
const data = imageData.data;
const imageWidth = imageData.width; // This will be tempCanvas.width
const imageHeight = imageData.height; // This will be tempCanvas.height
// Helper function to get average brightness (luminance) of a cell
function getAverageBrightness(cellPixelX, cellPixelY, cellWidth, cellHeight) {
let totalLuminance = 0;
let pixelCount = 0;
// Define the actual region to sample pixels from, clamped to image dimensions
const startX = Math.floor(cellPixelX);
const startY = Math.floor(cellPixelY);
const endX = Math.min(startX + Math.floor(cellWidth), imageWidth);
const endY = Math.min(startY + Math.floor(cellHeight), imageHeight);
for (let y = startY; y < endY; y++) {
for (let x = startX; x < endX; x++) {
const i = (y * imageWidth + x) * 4; // Calculate index in the 1D pixel array
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Alpha (data[i+3]) is ignored for brightness calculation here
// Luminance formula (perceptual brightness), normalized to 0-1
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
totalLuminance += luminance;
pixelCount++;
}
}
return pixelCount > 0 ? totalLuminance / pixelCount : 0;
}
ctx.fillStyle = foregroundColor;
// Iterate over cells in a grid
for (let y = 0; y < imageHeight; y += cellSize) {
for (let x = 0; x < imageWidth; x += cellSize) {
// Determine actual width and height of the current cell, handling edges
const currentCellWidth = Math.min(cellSize, imageWidth - x);
const currentCellHeight = Math.min(cellSize, imageHeight - y);
if (currentCellWidth <= 0 || currentCellHeight <= 0) continue; // Skip if cell has no area
// Calculate average brightness for the current cell
let normalizedBrightness = getAverageBrightness(x, y, currentCellWidth, currentCellHeight);
if (invertEffect) {
normalizedBrightness = 1 - normalizedBrightness;
}
// Modulate shape size based on brightness
const shapeSizeFactor = minShapeFactor + normalizedBrightness * (maxShapeFactor - minShapeFactor);
// Determine the base dimension for scaling the shape (use smaller of cell width/height)
const baseDimensionForShape = Math.min(currentCellWidth, currentCellHeight);
const actualShapeSize = baseDimensionForShape * shapeSizeFactor;
// Calculate center of the current cell to draw the shape
const centerX = x + currentCellWidth / 2;
const centerY = y + currentCellHeight / 2;
// Draw the shape
if (shapeType === "circle") {
const radius = actualShapeSize / 2;
if (radius > 0) { // Only draw if radius is positive
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.fill();
}
} else if (shapeType === "square") {
if (actualShapeSize > 0) { // Only draw if side length is positive
ctx.fillRect(centerX - actualShapeSize / 2, centerY - actualShapeSize / 2, actualShapeSize, actualShapeSize);
}
}
}
}
return outputCanvas;
}
Apply Changes