From 14e75197cfd91dc3bb55c931cfe8766f26850296 Mon Sep 17 00:00:00 2001 From: 134 Date: Sun, 5 Jul 2026 21:52:48 +0800 Subject: [PATCH] fix: composite full-frame scale edits --- app/api/canvas/local-edit/route.ts | 26 ++++++++++++++++++-------- lib/canvas/generate-image.ts | 30 ++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index e6e3488e0..8137722e3 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -3357,13 +3357,21 @@ async function createScaleAdjustPatchReference(params: { const targetDraftBuffer = targetDraftReference.buffer; const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; if (params.fullFrameReferenceOnly && structureLockAppliedRef.value) { - const targetDraftFullUpload = await uploadFile( - `canvas-scale-adjust-full-target-draft-${suffix}.png`, - toArrayBuffer(targetDraftBuffer) - ); + const [targetDraftFullUpload, compositeClipMaskUpload] = await Promise.all([ + uploadFile( + `canvas-scale-adjust-full-target-draft-${suffix}.png`, + toArrayBuffer(targetDraftBuffer) + ), + targetDraftReference.compositeClipMaskBuffer + ? uploadFile( + `canvas-scale-adjust-composite-clip-mask-${suffix}.png`, + toArrayBuffer(targetDraftReference.compositeClipMaskBuffer) + ) + : Promise.resolve(null), + ]); return { - compositeClipMaskUrl: null, + compositeClipMaskUrl: compositeClipMaskUpload?.url || null, sourceCropUrl: sourceImageUrl || targetDraftFullUpload.url, structureLockedDraft: structureLockAppliedRef.value, targetDraftFullUrl: targetDraftFullUpload.url, @@ -5048,8 +5056,7 @@ export async function POST(request: NextRequest) { : "locked-plate-semantic-target-map-no-old-crop-v15" : undefined, scaleAdjustPostCompositeApplied: - Boolean(scaleAdjustPatchReference) && - !useScaleAdjustFullFrameStructurePatch, + Boolean(scaleAdjustPatchReference), scaleAdjustUsesFullFrameOutput: isScaleAdjustOperation && (!scaleAdjustPatchReference || useScaleAdjustFullFrameStructurePatch), @@ -5087,7 +5094,7 @@ export async function POST(request: NextRequest) { ? { maskImageUrl: scaleAdjustEditMaskUpload.url } : undefined, referenceImages, - scaleAdjustPatchComposite: scaleAdjustPatchReference && !useScaleAdjustFullFrameStructurePatch + scaleAdjustPatchComposite: scaleAdjustPatchReference ? { diffAlphaCutoff: scaleAdjustPatchReference.structureLockedDraft ? 24 : 8, @@ -5103,6 +5110,9 @@ export async function POST(request: NextRequest) { scaleAdjustPatchReference.structureLockedDraft ? 36 : 42, featherMinPx: 10, featherRatio: 0.018, + generatedFrame: useScaleAdjustFullFrameStructurePatch + ? "full" + : "workspace", maskMode: "diff", regions: scaleAdjustPatchCompositeRegions, sourceImageUrl, diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts index 77cf52646..acd2a2a09 100644 --- a/lib/canvas/generate-image.ts +++ b/lib/canvas/generate-image.ts @@ -56,6 +56,7 @@ export interface CanvasLocalEditCompositeConfig { export interface CanvasScaleAdjustPatchCompositeConfig { clipMaskImageUrl?: string; + generatedFrame?: "workspace" | "full"; sourceImageUrl: string; workspaceRegion: CanvasLocalEditCompositeRegion; regions?: CanvasLocalEditCompositeRegion[]; @@ -536,6 +537,7 @@ function normalizeScaleAdjustPatchCompositeConfig( max: 0.12, min: 0, }), + generatedFrame: value.generatedFrame === "full" ? "full" : "workspace", }; } @@ -3039,14 +3041,25 @@ async function applyScaleAdjustPatchCompositeToImages(params: { for (const [index, image] of images.entries()) { const generatedUrl = image.originalUrl || image.thumbnailUrl; const generatedBuffer = await fetchImageBufferFromUrl(generatedUrl); - const resizedGeneratedBuffer = await sharp(generatedBuffer, { failOn: "none" }) - .rotate() - .resize(workspaceBox.width, workspaceBox.height, { - fit: "cover", - position: "center", - }) - .png({ compressionLevel: 6 }) - .toBuffer(); + const resizedGeneratedBuffer = + composite.generatedFrame === "full" + ? await sharp(generatedBuffer, { failOn: "none" }) + .rotate() + .resize(sourceWidth, sourceHeight, { + fit: "cover", + position: "center", + }) + .extract(workspaceBox) + .png({ compressionLevel: 6 }) + .toBuffer() + : await sharp(generatedBuffer, { failOn: "none" }) + .rotate() + .resize(workspaceBox.width, workspaceBox.height, { + fit: "cover", + position: "center", + }) + .png({ compressionLevel: 6 }) + .toBuffer(); const diffPatchMask = composite.maskMode === "diff" ? await buildLocalEditDiffCompositeMask({ @@ -3139,6 +3152,7 @@ async function applyScaleAdjustPatchCompositeToImages(params: { imageCount: mergedImages.length, customClipMaskApplied: Boolean(customClipPatchMaskRaw), diffRegionClipApplied: Boolean(regionClipPatchMask), + generatedFrame: composite.generatedFrame || "workspace", maskMode: composite.maskMode || "workspace", maskRegionCount: maskRegionBoxes?.length || 0, sourceHeight,