diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts
index 43d55ec03..c859ee846 100644
--- a/app/api/canvas/local-edit/route.ts
+++ b/app/api/canvas/local-edit/route.ts
@@ -3900,6 +3900,7 @@ export async function POST(request: NextRequest) {
featherMaxPx: 42,
featherMinPx: 10,
featherRatio: 0.018,
+ regions: scaleAdjustCompositeRegions,
sourceImageUrl,
workspaceRegion: scaleAdjustPatchReference.workspaceRect,
}
diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts
index 937edfa3c..28617af9b 100644
--- a/lib/canvas/generate-image.ts
+++ b/lib/canvas/generate-image.ts
@@ -56,6 +56,7 @@ export interface CanvasLocalEditCompositeConfig {
export interface CanvasScaleAdjustPatchCompositeConfig {
sourceImageUrl: string;
workspaceRegion: CanvasLocalEditCompositeRegion;
+ regions?: CanvasLocalEditCompositeRegion[];
featherMinPx?: number;
featherMaxPx?: number;
featherRatio?: number;
@@ -384,10 +385,18 @@ function normalizeScaleAdjustPatchCompositeConfig(
if (!/^https?:\/\//i.test(sourceImageUrl)) return null;
const workspaceRegion = normalizeCanvasRegion(value.workspaceRegion);
if (!workspaceRegion) return null;
+ const regions = Array.isArray(value.regions)
+ ? value.regions
+ .map((region) => normalizeCanvasRegion(region))
+ .filter((region): region is CanvasLocalEditCompositeRegion =>
+ Boolean(region)
+ )
+ : [];
return {
sourceImageUrl,
workspaceRegion,
+ ...(regions.length > 0 ? { regions } : {}),
featherMaxPx: normalizeOptionalNumber(value.featherMaxPx, {
max: 180,
min: 0,
@@ -2335,6 +2344,7 @@ function buildScaleAdjustPatchCompositeMaskSvg(params: {
width: number;
height: number;
featherPx: number;
+ regions?: Array<{ left: number; top: number; width: number; height: number }>;
touchesLeft: boolean;
touchesTop: boolean;
touchesRight: boolean;
@@ -2342,6 +2352,46 @@ function buildScaleAdjustPatchCompositeMaskSvg(params: {
}) {
const { height, width } = params;
const featherPx = Math.max(0, Math.round(params.featherPx));
+ const regions = params.regions?.filter(
+ (region) => region.width > 0 && region.height > 0
+ );
+ if (regions?.length) {
+ const blur = Math.max(1, featherPx);
+ const softPad = Math.max(2, Math.round(featherPx * 1.35));
+ const corePad = Math.max(0, Math.round(featherPx * 0.22));
+ const softRects = regions
+ .map((region) => {
+ const left = Math.max(0, region.left - softPad);
+ const top = Math.max(0, region.top - softPad);
+ const right = Math.min(width, region.left + region.width + softPad);
+ const bottom = Math.min(height, region.top + region.height + softPad);
+ return ``;
+ })
+ .join("");
+ const coreRects = regions
+ .map((region) => {
+ const left = Math.max(0, region.left - corePad);
+ const top = Math.max(0, region.top - corePad);
+ const right = Math.min(width, region.left + region.width + corePad);
+ const bottom = Math.min(height, region.top + region.height + corePad);
+ return ``;
+ })
+ .join("");
+
+ return Buffer.from(`
+
+ `);
+ }
+
if (featherPx <= 0) {
return Buffer.from(`