You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, frameWidth = 50, tileSize = 10, gap = 2, tileColorPalette = "gold,darkred,#654321,#4B0082,darkgreen,#A0522D,saddlebrown", groutColor = "#404040", irregularityFactor = 0.3) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Ensure frameWidth, tileSize, gap are not negative
frameWidth = Math.max(0, frameWidth);
tileSize = Math.max(1, tileSize); // Tile size should be at least 1
gap = Math.max(0, gap);
irregularityFactor = Math.max(0, Math.min(1, irregularityFactor)); // Clamp between 0 and 1
const newWidth = originalImg.width + 2 * frameWidth;
const newHeight = originalImg.height + 2 * frameWidth;
canvas.width = newWidth;
canvas.height = newHeight;
// Parse colors
const colors = tileColorPalette.split(',')
.map(c => c.trim())
.filter(c => c.length > 0);
if (colors.length === 0) {
colors.push("gold"); // Default if parsing fails or palette is empty
}
// 1. Fill with grout color
ctx.fillStyle = groutColor;
ctx.fillRect(0, 0, newWidth, newHeight);
// 2. Draw the original image in the center (if frameWidth > 0)
if (frameWidth > 0) {
ctx.drawImage(originalImg, frameWidth, frameWidth, originalImg.width, originalImg.height);
} else { // No frame, just draw the original image
ctx.drawImage(originalImg, 0, 0, originalImg.width, originalImg.height);
return canvas; // No frame to create
}
// 3. Draw mosaic frame
const effectiveTileSize = tileSize + gap; // The grid cell size for one tile
// Helper function to draw a single irregular tile
function drawTile(baseX, baseY, nominalWidth, nominalHeight, color, shapeIrregularity) {
ctx.fillStyle = color;
// Max distance a vertex can move, scaled by irregularityFactor.
// Multiplied by 0.5 because (Math.random() - 0.5) * 2 gives range [-1, 1], so effect is up to maxVertexOffset.
const maxVertexOffset = Math.min(nominalWidth, nominalHeight) * shapeIrregularity * 0.5;
ctx.beginPath();
// Top-left
ctx.moveTo(baseX + (Math.random() - 0.5) * 2 * maxVertexOffset, baseY + (Math.random() - 0.5) * 2 * maxVertexOffset);
// Top-right
ctx.lineTo(baseX + nominalWidth + (Math.random() - 0.5) * 2 * maxVertexOffset, baseY + (Math.random() - 0.5) * 2 * maxVertexOffset);
// Bottom-right
ctx.lineTo(baseX + nominalWidth + (Math.random() - 0.5) * 2 * maxVertexOffset, baseY + nominalHeight + (Math.random() - 0.5) * 2 * maxVertexOffset);
// Bottom-left
ctx.lineTo(baseX + (Math.random() - 0.5) * 2 * maxVertexOffset, baseY + nominalHeight + (Math.random() - 0.5) * 2 * maxVertexOffset);
ctx.closePath();
ctx.fill();
}
// Helper function to draw tiles in a specific rectangular section of the frame
const drawFrameSection = (sectionX, sectionY, sectionWidth, sectionHeight) => {
if (sectionWidth <= 0 || sectionHeight <= 0) return; // Nothing to draw in this section
ctx.save();
ctx.beginPath();
ctx.rect(sectionX, sectionY, sectionWidth, sectionHeight);
ctx.clip(); // Clip drawing to this section
// Iterate over grid cells. Start cell coordinates slightly outside to ensure full coverage with jitter.
for (let y = sectionY - effectiveTileSize / 2; y < sectionY + sectionHeight + effectiveTileSize / 2; y += effectiveTileSize) {
for (let x = sectionX - effectiveTileSize / 2; x < sectionX + sectionWidth + effectiveTileSize / 2; x += effectiveTileSize) {
// Cell's top-left corner
const cellBaseX = x;
const cellBaseY = y;
// Ideal center of the tile if it were perfectly centered in its cell
const tileTheoreticalCenterX = cellBaseX + effectiveTileSize / 2;
const tileTheoreticalCenterY = cellBaseY + effectiveTileSize / 2;
// Top-left of the tile, initially centered
let drawX = tileTheoreticalCenterX - tileSize / 2;
let drawY = tileTheoreticalCenterY - tileSize / 2;
// Add positional jitter. Max jitter offset is scaled by irregularityFactor and gap.
const jitterRange = gap * irregularityFactor;
const jitterOffsetX = (Math.random() - 0.5) * jitterRange;
const jitterOffsetY = (Math.random() - 0.5) * jitterRange;
drawX += jitterOffsetX;
drawY += jitterOffsetY;
const chosenColor = colors[Math.floor(Math.random() * colors.length)];
// Parameters for drawTile: x, y, width, height, color, shapeIrregularity
drawTile(drawX, drawY, tileSize, tileSize, chosenColor, irregularityFactor);
}
}
ctx.restore(); // Remove clipping
};
// Define and draw the four frame sections
// Top border
drawFrameSection(0, 0, newWidth, frameWidth);
// Bottom border
drawFrameSection(0, originalImg.height + frameWidth, newWidth, frameWidth);
// Left border (between top and bottom ones, covers full height of original image)
drawFrameSection(0, frameWidth, frameWidth, originalImg.height);
// Right border (between top and bottom ones, covers full height of original image)
drawFrameSection(originalImg.width + frameWidth, frameWidth, frameWidth, originalImg.height);
return canvas;
}
Apply Changes