import { Rect } from '../types'; export const fileToDataUrl = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target?.result as string); reader.onerror = reject; reader.readAsDataURL(file); }); }; export const loadImage = (src: string): Promise => { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = "anonymous"; img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); }; export const stitchAssets = async ( baseSrc: string, blinkSrc?: string, talkSrc?: string ): Promise<{ imageUrl: string; mainBody: Rect; textureClosedEye?: Rect; textureOpenMouth?: Rect }> => { // Load images const baseImg = await loadImage(baseSrc); const blinkImg = blinkSrc ? await loadImage(blinkSrc) : null; const talkImg = talkSrc ? await loadImage(talkSrc) : null; // Layout: Base on Left. Sidebar on Right containing Blink (top) and Talk (bottom). // Sidebar width = max(blink.width, talk.width) const sidebarWidth = Math.max(blinkImg?.width || 0, talkImg?.width || 0); // If there are no variants, just return the base image as is if (sidebarWidth === 0) { return { imageUrl: baseSrc, mainBody: { x: 0, y: 0, w: 1, h: 1 } }; } const totalWidth = baseImg.width + sidebarWidth; const totalHeight = Math.max(baseImg.height, (blinkImg?.height || 0) + (talkImg?.height || 0)); const canvas = document.createElement('canvas'); canvas.width = totalWidth; canvas.height = totalHeight; const ctx = canvas.getContext('2d'); if (!ctx) throw new Error("Could not get canvas context"); // Draw Base ctx.drawImage(baseImg, 0, 0); // Calculate normalized rects const mainBody: Rect = { x: 0, y: 0, w: baseImg.width / totalWidth, h: baseImg.height / totalHeight }; let textureClosedEye: Rect | undefined; if (blinkImg) { ctx.drawImage(blinkImg, baseImg.width, 0); textureClosedEye = { x: baseImg.width / totalWidth, y: 0, w: blinkImg.width / totalWidth, h: blinkImg.height / totalHeight }; } let textureOpenMouth: Rect | undefined; if (talkImg) { const yPos = blinkImg ? blinkImg.height : 0; ctx.drawImage(talkImg, baseImg.width, yPos); textureOpenMouth = { x: baseImg.width / totalWidth, y: yPos / totalHeight, w: talkImg.width / totalWidth, h: talkImg.height / totalHeight }; } return { imageUrl: canvas.toDataURL('image/png'), mainBody, textureClosedEye, textureOpenMouth }; };