mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-25 14:25:44 -03:00
Add 'fit on add' option for image placement
Introduces a 'fit_on_add' boolean option to control whether images are fit to the canvas when added or pasted. Updates image addition and paste logic in Canvas, CanvasLayers, and CanvasView to support new placement modes ('fit', 'center', 'mouse', 'default').
This commit is contained in:
@@ -167,6 +167,7 @@ class CanvasNode:
|
|||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
|
"fit_on_add": ("BOOLEAN", {"default": False, "label_on": "Fit on Add/Paste", "label_off": "Default Behavior"}),
|
||||||
"trigger": ("INT", {"default": 0, "min": 0, "max": 99999999, "step": 1, "hidden": True}),
|
"trigger": ("INT", {"default": 0, "min": 0, "max": 99999999, "step": 1, "hidden": True}),
|
||||||
"node_id": ("STRING", {"default": "0", "hidden": True}),
|
"node_id": ("STRING", {"default": "0", "hidden": True}),
|
||||||
},
|
},
|
||||||
@@ -230,7 +231,7 @@ class CanvasNode:
|
|||||||
|
|
||||||
_processing_lock = threading.Lock()
|
_processing_lock = threading.Lock()
|
||||||
|
|
||||||
def process_canvas_image(self, trigger, node_id, prompt=None, unique_id=None):
|
def process_canvas_image(self, fit_on_add, trigger, node_id, prompt=None, unique_id=None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
|||||||
12
js/Canvas.js
12
js/Canvas.js
@@ -157,8 +157,8 @@ export class Canvas {
|
|||||||
return this.canvasLayers.pasteLayers();
|
return this.canvasLayers.pasteLayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
async handlePaste(pasteMode) {
|
async handlePaste(addMode) {
|
||||||
return this.canvasLayers.handlePaste(pasteMode);
|
return this.canvasLayers.handlePaste(addMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -194,13 +194,13 @@ export class Canvas {
|
|||||||
return this.canvasLayers.isRotationHandle(x, y);
|
return this.canvasLayers.isRotationHandle(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addLayerWithImage(image, layerProps = {}) {
|
async addLayerWithImage(image, layerProps = {}, addMode = 'default') {
|
||||||
return this.canvasLayers.addLayerWithImage(image, layerProps);
|
return this.canvasLayers.addLayerWithImage(image, layerProps, addMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async addLayer(image) {
|
async addLayer(image, addMode = 'default') {
|
||||||
return this.addLayerWithImage(image);
|
return this.addLayerWithImage(image, {}, addMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeLayer(index) {
|
async removeLayer(index) {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class CanvasLayers {
|
|||||||
log.info(`Pasted ${newLayers.length} layer(s).`);
|
log.info(`Pasted ${newLayers.length} layer(s).`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handlePaste(pasteMode = 'mouse') {
|
async handlePaste(addMode = 'mouse') {
|
||||||
try {
|
try {
|
||||||
if (!navigator.clipboard?.read) {
|
if (!navigator.clipboard?.read) {
|
||||||
log.info("Browser does not support clipboard read API. Falling back to internal paste.");
|
log.info("Browser does not support clipboard read API. Falling back to internal paste.");
|
||||||
@@ -86,15 +86,7 @@ export class CanvasLayers {
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = async () => {
|
img.onload = async () => {
|
||||||
let layerProps = {};
|
await this.addLayerWithImage(img, {}, addMode);
|
||||||
if (pasteMode === 'center') {
|
|
||||||
layerProps.x = (this.canvasLayers.width - img.width) / 2;
|
|
||||||
layerProps.y = (this.canvasLayers.height - img.height) / 2;
|
|
||||||
} else { // 'mouse' or default
|
|
||||||
layerProps.x = this.canvasLayers.lastMousePosition.x - img.width / 2;
|
|
||||||
layerProps.y = this.canvasLayers.lastMousePosition.y - img.height / 2;
|
|
||||||
}
|
|
||||||
await this.addLayerWithImage(img, layerProps);
|
|
||||||
};
|
};
|
||||||
img.src = event.target.result;
|
img.src = event.target.result;
|
||||||
};
|
};
|
||||||
@@ -113,23 +105,41 @@ export class CanvasLayers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addLayerWithImage = withErrorHandling(async (image, layerProps = {}) => {
|
addLayerWithImage = withErrorHandling(async (image, layerProps = {}, addMode = 'default') => {
|
||||||
if (!image) {
|
if (!image) {
|
||||||
throw createValidationError("Image is required for layer creation");
|
throw createValidationError("Image is required for layer creation");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Adding layer with image:", image);
|
log.debug("Adding layer with image:", image, "with mode:", addMode);
|
||||||
const imageId = generateUUID();
|
const imageId = generateUUID();
|
||||||
await saveImage(imageId, image.src);
|
await saveImage(imageId, image.src);
|
||||||
this.canvasLayers.imageCache.set(imageId, image.src);
|
this.canvasLayers.imageCache.set(imageId, image.src);
|
||||||
|
|
||||||
|
let finalWidth = image.width;
|
||||||
|
let finalHeight = image.height;
|
||||||
|
let finalX, finalY;
|
||||||
|
|
||||||
|
if (addMode === 'fit') {
|
||||||
|
const scale = Math.min(this.canvasLayers.width / image.width, this.canvasLayers.height / image.height);
|
||||||
|
finalWidth = image.width * scale;
|
||||||
|
finalHeight = image.height * scale;
|
||||||
|
finalX = (this.canvasLayers.width - finalWidth) / 2;
|
||||||
|
finalY = (this.canvasLayers.height - finalHeight) / 2;
|
||||||
|
} else if (addMode === 'mouse') {
|
||||||
|
finalX = this.canvasLayers.lastMousePosition.x - finalWidth / 2;
|
||||||
|
finalY = this.canvasLayers.lastMousePosition.y - finalHeight / 2;
|
||||||
|
} else { // 'center' or 'default'
|
||||||
|
finalX = (this.canvasLayers.width - finalWidth) / 2;
|
||||||
|
finalY = (this.canvasLayers.height - finalHeight) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
const layer = {
|
const layer = {
|
||||||
image: image,
|
image: image,
|
||||||
imageId: imageId,
|
imageId: imageId,
|
||||||
x: (this.canvasLayers.width - image.width) / 2,
|
x: finalX,
|
||||||
y: (this.canvasLayers.height - image.height) / 2,
|
y: finalY,
|
||||||
width: image.width,
|
width: finalWidth,
|
||||||
height: image.height,
|
height: finalHeight,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
zIndex: this.canvasLayers.layers.length,
|
zIndex: this.canvasLayers.layers.length,
|
||||||
blendMode: 'normal',
|
blendMode: 'normal',
|
||||||
|
|||||||
@@ -527,6 +527,8 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
textContent: "Add Image",
|
textContent: "Add Image",
|
||||||
title: "Add image from file",
|
title: "Add image from file",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
|
const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add");
|
||||||
|
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = 'image/*';
|
input.accept = 'image/*';
|
||||||
@@ -537,7 +539,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
canvas.addLayer(img);
|
canvas.addLayer(img, addMode);
|
||||||
};
|
};
|
||||||
img.src = event.target.result;
|
img.src = event.target.result;
|
||||||
};
|
};
|
||||||
@@ -555,7 +557,11 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
$el("button.painter-button.primary", {
|
$el("button.painter-button.primary", {
|
||||||
textContent: "Paste Image",
|
textContent: "Paste Image",
|
||||||
title: "Paste image from clipboard",
|
title: "Paste image from clipboard",
|
||||||
onclick: () => canvas.handlePaste('center')
|
onclick: () => {
|
||||||
|
const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add");
|
||||||
|
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||||
|
canvas.handlePaste(addMode);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user