import type { AgentPsdTemplateImageContentBounds } from "@/lib/agents/psd-template-config"; type PsdImageLayerCandidate = { id: string; layerName: string; layerPath: string; x: number; y: number; width: number; height: number; contentBounds?: AgentPsdTemplateImageContentBounds; }; const BACKGROUND_LAYER_PATTERN = /(背景|底图|bg|background)/i; const ADJUSTMENT_LAYER_PATTERN = /(色相\s*\/?\s*饱和度|曲线|色阶|亮度\s*\/?\s*对比度|可选颜色|色彩平衡|自然饱和度|曝光|黑白|阈值|反相|海报化|通道混合器|渐变映射|纯色|渐变填充|图案填充|hue\s*\/?\s*saturation|curves?|levels?|brightness\s*\/?\s*contrast|selective color|color balance|vibrance|exposure|black\s*&?\s*white|threshold|invert|posterize|channel mixer|gradient map|solid color|gradient fill|pattern fill)/i; const PRIMARY_IMAGE_HINT_PATTERN = /(主图|产品|商品|主体|sku|result|photo|image|product|subject|scene|素材)/i; function getCanvasOverlapArea(params: { x: number; y: number; width: number; height: number; canvasWidth: number; canvasHeight: number; }) { const left = Math.max(0, params.x); const top = Math.max(0, params.y); const right = Math.min(params.canvasWidth, params.x + params.width); const bottom = Math.min(params.canvasHeight, params.y + params.height); return Math.max(0, right - left) * Math.max(0, bottom - top); } export function isPsdAdjustmentLikeImageLayer(layer: { layerName: string; layerPath: string; }) { return ( ADJUSTMENT_LAYER_PATTERN.test(layer.layerName) || ADJUSTMENT_LAYER_PATTERN.test(layer.layerPath) ); } export function pickPrimaryPsdImageLayerId(params: { imageLayers: PsdImageLayerCandidate[]; canvasWidth: number; canvasHeight: number; }) { const { imageLayers, canvasWidth, canvasHeight } = params; if (imageLayers.length === 0) { return null; } const scored = imageLayers.map((layer) => { const overlapArea = getCanvasOverlapArea({ x: layer.x, y: layer.y, width: layer.width, height: layer.height, canvasWidth, canvasHeight, }); const layerArea = Math.max(1, layer.width * layer.height); const visibleArea = layer.contentBounds && layer.contentBounds.width > 0 && layer.contentBounds.height > 0 ? layer.contentBounds.width * layer.contentBounds.height : overlapArea; const coversWholeCanvas = overlapArea >= canvasWidth * canvasHeight * 0.96 && layer.x <= 0 && layer.y <= 0 && layer.width >= canvasWidth * 0.96 && layer.height >= canvasHeight * 0.96; const isAdjustmentLike = isPsdAdjustmentLikeImageLayer(layer); const hasPrimaryImageHint = PRIMARY_IMAGE_HINT_PATTERN.test(layer.layerName) || PRIMARY_IMAGE_HINT_PATTERN.test(layer.layerPath); const backgroundPenalty = BACKGROUND_LAYER_PATTERN.test(layer.layerName) || BACKGROUND_LAYER_PATTERN.test(layer.layerPath) || coversWholeCanvas ? 0.45 : 1; const adjustmentPenalty = isAdjustmentLike ? 0.05 : 1; const oversizePenalty = layer.width > canvasWidth * 1.2 || layer.height > canvasHeight * 1.2 ? 0.9 : 1; const primaryHintBonus = hasPrimaryImageHint ? 1.2 : 1; return { layer, score: (visibleArea * 1.15 + overlapArea + layerArea * 0.2) * backgroundPenalty * adjustmentPenalty * oversizePenalty * primaryHintBonus, isAdjustmentLike, }; }); const preferredCandidates = scored.filter((entry) => !entry.isAdjustmentLike); const candidates = preferredCandidates.length > 0 ? preferredCandidates : scored; candidates.sort((left, right) => right.score - left.score); return candidates[0]?.layer.id || null; }