From e67aab87c54563a49f493afd9e70195bc0583ee0 Mon Sep 17 00:00:00 2001 From: 134 Date: Wed, 8 Jul 2026 00:37:28 +0800 Subject: [PATCH] fix: rebase product lock scale composite on source --- app/api/canvas/local-edit/route.ts | 3 ++ lib/canvas/generate-image.ts | 60 +++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index 69d076539..fa9893a85 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -5866,6 +5866,9 @@ export async function POST(request: NextRequest) { scaleAdjustPatchReference?.productLockOverlayUrl as string, protectMaskImageUrl: scaleAdjustPatchReference?.productLockProtectMaskUrl || undefined, + repairMaskImageUrl: + scaleAdjustPatchReference?.compositeClipMaskUrl || undefined, + sourceImageUrl, sourceHeight, sourceWidth, } diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts index 51f07b37a..fe8b00185 100644 --- a/lib/canvas/generate-image.ts +++ b/lib/canvas/generate-image.ts @@ -74,6 +74,8 @@ export interface CanvasScaleAdjustPatchCompositeConfig { export interface CanvasScaleAdjustProductLockCompositeConfig { overlayImageUrl: string; protectMaskImageUrl?: string; + repairMaskImageUrl?: string; + sourceImageUrl?: string; sourceHeight: number; sourceWidth: number; } @@ -566,6 +568,8 @@ function normalizeScaleAdjustProductLockCompositeConfig( const overlayImageUrl = String(value.overlayImageUrl || "").trim(); if (!/^https?:\/\//i.test(overlayImageUrl)) return null; const protectMaskImageUrl = String(value.protectMaskImageUrl || "").trim(); + const repairMaskImageUrl = String(value.repairMaskImageUrl || "").trim(); + const sourceImageUrl = String(value.sourceImageUrl || "").trim(); const sourceWidth = Math.round(Number(value.sourceWidth || 0)); const sourceHeight = Math.round(Number(value.sourceHeight || 0)); if (sourceWidth <= 0 || sourceHeight <= 0) return null; @@ -575,6 +579,12 @@ function normalizeScaleAdjustProductLockCompositeConfig( ...(protectMaskImageUrl && /^https?:\/\//i.test(protectMaskImageUrl) ? { protectMaskImageUrl } : {}), + ...(repairMaskImageUrl && /^https?:\/\//i.test(repairMaskImageUrl) + ? { repairMaskImageUrl } + : {}), + ...(sourceImageUrl && /^https?:\/\//i.test(sourceImageUrl) + ? { sourceImageUrl } + : {}), sourceHeight, sourceWidth, }; @@ -3283,6 +3293,18 @@ async function applyScaleAdjustProductLockCompositeToImages(params: { const protectMaskBuffer = composite.protectMaskImageUrl ? await fetchImageBufferFromUrl(composite.protectMaskImageUrl) : null; + const repairMaskBuffer = composite.repairMaskImageUrl + ? await fetchImageBufferFromUrl(composite.repairMaskImageUrl) + : null; + const sourceBaseBuffer = composite.sourceImageUrl + ? await sharp(await fetchImageBufferFromUrl(composite.sourceImageUrl), { + failOn: "none", + }) + .rotate() + .ensureAlpha() + .png({ compressionLevel: 6 }) + .toBuffer() + : null; const overlayMetadata = await sharp(overlayBuffer, { failOn: "none" }).metadata(); const overlayWidth = Math.round(Number(overlayMetadata.width || 0)); const overlayHeight = Math.round(Number(overlayMetadata.height || 0)); @@ -3324,10 +3346,42 @@ async function applyScaleAdjustProductLockCompositeToImages(params: { width: generatedWidth, }) : resizedOverlayBaseBuffer; - const mergedPngBuffer = await sharp(normalizedGeneratedBuffer, { + const baseForProductLock = sourceBaseBuffer && repairMaskBuffer + ? await sharp(sourceBaseBuffer, { failOn: "none" }) + .resize(generatedWidth, generatedHeight, { + fit: "fill", + kernel: sharp.kernel.lanczos3, + }) + .ensureAlpha() + .png({ compressionLevel: 6 }) + .toBuffer() + : normalizedGeneratedBuffer; + const generatedRepairOverlay = + sourceBaseBuffer && repairMaskBuffer + ? await applyRawAlphaToImage({ + alphaMaskRaw: await sharp(repairMaskBuffer, { failOn: "none" }) + .resize(generatedWidth, generatedHeight, { fit: "fill" }) + .removeAlpha() + .extractChannel(0) + .blur(Math.max(4, Math.round(Math.min(generatedWidth, generatedHeight) * 0.006))) + .raw() + .toBuffer(), + height: generatedHeight, + imageBuffer: normalizedGeneratedBuffer, + width: generatedWidth, + }) + : null; + const mergedPngBuffer = await sharp(baseForProductLock, { failOn: "none", }) - .composite([{ input: resizedOverlayBuffer, left: 0, top: 0 }]) + .composite( + generatedRepairOverlay + ? [ + { input: generatedRepairOverlay, left: 0, top: 0 }, + { input: resizedOverlayBuffer, left: 0, top: 0 }, + ] + : [{ input: resizedOverlayBuffer, left: 0, top: 0 }] + ) .png({ compressionLevel: 6 }) .toBuffer(); const mergedBuffer = await sharp(mergedPngBuffer, { failOn: "none" }) @@ -3364,6 +3418,8 @@ async function applyScaleAdjustProductLockCompositeToImages(params: { overlayHeight, overlayWidth, protectMaskApplied: Boolean(protectMaskBuffer), + repairMaskApplied: Boolean(repairMaskBuffer && sourceBaseBuffer), + sourceBaseApplied: Boolean(sourceBaseBuffer), sourceHeight: composite.sourceHeight, sourceWidth: composite.sourceWidth, });