You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
frameThicknessStr = '50', // Default frame thickness on one side, as string
outerFrameColor = '#6F4E37', // Dark wood color for the main outer frame
innerGoldColor = '#B8860B', // Gold color for the inner decorative part
highlightColor = '#D4AF37', // Color for highlights (e.g., lighter gold or wood)
shadowColor = '#4B3621' // Color for shadows (e.g., darker wood or generic shadow)
) {
const frameThickness = Math.max(0, Number(frameThicknessStr));
const newWidth = originalImg.width + 2 * frameThickness;
const newHeight = originalImg.height + 2 * frameThickness;
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
const ctx = canvas.getContext('2d');
if (frameThickness === 0) {
ctx.drawImage(originalImg, 0, 0, originalImg.width, originalImg.height);
return canvas;
}
// Proportions for frame sections (sum to 1.0)
const p_outerBevel = 0.05; // Outermost edge bevel
const p_outerWood = 0.50; // Main wooden part
const p_groove = 0.10; // Decorative groove or inner shadow
const p_innerGold = 0.30; // Main golden part
const p_innerBevel = 0.05; // Innermost bevel around the image (total should be 1.0)
// Calculate actual pixel thicknesses for each band using cumulative offsets.
// This ensures that the sum of rounded thicknesses equals frameThickness.
const off_outerBevelEnd = Math.round(frameThickness * p_outerBevel);
const off_outerWoodEnd = Math.round(frameThickness * (p_outerBevel + p_outerWood));
const off_grooveEnd = Math.round(frameThickness * (p_outerBevel + p_outerWood + p_groove));
const off_innerGoldEnd = Math.round(frameThickness * (p_outerBevel + p_outerWood + p_groove + p_innerGold));
const off_innerBevelEnd = frameThickness; // This corresponds to Math.round(frameThickness * 1.0)
const thick_outerBevel = off_outerBevelEnd;
const thick_outerWood = off_outerWoodEnd - off_outerBevelEnd;
const thick_groove = off_grooveEnd - off_outerWoodEnd;
const thick_innerGold = off_innerGoldEnd - off_grooveEnd;
const thick_innerBevel = off_innerBevelEnd - off_innerGoldEnd;
// Ensure all calculated thicknesses are non-negative, which can happen if frameThickness is very small.
// The calculation method should already prevent this unless frameThickness itself is 0 (handled).
// However, as a safeguard for individual thicknesses if some proportions are tiny:
const final_thick_outerBevel = Math.max(0, thick_outerBevel);
const final_thick_outerWood = Math.max(0, thick_outerWood);
const final_thick_groove = Math.max(0, thick_groove);
const final_thick_innerGold = Math.max(0, thick_innerGold);
const final_thick_innerBevel = Math.max(0, thick_innerBevel);
let currentX = 0;
let currentY = 0;
let currentW = newWidth;
let currentH = newHeight;
// Layer 1: Outer Bevel (frame appears raised from wall)
// Light source from top-left: Top/Left edges are highlighted, Bottom/Right edges are shadowed.
if (final_thick_outerBevel > 0) {
ctx.fillStyle = highlightColor;
ctx.fillRect(currentX, currentY, currentW, final_thick_outerBevel); // Top highlight bar
ctx.fillRect(currentX, currentY, final_thick_outerBevel, currentH); // Left highlight bar (overwrites TL corner to HL)
ctx.fillStyle = shadowColor;
ctx.fillRect(currentX, currentY + currentH - final_thick_outerBevel, currentW, final_thick_outerBevel); // Bottom shadow bar (overwrites BL corner to shadow)
ctx.fillRect(currentX + currentW - final_thick_outerBevel, currentY, final_thick_outerBevel, currentH); // Right shadow bar (overwrites TR and BR corners to shadow)
currentX += final_thick_outerBevel;
currentY += final_thick_outerBevel;
currentW -= 2 * final_thick_outerBevel;
currentH -= 2 * final_thick_outerBevel;
}
// Layer 2: Main Outer Frame (Wood Color)
// This is a flat band of outerFrameColor.
if (final_thick_outerWood > 0) {
ctx.fillStyle = outerFrameColor;
ctx.fillRect(currentX, currentY, currentW, final_thick_outerWood); // Top
ctx.fillRect(currentX, currentY + currentH - final_thick_outerWood, currentW, final_thick_outerWood); // Bottom
ctx.fillRect(currentX, currentY + final_thick_outerWood, final_thick_outerWood, currentH - 2 * final_thick_outerWood); // Left
ctx.fillRect(currentX + currentW - final_thick_outerWood, currentY + final_thick_outerWood, final_thick_outerWood, currentH - 2 * final_thick_outerWood); // Right
currentX += final_thick_outerWood;
currentY += final_thick_outerWood;
currentW -= 2 * final_thick_outerWood;
currentH -= 2 * final_thick_outerWood;
}
// Layer 3: Decorative Groove (typically a shadow color to give depth)
if (final_thick_groove > 0) {
ctx.fillStyle = shadowColor; // Using the general shadow color for the groove
ctx.fillRect(currentX, currentY, currentW, final_thick_groove); // Top
ctx.fillRect(currentX, currentY + currentH - final_thick_groove, currentW, final_thick_groove); // Bottom
ctx.fillRect(currentX, currentY + final_thick_groove, final_thick_groove, currentH - 2 * final_thick_groove); // Left
ctx.fillRect(currentX + currentW - final_thick_groove, currentY + final_thick_groove, final_thick_groove, currentH - 2 * final_thick_groove); // Right
currentX += final_thick_groove;
currentY += final_thick_groove;
currentW -= 2 * final_thick_groove;
currentH -= 2 * final_thick_groove;
}
// Layer 4: Main Inner Frame (e.g., Gold Color)
// This is a flat band of innerGoldColor.
if (final_thick_innerGold > 0) {
ctx.fillStyle = innerGoldColor;
ctx.fillRect(currentX, currentY, currentW, final_thick_innerGold); // Top
ctx.fillRect(currentX, currentY + currentH - final_thick_innerGold, currentW, final_thick_innerGold); // Bottom
ctx.fillRect(currentX, currentY + final_thick_innerGold, final_thick_innerGold, currentH - 2 * final_thick_innerGold); // Left
ctx.fillRect(currentX + currentW - final_thick_innerGold, currentY + final_thick_innerGold, final_thick_innerGold, currentH - 2 * final_thick_innerGold); // Right
currentX += final_thick_innerGold;
currentY += final_thick_innerGold;
currentW -= 2 * final_thick_innerGold;
currentH -= 2 * final_thick_innerGold;
}
// Layer 5: Inner Bevel (image appears recessed into the frame)
// Light source from top-left: Image's Top/Left edges are shadowed, Bottom/Right edges are highlighted.
if (final_thick_innerBevel > 0) {
ctx.fillStyle = shadowColor;
ctx.fillRect(currentX, currentY, currentW, final_thick_innerBevel); // Top shadow bar
ctx.fillRect(currentX, currentY, final_thick_innerBevel, currentH); // Left shadow bar (overwrites TL corner to shadow)
ctx.fillStyle = highlightColor;
ctx.fillRect(currentX, currentY + currentH - final_thick_innerBevel, currentW, final_thick_innerBevel); // Bottom highlight bar (overwrites BL corner to HL)
ctx.fillRect(currentX + currentW - final_thick_innerBevel, currentY, final_thick_innerBevel, currentH); // Right highlight bar (overwrites TR and BR corners to HL)
// No update to currentX, currentY, etc. as this is the innermost graphical frame band.
// The space available for image is (currentX, currentY, currentW, currentH). Drawing the bands fills the periphery.
}
// Draw the original image in the very center space
// The image position (frameThickness, frameThickness) refers to the top-left corner of the image content.
// The calculated currentX, currentY after all band offset applications should correspond to frameThickness.
ctx.drawImage(originalImg, frameThickness, frameThickness, originalImg.width, originalImg.height);
return canvas;
}
Apply Changes