You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, frameSize = 30, frameColor = "saddlebrown", paddingSize = 15, matColor = "ivory", decorationSpread = 40, decorationColor = "darkgreen", leafSize = 15, leafDensity = 0.7) {
// Coerce and validate numeric parameters
frameSize = Number(frameSize);
paddingSize = Number(paddingSize);
decorationSpread = Number(decorationSpread);
leafSize = Number(leafSize);
leafDensity = Number(leafDensity);
if (isNaN(frameSize) || frameSize < 0) frameSize = 30;
if (isNaN(paddingSize) || paddingSize < 0) paddingSize = 15;
if (isNaN(decorationSpread) || decorationSpread < 0) decorationSpread = 40;
if (isNaN(leafSize) || leafSize <= 0) leafSize = 15;
if (isNaN(leafDensity) || leafDensity < 0) leafDensity = 0;
if (leafDensity > 1) leafDensity = 1;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const dW = originalImg.width;
const dH = originalImg.height;
// Handle cases where the original image has no valid dimensions
if (dW <= 0 || dH <= 0) {
canvas.width = Math.max(1, 2 * paddingSize + 2 * frameSize + 2 * decorationSpread);
canvas.height = Math.max(1, 2 * paddingSize + 2 * frameSize + 2 * decorationSpread);
ctx.fillStyle = matColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (typeof console !== 'undefined' && console.warn) { // Check for console existence
console.warn("Original image has zero or invalid dimension. Returning empty framed canvas.");
}
return canvas;
}
canvas.width = dW + 2 * paddingSize + 2 * frameSize + 2 * decorationSpread;
canvas.height = dH + 2 * paddingSize + 2 * frameSize + 2 * decorationSpread;
// 1. Fill entire canvas with matColor (background for decoration area and padding)
ctx.fillStyle = matColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Draw the frame rectangle
const frameRectX = decorationSpread;
const frameRectY = decorationSpread;
const frameRectW = dW + 2 * paddingSize + 2 * frameSize;
const frameRectH = dH + 2 * paddingSize + 2 * frameSize;
ctx.fillStyle = frameColor;
ctx.fillRect(frameRectX, frameRectY, frameRectW, frameRectH);
// 3. "Cut out" the center for padding area by drawing matColor over the frame
const innerMatX = decorationSpread + frameSize;
const innerMatY = decorationSpread + frameSize;
const innerMatW = dW + 2 * paddingSize;
const innerMatH = dH + 2 * paddingSize;
ctx.fillStyle = matColor;
ctx.fillRect(innerMatX, innerMatY, innerMatW, innerMatH);
// 4. Draw the original image
const imgDrawX = decorationSpread + frameSize + paddingSize;
const imgDrawY = decorationSpread + frameSize + paddingSize;
ctx.drawImage(originalImg, imgDrawX, imgDrawY, dW, dH);
// 5. Draw botanical decorations if conditions are met
if (leafDensity > 0 && decorationSpread > 0 && leafSize > 0) {
// Helper function to draw a single botanical leaf
function drawBotanicalLeaf(pivotX, pivotY, lSize, angleDegrees, lColor) {
ctx.save();
ctx.translate(pivotX, pivotY);
ctx.rotate(angleDegrees * Math.PI / 180);
// Leaf shape: tip at (0, -lSize*0.6), base at (0, lSize*0.6) in local coords when angle=0
// This means the leaf is drawn along its local Y-axis.
const tipY = -lSize * 0.6;
const baseY = lSize * 0.6;
const midBulgeX = lSize * 0.35; // Width of the leaf bulge
const midBulgeYControl = lSize * 0.2; // Y position of control point for bulge
const midNarrowX = lSize * 0.25; // Width at narrower part near tip
const midNarrowYControl = -lSize * 0.2; // Y position of control point for narrow part
ctx.beginPath();
ctx.moveTo(0, tipY);
ctx.quadraticCurveTo(midNarrowX, midNarrowYControl, midBulgeX, midBulgeYControl);
ctx.quadraticCurveTo(lSize * 0.1, lSize * 0.5, 0, baseY);
ctx.quadraticCurveTo(-lSize * 0.1, lSize * 0.5, -midBulgeX, midBulgeYControl);
ctx.quadraticCurveTo(-midNarrowX, midNarrowYControl, 0, tipY);
ctx.fillStyle = lColor;
ctx.fill();
// Simple vein for added detail
ctx.strokeStyle = 'rgba(0,0,0,0.18)';
ctx.lineWidth = Math.max(1, lSize / 18);
ctx.beginPath();
ctx.moveTo(0, tipY * 0.85);
ctx.lineTo(0, baseY * 0.65);
ctx.stroke();
ctx.restore();
}
// Calculate step for leaf placement based on density
// Smaller step = denser leaves
const step = Math.max(leafSize * 0.4, leafSize * (1.8 - leafDensity * 1.4));
// Function to process drawing leaves along one edge of the frame
const processEdge = (iterStartX, iterStartY, edgeLength, edgeType) => {
for (let i = 0; i < edgeLength; i += step) {
const currentLeafSize = leafSize * (0.75 + Math.random() * 0.5); // Vary size
const actual_S_base = currentLeafSize * 0.6; // Distance from pivot to leaf's local base point
// How far from the frame's actual edge the leaf base should be.
// Random value within decorationSpread.
const randomOffsetFromEdge = Math.random() * decorationSpread * 0.8;
let angleDeg, targetBaseX, targetBaseY;
const angleRadVariation = (Math.random() - 0.5) * 70; // +/- 35 degrees variation
if (edgeType === "top") {
angleDeg = 0 + angleRadVariation; // Points towards canvas -Y (up)
targetBaseX = iterStartX + i + (Math.random() - 0.5) * step; // Jitter along edge
targetBaseY = iterStartY - randomOffsetFromEdge;
} else if (edgeType === "bottom") {
angleDeg = 180 + angleRadVariation; // Points towards canvas +Y (down)
targetBaseX = iterStartX + i + (Math.random() - 0.5) * step;
targetBaseY = iterStartY + randomOffsetFromEdge;
} else if (edgeType === "left") {
angleDeg = 90 + angleRadVariation; // Points towards canvas -X (left)
targetBaseX = iterStartX - randomOffsetFromEdge;
targetBaseY = iterStartY + i + (Math.random() - 0.5) * step;
} else { // edgeType === "right"
angleDeg = -90 + angleRadVariation; // Points towards canvas +X (right)
targetBaseX = iterStartX + randomOffsetFromEdge;
targetBaseY = iterStartY + i + (Math.random() - 0.5) * step;
}
const angleRad = angleDeg * Math.PI / 180;
// Calculate pivot point for drawBotanicalLeaf based on where the base should be
const pivotX = targetBaseX + actual_S_base * Math.sin(angleRad);
const pivotY = targetBaseY - actual_S_base * Math.cos(angleRad);
drawBotanicalLeaf(pivotX, pivotY, currentLeafSize, angleDeg, decorationColor);
}
};
// Process each of the four edges for decoration
// iterStartX, iterStartY are the coordinates of the frame edge itself.
processEdge(frameRectX, frameRectY, frameRectW, "top");
processEdge(frameRectX, frameRectY + frameRectH, frameRectW, "bottom");
processEdge(frameRectX, frameRectY, frameRectH, "left");
processEdge(frameRectX + frameRectW, frameRectY, frameRectH, "right");
}
return canvas;
}
Apply Changes