mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Revert Cached Blend Area
This commit is contained in:
@@ -313,7 +313,6 @@ export class CanvasLayers {
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||
layer.width *= scale;
|
||||
layer.height *= scale;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -323,7 +322,6 @@ export class CanvasLayers {
|
||||
return;
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||
layer.rotation += angle;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -373,27 +371,64 @@ export class CanvasLayers {
|
||||
const blendArea = layer.blendArea ?? 0;
|
||||
const needsBlendAreaEffect = blendArea > 0;
|
||||
if (needsBlendAreaEffect) {
|
||||
// Check if we have a valid cached blended image
|
||||
if (layer.blendedImageCache && !layer.blendedImageDirty) {
|
||||
// Use cached blended image for optimal performance
|
||||
ctx.globalCompositeOperation = layer.blendMode || 'normal';
|
||||
ctx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1;
|
||||
ctx.drawImage(layer.blendedImageCache, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
log.debug(`Applying blend area effect for layer ${layer.id}, blendArea: ${blendArea}%`);
|
||||
// --- BLEND AREA MASK: Use cropped region if cropBounds is set ---
|
||||
let maskCanvas = null;
|
||||
let maskWidth = layer.width;
|
||||
let maskHeight = layer.height;
|
||||
if (layer.cropBounds && layer.originalWidth && layer.originalHeight) {
|
||||
// Create a cropped canvas
|
||||
const s = layer.cropBounds;
|
||||
const { canvas: cropCanvas, ctx: cropCtx } = createCanvas(s.width, s.height);
|
||||
if (cropCtx) {
|
||||
cropCtx.drawImage(layer.image, s.x, s.y, s.width, s.height, 0, 0, s.width, s.height);
|
||||
// Generate distance field mask for the cropped region
|
||||
maskCanvas = this.getDistanceFieldMaskSync(cropCanvas, blendArea);
|
||||
maskWidth = s.width;
|
||||
maskHeight = s.height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Cache is invalid or doesn't exist, update it
|
||||
this.updateLayerBlendEffect(layer);
|
||||
// Use the newly created cache if available, otherwise fallback
|
||||
if (layer.blendedImageCache) {
|
||||
// No crop, use full image
|
||||
maskCanvas = this.getDistanceFieldMaskSync(layer.image, blendArea);
|
||||
maskWidth = layer.originalWidth || layer.width;
|
||||
maskHeight = layer.originalHeight || layer.height;
|
||||
}
|
||||
if (maskCanvas) {
|
||||
// Create a temporary canvas for the masked layer
|
||||
const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height);
|
||||
if (tempCtx) {
|
||||
const s = layer.cropBounds || { x: 0, y: 0, width: layer.originalWidth, height: layer.originalHeight };
|
||||
if (!layer.originalWidth || !layer.originalHeight) {
|
||||
tempCtx.drawImage(layer.image, 0, 0, layer.width, layer.height);
|
||||
}
|
||||
else {
|
||||
const layerScaleX = layer.width / layer.originalWidth;
|
||||
const layerScaleY = layer.height / layer.originalHeight;
|
||||
const dWidth = s.width * layerScaleX;
|
||||
const dHeight = s.height * layerScaleY;
|
||||
const dX = s.x * layerScaleX;
|
||||
const dY = s.y * layerScaleY;
|
||||
tempCtx.drawImage(layer.image, s.x, s.y, s.width, s.height, dX, dY, dWidth, dHeight);
|
||||
// --- Apply the distance field mask only to the visible (cropped) area ---
|
||||
tempCtx.globalCompositeOperation = 'destination-in';
|
||||
// Scale the mask to match the drawn area
|
||||
tempCtx.drawImage(maskCanvas, 0, 0, maskWidth, maskHeight, dX, dY, dWidth, dHeight);
|
||||
}
|
||||
// Draw the result
|
||||
ctx.globalCompositeOperation = layer.blendMode || 'normal';
|
||||
ctx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1;
|
||||
ctx.drawImage(layer.blendedImageCache, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
ctx.drawImage(tempCanvas, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
}
|
||||
else {
|
||||
// Fallback to normal drawing
|
||||
this._drawLayerImage(ctx, layer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Fallback to normal drawing
|
||||
this._drawLayerImage(ctx, layer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Normal drawing without blend area effect
|
||||
@@ -425,92 +460,6 @@ export class CanvasLayers {
|
||||
dX, dY, dWidth, dHeight // destination rect (scaled and positioned within the transform frame)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Invalidates the blended image cache for a layer
|
||||
*/
|
||||
invalidateBlendCache(layer) {
|
||||
layer.blendedImageDirty = true;
|
||||
layer.blendedImageCache = undefined;
|
||||
}
|
||||
/**
|
||||
* Updates the blended image cache for a layer with blendArea effect
|
||||
*/
|
||||
updateLayerBlendEffect(layer) {
|
||||
const blendArea = layer.blendArea ?? 0;
|
||||
if (blendArea <= 0) {
|
||||
// No blend effect needed, clear cache
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.debug(`Updating blend effect cache for layer ${layer.id}, blendArea: ${blendArea}%`);
|
||||
// Create the blended image using the same logic as _drawLayer
|
||||
let maskCanvas = null;
|
||||
let maskWidth = layer.width;
|
||||
let maskHeight = layer.height;
|
||||
if (layer.cropBounds && layer.originalWidth && layer.originalHeight) {
|
||||
// Create a cropped canvas
|
||||
const s = layer.cropBounds;
|
||||
const { canvas: cropCanvas, ctx: cropCtx } = createCanvas(s.width, s.height);
|
||||
if (cropCtx) {
|
||||
cropCtx.drawImage(layer.image, s.x, s.y, s.width, s.height, 0, 0, s.width, s.height);
|
||||
// Generate distance field mask for the cropped region
|
||||
maskCanvas = this.getDistanceFieldMaskSync(cropCanvas, blendArea);
|
||||
maskWidth = s.width;
|
||||
maskHeight = s.height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No crop, use full image
|
||||
maskCanvas = this.getDistanceFieldMaskSync(layer.image, blendArea);
|
||||
maskWidth = layer.originalWidth || layer.width;
|
||||
maskHeight = layer.originalHeight || layer.height;
|
||||
}
|
||||
if (maskCanvas) {
|
||||
// Create the final blended canvas
|
||||
const { canvas: blendedCanvas, ctx: blendedCtx } = createCanvas(layer.width, layer.height);
|
||||
if (blendedCtx) {
|
||||
const s = layer.cropBounds || { x: 0, y: 0, width: layer.originalWidth, height: layer.originalHeight };
|
||||
if (!layer.originalWidth || !layer.originalHeight) {
|
||||
blendedCtx.drawImage(layer.image, 0, 0, layer.width, layer.height);
|
||||
}
|
||||
else {
|
||||
const layerScaleX = layer.width / layer.originalWidth;
|
||||
const layerScaleY = layer.height / layer.originalHeight;
|
||||
const dWidth = s.width * layerScaleX;
|
||||
const dHeight = s.height * layerScaleY;
|
||||
const dX = s.x * layerScaleX;
|
||||
const dY = s.y * layerScaleY;
|
||||
blendedCtx.drawImage(layer.image, s.x, s.y, s.width, s.height, dX, dY, dWidth, dHeight);
|
||||
// Apply the distance field mask only to the visible (cropped) area
|
||||
blendedCtx.globalCompositeOperation = 'destination-in';
|
||||
// Scale the mask to match the drawn area
|
||||
blendedCtx.drawImage(maskCanvas, 0, 0, maskWidth, maskHeight, dX, dY, dWidth, dHeight);
|
||||
}
|
||||
// Store the blended result in cache
|
||||
layer.blendedImageCache = blendedCanvas;
|
||||
layer.blendedImageDirty = false;
|
||||
log.debug(`Blend effect cache updated for layer ${layer.id}`);
|
||||
}
|
||||
else {
|
||||
log.warn(`Failed to create blended canvas context for layer ${layer.id}`);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn(`Failed to create distance field mask for layer ${layer.id}`);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Error updating blend effect for layer ${layer.id}:`, error);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
}
|
||||
getDistanceFieldMaskSync(imageOrCanvas, blendArea) {
|
||||
// Use a WeakMap for images, and a Map for canvases (since canvases are not always stable references)
|
||||
let cacheKey = imageOrCanvas;
|
||||
@@ -581,7 +530,6 @@ export class CanvasLayers {
|
||||
return;
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||
layer.flipH = !layer.flipH;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -591,7 +539,6 @@ export class CanvasLayers {
|
||||
return;
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||
layer.flipV = !layer.flipV;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -821,16 +768,10 @@ export class CanvasLayers {
|
||||
if (selectedLayer) {
|
||||
const newValue = parseInt(blendAreaSlider.value, 10);
|
||||
selectedLayer.blendArea = newValue;
|
||||
// Invalidate cache when blend area changes
|
||||
this.invalidateBlendCache(selectedLayer);
|
||||
this.canvas.render();
|
||||
}
|
||||
};
|
||||
blendAreaSlider.addEventListener('change', () => {
|
||||
if (selectedLayer) {
|
||||
// Update the blend effect cache when the slider value is finalized
|
||||
this.updateLayerBlendEffect(selectedLayer);
|
||||
}
|
||||
this.canvas.saveState();
|
||||
});
|
||||
blendAreaContainer.appendChild(blendAreaLabel);
|
||||
|
||||
@@ -286,9 +286,6 @@ If you see dark images or masks in the output, make sure node_id is set to ${cor
|
||||
const preparedLayers = await Promise.all(this.canvas.layers.map(async (layer, index) => {
|
||||
const newLayer = { ...layer, imageId: layer.imageId || '' };
|
||||
delete newLayer.image;
|
||||
// Remove cache properties that cannot be serialized for the worker
|
||||
delete newLayer.blendedImageCache;
|
||||
delete newLayer.blendedImageDirty;
|
||||
if (layer.image instanceof HTMLImageElement) {
|
||||
if (layer.imageId) {
|
||||
newLayer.imageId = layer.imageId;
|
||||
|
||||
@@ -359,7 +359,6 @@ export class CanvasLayers {
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||
layer.width *= scale;
|
||||
layer.height *= scale;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -370,7 +369,6 @@ export class CanvasLayers {
|
||||
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||
layer.rotation += angle;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -434,25 +432,80 @@ export class CanvasLayers {
|
||||
const needsBlendAreaEffect = blendArea > 0;
|
||||
|
||||
if (needsBlendAreaEffect) {
|
||||
// Check if we have a valid cached blended image
|
||||
if (layer.blendedImageCache && !layer.blendedImageDirty) {
|
||||
// Use cached blended image for optimal performance
|
||||
ctx.globalCompositeOperation = layer.blendMode as any || 'normal';
|
||||
ctx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1;
|
||||
ctx.drawImage(layer.blendedImageCache, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
} else {
|
||||
// Cache is invalid or doesn't exist, update it
|
||||
this.updateLayerBlendEffect(layer);
|
||||
log.debug(`Applying blend area effect for layer ${layer.id}, blendArea: ${blendArea}%`);
|
||||
|
||||
// Use the newly created cache if available, otherwise fallback
|
||||
if (layer.blendedImageCache) {
|
||||
// --- BLEND AREA MASK: Use cropped region if cropBounds is set ---
|
||||
let maskCanvas: HTMLCanvasElement | null = null;
|
||||
let maskWidth = layer.width;
|
||||
let maskHeight = layer.height;
|
||||
|
||||
if (layer.cropBounds && layer.originalWidth && layer.originalHeight) {
|
||||
// Create a cropped canvas
|
||||
const s = layer.cropBounds;
|
||||
const { canvas: cropCanvas, ctx: cropCtx } = createCanvas(s.width, s.height);
|
||||
if (cropCtx) {
|
||||
cropCtx.drawImage(
|
||||
layer.image,
|
||||
s.x, s.y, s.width, s.height,
|
||||
0, 0, s.width, s.height
|
||||
);
|
||||
// Generate distance field mask for the cropped region
|
||||
maskCanvas = this.getDistanceFieldMaskSync(cropCanvas, blendArea);
|
||||
maskWidth = s.width;
|
||||
maskHeight = s.height;
|
||||
}
|
||||
} else {
|
||||
// No crop, use full image
|
||||
maskCanvas = this.getDistanceFieldMaskSync(layer.image, blendArea);
|
||||
maskWidth = layer.originalWidth || layer.width;
|
||||
maskHeight = layer.originalHeight || layer.height;
|
||||
}
|
||||
|
||||
if (maskCanvas) {
|
||||
// Create a temporary canvas for the masked layer
|
||||
const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height);
|
||||
|
||||
if (tempCtx) {
|
||||
const s = layer.cropBounds || { x: 0, y: 0, width: layer.originalWidth, height: layer.originalHeight };
|
||||
|
||||
if (!layer.originalWidth || !layer.originalHeight) {
|
||||
tempCtx.drawImage(layer.image, 0, 0, layer.width, layer.height);
|
||||
} else {
|
||||
const layerScaleX = layer.width / layer.originalWidth;
|
||||
const layerScaleY = layer.height / layer.originalHeight;
|
||||
|
||||
const dWidth = s.width * layerScaleX;
|
||||
const dHeight = s.height * layerScaleY;
|
||||
const dX = s.x * layerScaleX;
|
||||
const dY = s.y * layerScaleY;
|
||||
|
||||
tempCtx.drawImage(
|
||||
layer.image,
|
||||
s.x, s.y, s.width, s.height,
|
||||
dX, dY, dWidth, dHeight
|
||||
);
|
||||
|
||||
// --- Apply the distance field mask only to the visible (cropped) area ---
|
||||
tempCtx.globalCompositeOperation = 'destination-in';
|
||||
// Scale the mask to match the drawn area
|
||||
tempCtx.drawImage(
|
||||
maskCanvas,
|
||||
0, 0, maskWidth, maskHeight,
|
||||
dX, dY, dWidth, dHeight
|
||||
);
|
||||
}
|
||||
|
||||
// Draw the result
|
||||
ctx.globalCompositeOperation = layer.blendMode as any || 'normal';
|
||||
ctx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1;
|
||||
ctx.drawImage(layer.blendedImageCache, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
ctx.drawImage(tempCanvas, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
} else {
|
||||
// Fallback to normal drawing
|
||||
this._drawLayerImage(ctx, layer);
|
||||
}
|
||||
} else {
|
||||
// Fallback to normal drawing
|
||||
this._drawLayerImage(ctx, layer);
|
||||
}
|
||||
} else {
|
||||
// Normal drawing without blend area effect
|
||||
@@ -495,113 +548,6 @@ export class CanvasLayers {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the blended image cache for a layer
|
||||
*/
|
||||
public invalidateBlendCache(layer: Layer): void {
|
||||
layer.blendedImageDirty = true;
|
||||
layer.blendedImageCache = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the blended image cache for a layer with blendArea effect
|
||||
*/
|
||||
public updateLayerBlendEffect(layer: Layer): void {
|
||||
const blendArea = layer.blendArea ?? 0;
|
||||
|
||||
if (blendArea <= 0) {
|
||||
// No blend effect needed, clear cache
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug(`Updating blend effect cache for layer ${layer.id}, blendArea: ${blendArea}%`);
|
||||
|
||||
// Create the blended image using the same logic as _drawLayer
|
||||
let maskCanvas: HTMLCanvasElement | null = null;
|
||||
let maskWidth = layer.width;
|
||||
let maskHeight = layer.height;
|
||||
|
||||
if (layer.cropBounds && layer.originalWidth && layer.originalHeight) {
|
||||
// Create a cropped canvas
|
||||
const s = layer.cropBounds;
|
||||
const { canvas: cropCanvas, ctx: cropCtx } = createCanvas(s.width, s.height);
|
||||
if (cropCtx) {
|
||||
cropCtx.drawImage(
|
||||
layer.image,
|
||||
s.x, s.y, s.width, s.height,
|
||||
0, 0, s.width, s.height
|
||||
);
|
||||
// Generate distance field mask for the cropped region
|
||||
maskCanvas = this.getDistanceFieldMaskSync(cropCanvas, blendArea);
|
||||
maskWidth = s.width;
|
||||
maskHeight = s.height;
|
||||
}
|
||||
} else {
|
||||
// No crop, use full image
|
||||
maskCanvas = this.getDistanceFieldMaskSync(layer.image, blendArea);
|
||||
maskWidth = layer.originalWidth || layer.width;
|
||||
maskHeight = layer.originalHeight || layer.height;
|
||||
}
|
||||
|
||||
if (maskCanvas) {
|
||||
// Create the final blended canvas
|
||||
const { canvas: blendedCanvas, ctx: blendedCtx } = createCanvas(layer.width, layer.height);
|
||||
|
||||
if (blendedCtx) {
|
||||
const s = layer.cropBounds || { x: 0, y: 0, width: layer.originalWidth, height: layer.originalHeight };
|
||||
|
||||
if (!layer.originalWidth || !layer.originalHeight) {
|
||||
blendedCtx.drawImage(layer.image, 0, 0, layer.width, layer.height);
|
||||
} else {
|
||||
const layerScaleX = layer.width / layer.originalWidth;
|
||||
const layerScaleY = layer.height / layer.originalHeight;
|
||||
|
||||
const dWidth = s.width * layerScaleX;
|
||||
const dHeight = s.height * layerScaleY;
|
||||
const dX = s.x * layerScaleX;
|
||||
const dY = s.y * layerScaleY;
|
||||
|
||||
blendedCtx.drawImage(
|
||||
layer.image,
|
||||
s.x, s.y, s.width, s.height,
|
||||
dX, dY, dWidth, dHeight
|
||||
);
|
||||
|
||||
// Apply the distance field mask only to the visible (cropped) area
|
||||
blendedCtx.globalCompositeOperation = 'destination-in';
|
||||
// Scale the mask to match the drawn area
|
||||
blendedCtx.drawImage(
|
||||
maskCanvas,
|
||||
0, 0, maskWidth, maskHeight,
|
||||
dX, dY, dWidth, dHeight
|
||||
);
|
||||
}
|
||||
|
||||
// Store the blended result in cache
|
||||
layer.blendedImageCache = blendedCanvas;
|
||||
layer.blendedImageDirty = false;
|
||||
|
||||
log.debug(`Blend effect cache updated for layer ${layer.id}`);
|
||||
} else {
|
||||
log.warn(`Failed to create blended canvas context for layer ${layer.id}`);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
} else {
|
||||
log.warn(`Failed to create distance field mask for layer ${layer.id}`);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error updating blend effect for layer ${layer.id}:`, error);
|
||||
layer.blendedImageCache = undefined;
|
||||
layer.blendedImageDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
private getDistanceFieldMaskSync(imageOrCanvas: HTMLImageElement | HTMLCanvasElement, blendArea: number): HTMLCanvasElement | null {
|
||||
// Use a WeakMap for images, and a Map for canvases (since canvases are not always stable references)
|
||||
let cacheKey: any = imageOrCanvas;
|
||||
@@ -669,7 +615,6 @@ export class CanvasLayers {
|
||||
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||
layer.flipH = !layer.flipH;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -679,7 +624,6 @@ export class CanvasLayers {
|
||||
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||
layer.flipV = !layer.flipV;
|
||||
this.invalidateBlendCache(layer);
|
||||
});
|
||||
this.canvas.render();
|
||||
this.canvas.requestSaveState();
|
||||
@@ -965,17 +909,11 @@ export class CanvasLayers {
|
||||
if (selectedLayer) {
|
||||
const newValue = parseInt(blendAreaSlider.value, 10);
|
||||
selectedLayer.blendArea = newValue;
|
||||
// Invalidate cache when blend area changes
|
||||
this.invalidateBlendCache(selectedLayer);
|
||||
this.canvas.render();
|
||||
}
|
||||
};
|
||||
|
||||
blendAreaSlider.addEventListener('change', () => {
|
||||
if (selectedLayer) {
|
||||
// Update the blend effect cache when the slider value is finalized
|
||||
this.updateLayerBlendEffect(selectedLayer);
|
||||
}
|
||||
this.canvas.saveState();
|
||||
});
|
||||
|
||||
|
||||
@@ -326,9 +326,6 @@ If you see dark images or masks in the output, make sure node_id is set to ${cor
|
||||
const preparedLayers = await Promise.all(this.canvas.layers.map(async (layer: Layer, index: number) => {
|
||||
const newLayer: Omit<Layer, 'image'> & { imageId: string } = { ...layer, imageId: layer.imageId || '' };
|
||||
delete (newLayer as any).image;
|
||||
// Remove cache properties that cannot be serialized for the worker
|
||||
delete (newLayer as any).blendedImageCache;
|
||||
delete (newLayer as any).blendedImageDirty;
|
||||
|
||||
if (layer.image instanceof HTMLImageElement) {
|
||||
if (layer.imageId) {
|
||||
|
||||
@@ -28,8 +28,6 @@ export interface Layer {
|
||||
width: number; // szerokość widocznego obszaru
|
||||
height: number; // wysokość widocznego obszaru
|
||||
};
|
||||
blendedImageCache?: HTMLCanvasElement; // Cache for the pre-rendered blendArea effect
|
||||
blendedImageDirty?: boolean; // Flag to invalidate the cache
|
||||
}
|
||||
|
||||
export interface ComfyNode {
|
||||
|
||||
Reference in New Issue
Block a user