You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
titleText = "SUPERHERO RISING",
taglineText = "THEIR TIME IS NOW",
bottomText = "EXPERIENCE IT IN CINEMAS",
titleFont = "Anton",
bodyFont = "Arial",
titleColor = "#FFD700", // Gold
titleOutlineColor = "#000000",
taglineColor = "#FFFFFF",
bottomTextColor = "#E0E0E0", // Light gray
bgColorTop = "#0A102A", // Dark Navy Blue-ish Blackstone
bgColorBottom = "#000000", // Black
imageGlowAmount = 15,
imageGlowColor = "#FFFFFF4D" // Semi-transparent white (30% opacity, e.g. #RRGGBBAA)
) {
const FONT_URLS = {
"Anton": "https://fonts.gstatic.com/s/anton/v25/1Ptgg87LROyAm0K08iAcA.woff2",
"Bebas Neue": "https://fonts.gstatic.com/s/bebasneue/v9/JTUSjIg69CK48gW7PXoo9Wlhyw.woff2",
};
async function _loadFont(fontFamily, fontUrl) {
if (!fontFamily || !fontUrl) return;
if (document.fonts && typeof document.fonts.check === 'function' && document.fonts.check(`12px "${fontFamily}"`)) {
// console.log(`Font "${fontFamily}" already available.`);
return;
}
if (typeof FontFace === 'function' && document.fonts && typeof document.fonts.add === 'function') {
try {
const font = new FontFace(fontFamily, `url(${fontUrl}) format('woff2')`, { display: 'swap' });
await font.load();
document.fonts.add(font);
// console.log(`FontFace: Font "${fontFamily}" loaded from ${fontUrl}`);
} catch (e) {
console.error(`FontFace: Failed to load font "${fontFamily}" from ${fontUrl}:`, e);
// Font will fallback to system default in canvas drawing if this fails
}
} else {
// Fallback for very old browsers or environments where FontFace API might be disabled/problematic.
// console.warn(`FontFace API or document.fonts.add not fully supported. Attempting CSS @font-face for ${fontFamily}.`);
const styleId = `font-loader-style-${fontFamily.replace(/\s+/g, '-')}`;
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
@font-face {
font-family: '${fontFamily}';
src: url('${fontUrl}') format('woff2');
font-display: swap; /* Crucial for behavior */
}
`;
document.head.appendChild(style);
// For this fallback, we can't reliably wait, so we proceed.
// font-display: swap will help browser to render with fallback then switch.
}
}
}
// --- Canvas Setup ---
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 900;
const canvas = document.createElement('canvas');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
const ctx = canvas.getContext('2d');
// --- Font Loading ---
if (titleFont && FONT_URLS[titleFont]) {
await _loadFont(titleFont, FONT_URLS[titleFont]);
}
// Assuming bodyFont (e.g., Arial) is a system font and doesn't need explicit loading.
// If bodyFont could also be a web font from CDN, similar loading logic would be needed here too.
// e.g. if (bodyFont && FONT_URLS[bodyFont]) { await _loadFont(bodyFont, FONT_URLS[bodyFont]); }
// --- Draw Background ---
const bgGradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
bgGradient.addColorStop(0, bgColorTop);
bgGradient.addColorStop(1, bgColorBottom);
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// --- Layout Constants & Calculations ---
const titleBaseY = canvas.height * 0.20; // Y position for the middle of the title
const titleFontSize = Math.floor(canvas.height * 0.10);
const bottomTextBaseY = canvas.height * 0.95; // Y position for the bottom of the bottom text
// Approximate image vertical positioning based on title and bottom text
// Make space for title: title's top would be roughly titleBaseY - titleFontSize / 2. Bottom: titleBaseY + titleFontSize / 2.
const imgMarginFromTitle = canvas.height * 0.03;
const imgAreaTop = titleBaseY + (titleFontSize / 2) + imgMarginFromTitle;
// Make space for tagline and bottom text
// Tagline rough height + margin, bottom text rough height + margin
const estimatedSpaceForLowerTexts = canvas.height * 0.18;
const imgAreaBottom = bottomTextBaseY - estimatedSpaceForLowerTexts;
const imgPaddingHorizontal = canvas.width * 0.08;
const imgAvailableWidth = canvas.width - 2 * imgPaddingHorizontal;
const imgAvailableHeight = imgAreaBottom - imgAreaTop;
let imgDisplayWidth = 0;
let imgDisplayHeight = 0;
let imgDrawX = 0;
let imgDrawY = imgAreaTop; // Default starting Y for image
if (originalImg && originalImg.width > 0 && originalImg.height > 0 && imgAvailableWidth > 0 && imgAvailableHeight > 0) {
const aspect = originalImg.width / originalImg.height;
if ((imgAvailableWidth / aspect) <= imgAvailableHeight) {
// Width constrained: fit to available width
imgDisplayWidth = imgAvailableWidth;
imgDisplayHeight = imgDisplayWidth / aspect;
} else {
// Height constrained: fit to available height
imgDisplayHeight = imgAvailableHeight;
imgDisplayWidth = imgDisplayHeight * aspect;
}
imgDrawX = (canvas.width - imgDisplayWidth) / 2;
// Center the image vertically in its allocated space
imgDrawY = imgAreaTop + (imgAvailableHeight - imgDisplayHeight) / 2;
ctx.save();
if (imageGlowAmount > 0 && imageGlowColor && imageGlowColor !== 'none') {
try { // Validate color to prevent errors if malformed
ctx.shadowColor = imageGlowColor;
ctx.shadowBlur = imageGlowAmount;
} catch (e) {
console.warn("Invalid imageGlowColor:", imageGlowColor);
// Proceed without shadow if color is invalid
}
}
ctx.drawImage(originalImg, imgDrawX, imgDrawY, imgDisplayWidth, imgDisplayHeight);
ctx.restore(); // Clear shadow effects
} else {
// If no image or invalid image, use a placeholder Y for tagline calculation later
imgDrawY = canvas.height * 0.4; // Mid-point fallback if image isn't drawn
imgDisplayHeight = canvas.height * 0.3; // Placeholder height
}
// --- Draw Title ---
ctx.font = `${titleFontSize}px "${titleFont}", Impact, "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const maxTitleWidth = canvas.width * 0.90; // Max width before text scaling occurs
if (titleOutlineColor && titleOutlineColor !== 'none' && titleOutlineColor.trim() !== "") {
ctx.strokeStyle = titleOutlineColor;
ctx.lineWidth = Math.max(1, titleFontSize / 18);
ctx.strokeText(titleText.toUpperCase(), canvas.width / 2, titleBaseY, maxTitleWidth);
}
ctx.fillStyle = titleColor;
ctx.fillText(titleText.toUpperCase(), canvas.width / 2, titleBaseY, maxTitleWidth);
// --- Draw Tagline ---
const taglineFontSize = Math.floor(canvas.height * 0.032);
ctx.font = `bold ${taglineFontSize}px "${bodyFont}", "Helvetica Neue", Helvetica, Arial, sans-serif`;
ctx.fillStyle = taglineColor;
ctx.textBaseline = 'top';
// Position tagline below the image (or its placeholder area if no image)
const taglineY = (imgDrawY + imgDisplayHeight) + canvas.height * 0.04;
const maxTaglineWidth = canvas.width * 0.80;
ctx.fillText(taglineText.toUpperCase(), canvas.width / 2, taglineY, maxTaglineWidth);
// --- Draw Bottom Text (e.g., Release Info) ---
const bottomTextFontSize = Math.floor(canvas.height * 0.025);
ctx.font = `${bottomTextFontSize}px "${bodyFont}", "Helvetica Neue", Helvetica, Arial, sans-serif`;
ctx.fillStyle = bottomTextColor;
ctx.textBaseline = 'bottom';
const maxBottomTextWidth = canvas.width * 0.9;
ctx.fillText(bottomText.toUpperCase(), canvas.width / 2, bottomTextBaseY, maxBottomTextWidth);
return canvas;
}
Apply Changes