You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, headlineText = "AMAZING PRODUCT!", taglineText = "Get Yours Today!", fontFamily = "Oswald", primaryColor = "#3A2F2F", secondaryColor = "#EAE0D1", vintageStrength = 0.7) {
// 0. Sanitize vintageStrength
vintageStrength = Math.max(0, Math.min(1, parseFloat(vintageStrength) || 0));
// 1. Font handling
let activeFontFamily = fontFamily;
let mainFallbackFont = "sans-serif";
if (typeof fontFamily === 'string') {
const lowerFontFamily = fontFamily.toLowerCase();
if (lowerFontFamily === "oswald") {
activeFontFamily = "Oswald"; // Standardize name
mainFallbackFont = "sans-serif";
if (!document.fonts.check(`12px ${activeFontFamily}`)) {
const fontOswald = new FontFace(activeFontFamily, `url(https://fonts.gstatic.com/s/oswald/v49/TK3_WkUHHAIjg75cFRf3bXL8LICs1_FvsUtiZTaR.woff2)`);
try {
await fontOswald.load();
document.fonts.add(fontOswald);
console.log(`Font '${activeFontFamily}' loaded successfully.`);
} catch (e) {
console.error(`Font '${activeFontFamily}' loading failed:`, e);
activeFontFamily = mainFallbackFont; // Use fallback if loading fails
}
}
} else if (lowerFontFamily === "bangers") {
activeFontFamily = "Bangers"; // Standardize name
mainFallbackFont = "cursive";
if (!document.fonts.check(`12px ${activeFontFamily}`)) {
const fontBangers = new FontFace(activeFontFamily, `url(https://fonts.gstatic.com/s/bangers/v20/FeVQS0BTqb0h60ACL5k.woff2)`);
try {
await fontBangers.load();
document.fonts.add(fontBangers);
console.log(`Font '${activeFontFamily}' loaded successfully.`);
} catch (e) {
console.error(`Font '${activeFontFamily}' loading failed:`, e);
activeFontFamily = mainFallbackFont; // Use fallback if loading fails
}
}
} else {
// For any other font family name, we assume it's a system font or loaded by the user elsewhere.
// activeFontFamily is already set to fontFamily. We'll use a generic fallback.
console.log(`Using system font or externally loaded font: '${fontFamily}'`);
}
} else {
// Fallback if fontFamily parameter is not a string for some reason
activeFontFamily = "sans-serif";
}
// 2. Canvas Setup
const padding = Math.max(20, originalImg.width * 0.05); // Dynamic padding, at least 20px
const textBlockHeightRatio = 0.25; // Proportion of image height for text block, or fixed
let textBlockHeight = Math.max(100, originalImg.height * textBlockHeightRatio);
if (originalImg.height < 300) textBlockHeight = 120; // Ensure minimum space for small images
const borderWidth = Math.max(2, Math.min(10, padding * 0.1)); // Dynamic border width
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width + 2 * padding;
canvas.height = originalImg.height + 2 * padding + textBlockHeight;
// 3. Background
ctx.fillStyle = secondaryColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 4. Draw and Process Image
const imgX = padding;
const imgY = padding;
ctx.drawImage(originalImg, imgX, imgY, originalImg.width, originalImg.height);
if (vintageStrength > 0 && originalImg.width > 0 && originalImg.height > 0) {
try {
const imageData = ctx.getImageData(imgX, imgY, originalImg.width, originalImg.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Sepia calculation
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
// Apply sepia blended with original based on vintageStrength
data[i] = r * (1 - vintageStrength) + Math.min(255, tr) * vintageStrength;
data[i + 1] = g * (1 - vintageStrength) + Math.min(255, tg) * vintageStrength;
data[i + 2] = b * (1 - vintageStrength) + Math.min(255, tb) * vintageStrength;
}
ctx.putImageData(imageData, imgX, imgY);
} catch (e) {
console.error("Error processing image pixels for vintage effect:", e);
// If getImageData fails (e.g. tainted canvas from cross-origin image without CORS),
// we can't apply pixel effects. The original image will remain.
// To mitigate, draw original image again if it was cleared or partially processed.
ctx.drawImage(originalImg, imgX, imgY, originalImg.width, originalImg.height);
}
}
// 5. Add Text
ctx.fillStyle = primaryColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Headline
// Adjust font size based on available width and textBlockHeight
let headlineFontSize = Math.max(18, Math.min(originalImg.width / 8, textBlockHeight * 0.4));
ctx.font = `bold ${headlineFontSize}px "${activeFontFamily}", ${mainFallbackFont}`;
const headlineY = imgY + originalImg.height + padding * 0.5 + headlineFontSize / 2;
ctx.fillText(headlineText.toUpperCase(), canvas.width / 2, headlineY);
// Tagline
let taglineFontSize = Math.max(12, Math.min(originalImg.width / 15, textBlockHeight * 0.25));
ctx.font = `${taglineFontSize}px "${activeFontFamily}", ${mainFallbackFont}`;
const taglineY = headlineY + headlineFontSize / 2 + padding * 0.25 + taglineFontSize / 2;
ctx.fillText(taglineText, canvas.width / 2, taglineY);
// 6. Add Border
ctx.strokeStyle = primaryColor;
ctx.lineWidth = borderWidth;
ctx.strokeRect(borderWidth / 2, borderWidth / 2, canvas.width - borderWidth, canvas.height - borderWidth);
// Optional: Inner border for image aesthetic
const innerBorderWidth = Math.max(1, borderWidth / 2);
ctx.lineWidth = innerBorderWidth;
ctx.strokeStyle = primaryColorAlpha(primaryColor, 0.5); // Slightly transparent border
ctx.strokeRect(imgX - innerBorderWidth, imgY - innerBorderWidth, originalImg.width + 2 * innerBorderWidth, originalImg.height + 2 * innerBorderWidth);
// 7. Global Noise Overlay (subtle)
if (vintageStrength > 0.1 && canvas.width > 0 && canvas.height > 0) { // Only add noise if vintage effect is somewhat strong
try {
const noiseAmount = 25 * vintageStrength; // Max noise pixel intensity
const noiseImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const noiseData = noiseImageData.data;
for (let i = 0; i < noiseData.length; i += 4) {
// Add monochrome noise
const noise = (Math.random() - 0.5) * noiseAmount;
noiseData[i] = Math.min(255, Math.max(0, noiseData[i] + noise));
noiseData[i+1] = Math.min(255, Math.max(0, noiseData[i+1] + noise));
noiseData[i+2] = Math.min(255, Math.max(0, noiseData[i+2] + noise));
}
ctx.putImageData(noiseImageData, 0, 0);
} catch (e) {
console.error("Error applying noise effect:", e);
// Likely due to tainted canvas.
}
}
return canvas;
}
// Helper function to add alpha to a hex color for the inner border
function primaryColorAlpha(hexColor, alpha) {
let r = 0, g = 0, b = 0;
if (hexColor.length == 4) { // #RGB format
r = parseInt(hexColor[1] + hexColor[1], 16);
g = parseInt(hexColor[2] + hexColor[2], 16);
b = parseInt(hexColor[3] + hexColor[3], 16);
} else if (hexColor.length == 7) { // #RRGGBB format
r = parseInt(hexColor[1] + hexColor[2], 16);
g = parseInt(hexColor[3] + hexColor[4], 16);
b = parseInt(hexColor[5] + hexColor[6], 16);
}
return `rgba(${r},${g},${b},${alpha})`;
}
Apply Changes