You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, couchColor = '#D2691E', wallColor = '#ADD8E6', petName = 'My Pet', price = 99.99) {
/**
* Dynamically loads a font from Google Fonts and waits for it to be ready for canvas use.
* @param {string} fontFamily The name of the font family to load.
*/
const loadGoogleFont = async (fontFamily) => {
const fontId = `google-font-${fontFamily.replace(/ /g, '-')}`;
// Check if the font's <link> tag is already added
if (document.getElementById(fontId)) {
try {
// If so, just ensure it's loaded for the canvas context
await document.fonts.load(`12px "${fontFamily}"`);
} catch (e) {
console.warn(`Font ${fontFamily} might not be available for canvas.`);
}
return;
}
const link = document.createElement('link');
link.id = fontId;
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}&display=swap`;
const fontPromise = new Promise((resolve) => {
link.onload = resolve;
link.onerror = () => {
console.error(`Failed to load Google Font: ${fontFamily}`);
resolve(); // Resolve anyway to not block the process
};
});
document.head.appendChild(link);
await fontPromise;
// After the stylesheet is loaded, wait for the font to be ready for drawing
try {
await document.fonts.load(`12px "${fontFamily}"`);
} catch (e) {
console.warn(`Font ${fontFamily} could not be loaded for canvas rendering.`);
}
};
/**
* Adjusts a hex color by a given amount.
* @param {string} color - The hex color string (e.g., '#RRGGBB').
* @param {number} amount - The amount to adjust each channel by (-255 to 255).
* @returns {string} The adjusted hex color string.
*/
const adjustColor = (color, amount) => {
return '#' + color.replace(/^#/, '').replace(/../g, c => ('0' + Math.min(255, Math.max(0, parseInt(c, 16) + amount)).toString(16)).substr(-2));
};
// Load fonts needed for the scene
await Promise.all([
loadGoogleFont('Caveat'),
loadGoogleFont('Patrick Hand')
]);
// Canvas Setup
const canvas = document.createElement('canvas');
const canvasWidth = 800;
const canvasHeight = 600;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
// --- Draw Scene ---
// 1. Background (Wall and Floor)
const floorY = 450;
ctx.fillStyle = wallColor;
ctx.fillRect(0, 0, canvasWidth, floorY);
ctx.fillStyle = '#C2B280'; // Floor color
ctx.fillRect(0, floorY, canvasWidth, canvasHeight - floorY);
ctx.fillStyle = '#FFFFFF'; // Baseboard
ctx.fillRect(0, floorY - 10, canvasWidth, 10);
ctx.fillStyle = '#E0E0E0';
ctx.fillRect(0, floorY - 3, canvasWidth, 3);
// 2. Pet Shop Shelves and Products
const shelfColor = '#8B4513';
ctx.fillStyle = shelfColor;
ctx.fillRect(50, 150, 700, 15); // Top shelf
ctx.fillStyle = adjustColor(shelfColor, -20);
ctx.fillRect(50, 165, 700, 5);
ctx.fillStyle = shelfColor;
ctx.fillRect(50, 300, 700, 15); // Bottom shelf
ctx.fillStyle = adjustColor(shelfColor, -20);
ctx.fillRect(50, 315, 700, 5);
// Products on shelves
ctx.fillStyle = '#DC143C'; // Red ball
ctx.beginPath();
ctx.arc(100, 140, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.beginPath();
ctx.arc(95, 135, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = '#4682B4'; // Toy box
ctx.fillRect(500, 100, 60, 50);
ctx.fillStyle = '#5F9EA0';
ctx.font = '14px Arial';
ctx.fillText('TOYS', 530, 125);
ctx.fillStyle = '#FFD700'; // Food bag
ctx.fillRect(150, 250, 50, 50);
ctx.fillStyle = '#000000';
ctx.font = '12px "Patrick Hand", sans-serif';
ctx.fillText('FOOD', 175, 275);
// 3. The Couch
const couch_x = 150;
const couch_y = 380;
const couch_w = 500;
const highlightColor = adjustColor(couchColor, 20);
const shadowColor = adjustColor(couchColor, -30);
// Couch Base shadow
ctx.fillStyle = 'rgba(0,0,0,0.1)';
ctx.fillRect(couch_x - 40, couch_y + 150, couch_w + 80, 20);
// Structure
ctx.fillStyle = couchColor;
ctx.fillRect(couch_x, couch_y, couch_w, 80); // Backrest
ctx.fillRect(couch_x - 40, couch_y + 20, 40, 60); // Left arm
ctx.fillRect(couch_x + couch_w, couch_y + 20, 40, 60); // Right arm
// Highlights
ctx.fillStyle = highlightColor;
ctx.fillRect(couch_x, couch_y, couch_w, 10); // Backrest top
ctx.fillRect(couch_x - 40, couch_y + 20, 40, 10); // Left arm top
ctx.fillRect(couch_x + couch_w, couch_y + 20, 40, 10); // Right arm top
ctx.fillRect(couch_x, couch_y + 60, couch_w, 60); // Seat
// Base & Legs
ctx.fillStyle = shadowColor;
ctx.fillRect(couch_x - 40, couch_y + 120, couch_w + 80, 30);
ctx.fillStyle = adjustColor(shelfColor, -40);
ctx.fillRect(couch_x - 20, couch_y + 150, 20, 20); // Left leg
ctx.fillRect(couch_x + couch_w, couch_y + 150, 20, 20); // Right leg
// 4. The Pet (originalImg)
const maxWidth = couch_w - 40;
const maxHeight = 280;
const ratio = Math.min(maxWidth / originalImg.width, maxHeight / originalImg.height);
const petWidth = originalImg.width * ratio;
const petHeight = originalImg.height * ratio;
const petX = couch_x + (couch_w - petWidth) / 2;
const petBottom = couch_y + 115;
const petY = petBottom - petHeight;
// Pet shadow
ctx.save();
ctx.filter = 'blur(8px)';
ctx.fillStyle = 'rgba(0, 0, 0, 0.35)';
ctx.beginPath();
ctx.ellipse(petX + petWidth / 2, petBottom + 5, petWidth / 2 * 0.9, 10, 0, 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
// Draw the image
ctx.drawImage(originalImg, petX, petY, petWidth, petHeight);
// 5. Price Tag
const tagX = 670, tagY = 410, tagW = 100, tagH = 50;
ctx.strokeStyle = '#000';
ctx.lineWidth = 1.5;
ctx.fillStyle = '#F5DEB3';
ctx.beginPath();
ctx.moveTo(tagX, tagY);
ctx.lineTo(tagX + tagW - 15, tagY);
ctx.lineTo(tagX + tagW, tagY + tagH / 2);
ctx.lineTo(tagX + tagW - 15, tagY + tagH);
ctx.lineTo(tagX, tagY + tagH);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath(); // Tag hole
ctx.arc(tagX + 10, tagY + 15, 4, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath(); // String
ctx.moveTo(tagX + 10, tagY + 15);
ctx.quadraticCurveTo(tagX + 30, tagY - 20, 680, 420);
ctx.stroke();
ctx.fillStyle = '#8B0000';
ctx.font = `28px "Caveat", cursive`;
ctx.fillText(`$${price.toFixed(2)}`, tagX + tagW / 2 + 5, tagY + tagH / 2 + 3);
// 6. Pet Name Tag
if (petName.trim() !== '') {
const heartSize = Math.max(15, Math.min(25, petWidth / 10));
const hx = petX + petWidth / 2;
const hy = petY + Math.min(petHeight * 0.3, heartSize * 2.5);
ctx.lineWidth = 2;
ctx.strokeStyle = "gold";
ctx.fillStyle = '#E32636';
ctx.beginPath();
const topCurveHeight = heartSize * 0.3;
const bottomCurveHeight = heartSize*1.4;
ctx.moveTo(hx, hy - topCurveHeight);
ctx.bezierCurveTo(hx, hy - topCurveHeight, hx - heartSize/2, hy - topCurveHeight, hx - heartSize/2, hy);
ctx.bezierCurveTo(hx - heartSize, hy, hx- heartSize, hy + bottomCurveHeight/2, hx, hy + bottomCurveHeight);
ctx.bezierCurveTo(hx, hy + bottomCurveHeight, hx + heartSize, hy + bottomCurveHeight/2, hx+ heartSize, hy);
ctx.bezierCurveTo(hx + heartSize, hy - topCurveHeight, hx, hy - topCurveHeight, hx, hy- topCurveHeight);
ctx.closePath();
ctx.fill();
ctx.stroke();
const fontSize = Math.floor(heartSize * 0.8);
ctx.font = `${fontSize}px "Patrick Hand", sans-serif`;
ctx.fillStyle = '#FFFFFF';
ctx.fillText(petName, hx, hy + heartSize * 0.4, heartSize * 1.8);
}
return canvas;
}
Apply Changes