You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, frameWidth = 50, frameColor = "#B08D57", borderColor = "#70543E", cornerRadius = 20, textureDensityFactor = 5, crackCount = 10) {
// Helper function to draw a rounded rectangle
function drawRoundedRect(ctx, x, y, width, height, radius) {
if (typeof radius === 'undefined') {
radius = 5;
}
if (typeof radius === 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
const defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (let side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
// Ensure radius is not too large
radius.tl = Math.min(radius.tl, height / 2, width / 2);
radius.tr = Math.min(radius.tr, height / 2, width / 2);
radius.br = Math.min(radius.br, height / 2, width / 2);
radius.bl = Math.min(radius.bl, height / 2, width / 2);
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.arcTo(x + width, y, x + width, y + radius.tr, radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.arcTo(x + width, y + height, x + width - radius.br, y + height, radius.br);
ctx.lineTo(x + radius.bl, y + height);
ctx.arcTo(x, y + height, x, y + height - radius.bl, radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.arcTo(x, y, x + radius.tl, y, radius.tl);
ctx.closePath();
}
// Helper function to shade a color (lighten or darken)
// percent is -1 to 1 (e.g., 0.1 for 10% lighter, -0.1 for 10% darker)
function shadeColor(color, percent) {
let R = parseInt(color.substring(1, 3), 16);
let G = parseInt(color.substring(3, 5), 16);
let B = parseInt(color.substring(5, 7), 16);
R = parseInt(R * (1 + percent));
G = parseInt(G * (1 + percent));
B = parseInt(B * (1 + percent));
R = Math.max(0, Math.min(255, R));
G = Math.max(0, Math.min(255, G));
B = Math.max(0, Math.min(255, B));
const RR = R.toString(16).padStart(2, '0');
const GG = G.toString(16).padStart(2, '0');
const BB = B.toString(16).padStart(2, '0');
return `#${RR}${GG}${BB}`;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const canvasWidth = originalImg.width + 2 * frameWidth;
const canvasHeight = originalImg.height + 2 * frameWidth;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const canvasArea = canvasWidth * canvasHeight;
const numTextureDots = Math.floor(canvasArea * textureDensityFactor / 1000);
// --- Start drawing ---
ctx.save();
// 1. Define Tablet Shape as Clip Path & Fill Base Color
drawRoundedRect(ctx, 0, 0, canvasWidth, canvasHeight, cornerRadius);
ctx.fillStyle = frameColor;
ctx.fill(); // Fill it first
ctx.clip(); // Then clip. Future draws are within this path.
// 2. Image Placement (directly on base, within clipped area)
ctx.drawImage(originalImg, frameWidth, frameWidth, originalImg.width, originalImg.height);
// 3. Stone Texture Dots (within clip, over image and frame area)
ctx.globalAlpha = 0.5; // Texture dots are semi-transparent
for (let i = 0; i < numTextureDots; i++) {
const randX = Math.random() * canvasWidth;
const randY = Math.random() * canvasHeight;
const dotColor = Math.random() > 0.5 ? shadeColor(frameColor, 0.15) : shadeColor(frameColor, -0.15);
ctx.fillStyle = dotColor;
ctx.beginPath();
ctx.arc(randX, randY, Math.random() * 1.5 + 0.5, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1.0; // Reset alpha
// 4. Crack Lines (within clip, over image and frame area)
ctx.globalAlpha = 0.6; // Cracks are also semi-transparent
for (let i = 0; i < crackCount; i++) {
ctx.strokeStyle = shadeColor(borderColor, -0.3); // Darker cracks
ctx.lineWidth = Math.random() * 1.2 + 0.5;
const sx = Math.random() * canvasWidth;
const sy = Math.random() * canvasHeight;
ctx.beginPath();
ctx.moveTo(sx, sy);
const numSegments = Math.floor(Math.random() * 3) + 2; // 2 to 4 segments
let currX = sx;
let currY = sy;
const maxCrackSegmentLength = frameWidth > 0 ? frameWidth * 0.7 : canvasWidth * 0.1;
for (let j = 0; j < numSegments; j++) {
const nextX = currX + (Math.random() - 0.5) * maxCrackSegmentLength;
const nextY = currY + (Math.random() - 0.5) * maxCrackSegmentLength;
ctx.lineTo(nextX, nextY);
currX = nextX;
currY = nextY;
}
ctx.stroke();
}
ctx.globalAlpha = 1.0; // Reset alpha
ctx.restore(); // Remove the clip path
// 5. Outer Border Stroke for the Tablet
const outerBorderWidth = Math.max(1.5, frameWidth * 0.06);
drawRoundedRect(ctx, outerBorderWidth / 2, outerBorderWidth / 2, canvasWidth - outerBorderWidth, canvasHeight - outerBorderWidth, Math.max(0, cornerRadius - outerBorderWidth / 2));
ctx.strokeStyle = borderColor;
ctx.lineWidth = outerBorderWidth;
ctx.stroke();
// Optional: A very subtle inner highlight on the border for a bit more depth
if (outerBorderWidth > 1) {
const highlightWidth = outerBorderWidth * 0.3;
drawRoundedRect(ctx, outerBorderWidth - highlightWidth/2 , outerBorderWidth - highlightWidth/2, canvasWidth - (outerBorderWidth*2) + highlightWidth, canvasHeight- (outerBorderWidth*2) + highlightWidth, Math.max(0, cornerRadius - outerBorderWidth));
ctx.strokeStyle = shadeColor(borderColor, 0.25); // Lighter shade of border
ctx.lineWidth = highlightWidth;
ctx.stroke();
}
return canvas;
}
Apply Changes