import { commandIds } from "@commands/ids"; import type { ImageDocument } from "@core/document"; import type { Layer } from "@core/layer"; import { getLayerMask } from "@core/layer-mask-utils"; import { resolveTransformTargetBounds } from "@editor/transform-targets"; import type { SelectionState } from "@editor/state"; import type { AppStore } from "@editor/store"; import type { ChromaKeySettings } from "@editor/tools"; import { createChromaKeyMask, createChromaKeyPreview } from "@platform/browser/chromaKey"; export function previewChromaKey(source: string, width: number, height: number, settings: ChromaKeySettings) { return createChromaKeyPreview(source, width, height, settings); } export function resolveChromaKeyTarget(document: ImageDocument, selection: SelectionState) { const layerId = selection.layerIds[0]; if (selection.layerIds.length !== 1 || !layerId) return undefined; const layer = findLayer(document.artboards.find((artboard) => artboard.id === selection.artboardId)?.layers ?? [], layerId); if (!layer || layer.type === "group" || layer.type === "adjustment") return undefined; const asset = document.assets.find((candidate) => candidate.id === layer.assetId); const bounds = resolveTransformTargetBounds(document, { type: "layer", id: layer.id }); const layerMask = getLayerMask(layer); const maskLayer = layerMask?.enabled ? findLayer(document.artboards.flatMap((artboard) => artboard.layers), layerMask.maskLayerId) : undefined; const maskAsset = maskLayer && maskLayer.type !== "group" && maskLayer.type !== "adjustment" ? document.assets.find((candidate) => candidate.id === maskLayer.assetId) : undefined; return asset && bounds ? { layer, asset, bounds, maskLayer, maskAsset } : undefined; } export async function applyChromaKeyMask(target: NonNullable>, settings: ChromaKeySettings, dispatch: AppStore["dispatch"]) { const source = await createChromaKeyMask(target.asset.source, target.asset.intrinsicSize.w, target.asset.intrinsicSize.h, settings); dispatch(commandIds.toolSetBrushStrokePreview, undefined); if (target.maskAsset && target.maskLayer && target.maskLayer.type !== "group" && target.maskLayer.type !== "adjustment") { dispatch(commandIds.documentApplyLayerMaskOperation, { maskLayerId: target.maskLayer.id, source, mimeType: "image/png", operation: { type: "chromaKey" } }); dispatch(commandIds.workspaceSetPanel, { panel: "none" }); return; } const assetId = crypto.randomUUID(); const maskLayerId = crypto.randomUUID(); const width = Math.max(1, Math.round(target.asset.intrinsicSize.w)); const height = Math.max(1, Math.round(target.asset.intrinsicSize.h)); dispatch(commandIds.documentAddLayerMask, { layerId: target.layer.id, asset: { id: assetId, name: `${target.layer.name} Chroma Mask`, mimeType: "image/png", source, intrinsicSize: { w: width, h: height } }, maskLayer: { id: maskLayerId, type: "raster", name: `${target.layer.name} Chroma Mask`, visible: true, locked: false, opacity: 1, assetId, transform: { position: { x: target.bounds.x, y: target.bounds.y }, scale: { x: target.bounds.w / width, y: target.bounds.h / height }, rotation: target.layer.transform.rotation } }, }); dispatch(commandIds.toolExitMaskEdit, undefined); dispatch(commandIds.workspaceSetPanel, { panel: "none" }); } function findLayer(layers: readonly Layer[], layerId: string): Layer | undefined { for (const layer of layers) { if (layer.id === layerId) return layer; if (layer.type === "group") { const found = findLayer(layer.children, layerId); if (found) return found; } } }