You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies a maze pattern filter effect to an image.
* The original image is used as the background for the paths of the maze,
* and walls are drawn on top.
*
* @param {Image} originalImg The original JavaScript Image object (should be loaded).
* @param {number} [cellSize=20] The size of each cell in the maze grid.
* @param {string} [wallColor="black"] The color of the maze walls.
* @param {number} [wallThickness=2] The thickness of the maze walls.
* @returns {HTMLCanvasElement} A canvas element with the maze effect applied.
*/
function processImage(originalImg, cellSize = 20, wallColor = "black", wallThickness = 2) {
// 1. Canvas setup
const canvas = document.createElement('canvas');
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
// Image not loaded or invalid
console.warn("Image dimensions are zero. Ensure the image is loaded before processing.");
// Return a small canvas with an error message
canvas.width = 200;
canvas.height = 150;
const ctxError = canvas.getContext('2d');
if (ctxError) {
ctxError.fillStyle = 'lightgray';
ctxError.fillRect(0, 0, canvas.width, canvas.height);
ctxError.fillStyle = 'red';
ctxError.textAlign = 'center';
ctxError.font = '12px Arial';
ctxError.fillText("Error: Image not loaded or invalid.", canvas.width / 2, canvas.height / 2);
}
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
if (!ctx) {
// Fallback for environments where canvas context might not be available
console.error("Could not get 2D context from canvas.");
const errDiv = document.createElement('div');
errDiv.textContent = "Error: Canvas 2D context not available.";
return errDiv;
}
// 2. Draw original image onto the canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// Parameter validation and adjustment
if (cellSize <= 0) cellSize = 20;
if (wallThickness < 0) wallThickness = 0;
// 3. Maze grid initialization
const numCols = Math.floor(imgWidth / cellSize);
const numRows = Math.floor(imgHeight / cellSize);
// If maze dimensions are too small (e.g., cellSize larger than image dimensions),
// no maze can be meaningfully generated. Return the canvas with the original image.
if (numCols <= 0 || numRows <= 0) {
return canvas;
}
const grid = [];
for (let r = 0; r < numRows; r++) {
grid[r] = [];
for (let c = 0; c < numCols; c++) {
grid[r][c] = {
r: r, // Row index
c: c, // Column index
walls: [true, true, true, true], // [Top, Right, Bottom, Left] - true if wall exists
visited: false
};
}
}
// 4. Maze Generation (Depth-First Search algorithm)
const stack = [];
// Start DFS from a random cell for variety
let currentRow = Math.floor(Math.random() * numRows);
let currentCol = Math.floor(Math.random() * numCols);
let currentCell = grid[currentRow][currentCol];
currentCell.visited = true;
stack.push(currentCell);
let visitedCells = 1;
const totalCells = numRows * numCols;
while (visitedCells < totalCells && stack.length > 0) {
currentCell = stack[stack.length - 1]; // Peek at the top of the stack (current cell)
let r = currentCell.r;
let c = currentCell.c;
const unvisitedNeighbors = [];
// Check North neighbor
if (r > 0 && !grid[r - 1][c].visited) {
unvisitedNeighbors.push({ cell: grid[r - 1][c], currentWallIndex: 0, neighborWallIndex: 2 }); // Current's Top, Neighbor's Bottom
}
// Check East neighbor
if (c < numCols - 1 && !grid[r][c + 1].visited) {
unvisitedNeighbors.push({ cell: grid[r][c + 1], currentWallIndex: 1, neighborWallIndex: 3 }); // Current's Right, Neighbor's Left
}
// Check South neighbor
if (r < numRows - 1 && !grid[r + 1][c].visited) {
unvisitedNeighbors.push({ cell: grid[r + 1][c], currentWallIndex: 2, neighborWallIndex: 0 }); // Current's Bottom, Neighbor's Top
}
// Check West neighbor
if (c > 0 && !grid[r][c - 1].visited) {
unvisitedNeighbors.push({ cell: grid[r][c - 1], currentWallIndex: 3, neighborWallIndex: 1 }); // Current's Left, Neighbor's Right
}
if (unvisitedNeighbors.length > 0) {
// Choose a random unvisited neighbor
const randomIndex = Math.floor(Math.random() * unvisitedNeighbors.length);
const chosenNeighborData = unvisitedNeighbors[randomIndex];
const nextCell = chosenNeighborData.cell;
// Remove wall between currentCell and nextCell
currentCell.walls[chosenNeighborData.currentWallIndex] = false;
nextCell.walls[chosenNeighborData.neighborWallIndex] = false;
// Move to the chosen neighbor
nextCell.visited = true;
visitedCells++;
stack.push(nextCell);
} else {
// No unvisited neighbors, backtrack
stack.pop();
}
}
// 5. Draw Maze Walls
// Only draw walls if wallThickness is greater than 0
if (wallThickness > 0) {
ctx.strokeStyle = wallColor;
ctx.lineWidth = wallThickness;
// 'square' makes lines extend slightly for better corner connection, 'butt' ends lines exactly at coordinates
ctx.lineCap = 'square';
ctx.beginPath();
for (let r = 0; r < numRows; r++) {
for (let c = 0; c < numCols; c++) {
const cell = grid[r][c];
const x = c * cellSize;
const y = r * cellSize;
// Draw Top wall of cell (r,c) if it exists
if (cell.walls[0]) {
ctx.moveTo(x, y);
ctx.lineTo(x + cellSize, y);
}
// Draw Right wall of cell (r,c) if it exists
if (cell.walls[1]) {
ctx.moveTo(x + cellSize, y);
ctx.lineTo(x + cellSize, y + cellSize);
}
// Draw Bottom wall of cell (r,c) if it exists
if (cell.walls[2]) {
ctx.moveTo(x + cellSize, y + cellSize);
ctx.lineTo(x, y + cellSize);
}
// Draw Left wall of cell (r,c) if it exists
if (cell.walls[3]) {
ctx.moveTo(x, y + cellSize);
ctx.lineTo(x, y);
}
}
}
ctx.stroke(); // Draw all accumulated line segments
}
// 6. Return the canvas with the maze effect
return canvas;
}
Apply Changes