import "server-only"; import { fetchImageBufferFromUrl } from "@/lib/ai/rmbg/service"; export type CanvasLayoutReplaceSourceProfile = { boundingHeight: number; boundingWidth: number; silhouetteAspectRatio: number; sourceHeight: number; sourceWidth: number; transparentPixelRatio: number; }; export async function resolveCanvasLayoutReplaceSourceProfile( sourceImageUrl: string, ): Promise { try { const sharp = (await import("sharp")).default; const sourceBuffer = await fetchImageBufferFromUrl(sourceImageUrl); const { data, info } = await sharp(sourceBuffer, { failOn: "none" }) .rotate() .ensureAlpha() .raw() .toBuffer({ resolveWithObject: true }); const pixelCount = info.width * info.height; if (pixelCount <= 0 || info.channels < 4) return null; let transparentPixelCount = 0; let minX = info.width; let minY = info.height; let maxX = -1; let maxY = -1; for (let pixelIndex = 0; pixelIndex < pixelCount; pixelIndex += 1) { const alpha = data[pixelIndex * info.channels + 3] || 0; if (alpha < 250) transparentPixelCount += 1; if (alpha < 24) continue; const x = pixelIndex % info.width; const y = Math.floor(pixelIndex / info.width); if (x < minX) minX = x; if (x > maxX) maxX = x; if (y < minY) minY = y; if (y > maxY) maxY = y; } const transparentPixelRatio = transparentPixelCount / pixelCount; if (transparentPixelRatio < 0.01 || maxX < minX || maxY < minY) { return null; } const boundingWidth = maxX - minX + 1; const boundingHeight = maxY - minY + 1; if (boundingWidth < 2 || boundingHeight < 2) return null; return { boundingHeight, boundingWidth, silhouetteAspectRatio: boundingWidth / boundingHeight, sourceHeight: info.height, sourceWidth: info.width, transparentPixelRatio, }; } catch (error) { console.warn("[Canvas Layout Replace] Failed to resolve source profile:", { error: error instanceof Error ? error.message : String(error), }); return null; } }