mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-26 14:48:52 -03:00
Add layer flipH/flipV properties and rendering support
Refactors horizontal and vertical mirroring to toggle flipH/flipV properties on layers instead of modifying image data. Updates rendering logic in CanvasLayers and CanvasRenderer to apply horizontal/vertical flipping via canvas transforms. Adds flipH and flipV to Layer type and includes them in state signature calculation.
This commit is contained in:
@@ -306,52 +306,18 @@ export class CanvasLayers {
|
|||||||
async mirrorHorizontal() {
|
async mirrorHorizontal() {
|
||||||
if (this.canvas.canvasSelection.selectedLayers.length === 0)
|
if (this.canvas.canvasSelection.selectedLayers.length === 0)
|
||||||
return;
|
return;
|
||||||
const promises = this.canvas.canvasSelection.selectedLayers.map((layer) => {
|
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||||
return new Promise(resolve => {
|
layer.flipH = !layer.flipH;
|
||||||
const tempCanvas = document.createElement('canvas');
|
|
||||||
const tempCtx = tempCanvas.getContext('2d');
|
|
||||||
if (!tempCtx)
|
|
||||||
return;
|
|
||||||
tempCanvas.width = layer.image.width;
|
|
||||||
tempCanvas.height = layer.image.height;
|
|
||||||
tempCtx.translate(tempCanvas.width, 0);
|
|
||||||
tempCtx.scale(-1, 1);
|
|
||||||
tempCtx.drawImage(layer.image, 0, 0);
|
|
||||||
const newImage = new Image();
|
|
||||||
newImage.onload = () => {
|
|
||||||
layer.image = newImage;
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
newImage.src = tempCanvas.toDataURL();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
|
||||||
this.canvas.render();
|
this.canvas.render();
|
||||||
this.canvas.requestSaveState();
|
this.canvas.requestSaveState();
|
||||||
}
|
}
|
||||||
async mirrorVertical() {
|
async mirrorVertical() {
|
||||||
if (this.canvas.canvasSelection.selectedLayers.length === 0)
|
if (this.canvas.canvasSelection.selectedLayers.length === 0)
|
||||||
return;
|
return;
|
||||||
const promises = this.canvas.canvasSelection.selectedLayers.map((layer) => {
|
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||||
return new Promise(resolve => {
|
layer.flipV = !layer.flipV;
|
||||||
const tempCanvas = document.createElement('canvas');
|
|
||||||
const tempCtx = tempCanvas.getContext('2d');
|
|
||||||
if (!tempCtx)
|
|
||||||
return;
|
|
||||||
tempCanvas.width = layer.image.width;
|
|
||||||
tempCanvas.height = layer.image.height;
|
|
||||||
tempCtx.translate(0, tempCanvas.height);
|
|
||||||
tempCtx.scale(1, -1);
|
|
||||||
tempCtx.drawImage(layer.image, 0, 0);
|
|
||||||
const newImage = new Image();
|
|
||||||
newImage.onload = () => {
|
|
||||||
layer.image = newImage;
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
newImage.src = tempCanvas.toDataURL();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
|
||||||
this.canvas.render();
|
this.canvas.render();
|
||||||
this.canvas.requestSaveState();
|
this.canvas.requestSaveState();
|
||||||
}
|
}
|
||||||
@@ -759,6 +725,11 @@ export class CanvasLayers {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
tempCtx.translate(centerX, centerY);
|
tempCtx.translate(centerX, centerY);
|
||||||
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
tempCtx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||||
tempCtx.restore();
|
tempCtx.restore();
|
||||||
});
|
});
|
||||||
@@ -824,6 +795,11 @@ export class CanvasLayers {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
tempCtx.translate(centerX, centerY);
|
tempCtx.translate(centerX, centerY);
|
||||||
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
tempCtx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
tempCtx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||||
tempCtx.restore();
|
tempCtx.restore();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ export class CanvasRenderer {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
ctx.translate(centerX, centerY);
|
ctx.translate(centerX, centerY);
|
||||||
ctx.rotate(layer.rotation * Math.PI / 180);
|
ctx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
ctx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = 'high';
|
||||||
ctx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
ctx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||||
|
|||||||
@@ -111,7 +111,9 @@ export function getStateSignature(layers) {
|
|||||||
rotation: Math.round((layer.rotation || 0) * 100) / 100,
|
rotation: Math.round((layer.rotation || 0) * 100) / 100,
|
||||||
zIndex: layer.zIndex,
|
zIndex: layer.zIndex,
|
||||||
blendMode: layer.blendMode || 'normal',
|
blendMode: layer.blendMode || 'normal',
|
||||||
opacity: layer.opacity !== undefined ? Math.round(layer.opacity * 100) / 100 : 1
|
opacity: layer.opacity !== undefined ? Math.round(layer.opacity * 100) / 100 : 1,
|
||||||
|
flipH: !!layer.flipH,
|
||||||
|
flipV: !!layer.flipV
|
||||||
};
|
};
|
||||||
if (layer.imageId) {
|
if (layer.imageId) {
|
||||||
sig.imageId = layer.imageId;
|
sig.imageId = layer.imageId;
|
||||||
|
|||||||
@@ -351,58 +351,18 @@ export class CanvasLayers {
|
|||||||
|
|
||||||
async mirrorHorizontal(): Promise<void> {
|
async mirrorHorizontal(): Promise<void> {
|
||||||
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
||||||
|
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||||
const promises = this.canvas.canvasSelection.selectedLayers.map((layer: Layer) => {
|
layer.flipH = !layer.flipH;
|
||||||
return new Promise<void>(resolve => {
|
|
||||||
const tempCanvas = document.createElement('canvas');
|
|
||||||
const tempCtx = tempCanvas.getContext('2d');
|
|
||||||
if (!tempCtx) return;
|
|
||||||
tempCanvas.width = layer.image.width;
|
|
||||||
tempCanvas.height = layer.image.height;
|
|
||||||
|
|
||||||
tempCtx.translate(tempCanvas.width, 0);
|
|
||||||
tempCtx.scale(-1, 1);
|
|
||||||
tempCtx.drawImage(layer.image, 0, 0);
|
|
||||||
|
|
||||||
const newImage = new Image();
|
|
||||||
newImage.onload = () => {
|
|
||||||
layer.image = newImage;
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
newImage.src = tempCanvas.toDataURL();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
this.canvas.render();
|
this.canvas.render();
|
||||||
this.canvas.requestSaveState();
|
this.canvas.requestSaveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
async mirrorVertical(): Promise<void> {
|
async mirrorVertical(): Promise<void> {
|
||||||
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
if (this.canvas.canvasSelection.selectedLayers.length === 0) return;
|
||||||
|
this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
|
||||||
const promises = this.canvas.canvasSelection.selectedLayers.map((layer: Layer) => {
|
layer.flipV = !layer.flipV;
|
||||||
return new Promise<void>(resolve => {
|
|
||||||
const tempCanvas = document.createElement('canvas');
|
|
||||||
const tempCtx = tempCanvas.getContext('2d');
|
|
||||||
if (!tempCtx) return;
|
|
||||||
tempCanvas.width = layer.image.width;
|
|
||||||
tempCanvas.height = layer.image.height;
|
|
||||||
|
|
||||||
tempCtx.translate(0, tempCanvas.height);
|
|
||||||
tempCtx.scale(1, -1);
|
|
||||||
tempCtx.drawImage(layer.image, 0, 0);
|
|
||||||
|
|
||||||
const newImage = new Image();
|
|
||||||
newImage.onload = () => {
|
|
||||||
layer.image = newImage;
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
newImage.src = tempCanvas.toDataURL();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
this.canvas.render();
|
this.canvas.render();
|
||||||
this.canvas.requestSaveState();
|
this.canvas.requestSaveState();
|
||||||
}
|
}
|
||||||
@@ -896,6 +856,13 @@ export class CanvasLayers {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
tempCtx.translate(centerX, centerY);
|
tempCtx.translate(centerX, centerY);
|
||||||
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
tempCtx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
|
|
||||||
tempCtx.drawImage(
|
tempCtx.drawImage(
|
||||||
layer.image,
|
layer.image,
|
||||||
-layer.width / 2, -layer.height / 2,
|
-layer.width / 2, -layer.height / 2,
|
||||||
@@ -979,6 +946,13 @@ export class CanvasLayers {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
tempCtx.translate(centerX, centerY);
|
tempCtx.translate(centerX, centerY);
|
||||||
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
tempCtx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
|
|
||||||
tempCtx.drawImage(
|
tempCtx.drawImage(
|
||||||
layer.image,
|
layer.image,
|
||||||
-layer.width / 2, -layer.height / 2,
|
-layer.width / 2, -layer.height / 2,
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ export class CanvasRenderer {
|
|||||||
const centerY = layer.y + layer.height / 2;
|
const centerY = layer.y + layer.height / 2;
|
||||||
ctx.translate(centerX, centerY);
|
ctx.translate(centerX, centerY);
|
||||||
ctx.rotate(layer.rotation * Math.PI / 180);
|
ctx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
|
||||||
|
const scaleH = layer.flipH ? -1 : 1;
|
||||||
|
const scaleV = layer.flipV ? -1 : 1;
|
||||||
|
if (layer.flipH || layer.flipV) {
|
||||||
|
ctx.scale(scaleH, scaleV);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = 'high';
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export interface Layer {
|
|||||||
blendMode: string;
|
blendMode: string;
|
||||||
opacity: number;
|
opacity: number;
|
||||||
mask?: Float32Array;
|
mask?: Float32Array;
|
||||||
|
flipH?: boolean;
|
||||||
|
flipV?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComfyNode {
|
export interface ComfyNode {
|
||||||
|
|||||||
@@ -136,7 +136,9 @@ export function getStateSignature(layers: Layer[]): string {
|
|||||||
rotation: Math.round((layer.rotation || 0) * 100) / 100,
|
rotation: Math.round((layer.rotation || 0) * 100) / 100,
|
||||||
zIndex: layer.zIndex,
|
zIndex: layer.zIndex,
|
||||||
blendMode: layer.blendMode || 'normal',
|
blendMode: layer.blendMode || 'normal',
|
||||||
opacity: layer.opacity !== undefined ? Math.round(layer.opacity * 100) / 100 : 1
|
opacity: layer.opacity !== undefined ? Math.round(layer.opacity * 100) / 100 : 1,
|
||||||
|
flipH: !!layer.flipH,
|
||||||
|
flipV: !!layer.flipV
|
||||||
};
|
};
|
||||||
|
|
||||||
if (layer.imageId) {
|
if (layer.imageId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user