From 4376a21147ad1af4b3748d677b96c5bc846cfd8f Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Sat, 28 Jun 2025 19:48:57 +0200 Subject: [PATCH] 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'). --- canvas_node.py | 3 ++- js/Canvas.js | 12 ++++++------ js/CanvasLayers.js | 42 ++++++++++++++++++++++++++---------------- js/CanvasView.js | 10 ++++++++-- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/canvas_node.py b/canvas_node.py index c7bab11..06f2926 100644 --- a/canvas_node.py +++ b/canvas_node.py @@ -167,6 +167,7 @@ class CanvasNode: def INPUT_TYPES(cls): return { "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}), "node_id": ("STRING", {"default": "0", "hidden": True}), }, @@ -230,7 +231,7 @@ class CanvasNode: _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: diff --git a/js/Canvas.js b/js/Canvas.js index fdcafa8..ac66003 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -157,8 +157,8 @@ export class Canvas { return this.canvasLayers.pasteLayers(); } - async handlePaste(pasteMode) { - return this.canvasLayers.handlePaste(pasteMode); + async handlePaste(addMode) { + return this.canvasLayers.handlePaste(addMode); } @@ -194,13 +194,13 @@ export class Canvas { return this.canvasLayers.isRotationHandle(x, y); } - async addLayerWithImage(image, layerProps = {}) { - return this.canvasLayers.addLayerWithImage(image, layerProps); + async addLayerWithImage(image, layerProps = {}, addMode = 'default') { + return this.canvasLayers.addLayerWithImage(image, layerProps, addMode); } - async addLayer(image) { - return this.addLayerWithImage(image); + async addLayer(image, addMode = 'default') { + return this.addLayerWithImage(image, {}, addMode); } async removeLayer(index) { diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js index 05238e1..109dbca 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -66,7 +66,7 @@ export class CanvasLayers { log.info(`Pasted ${newLayers.length} layer(s).`); } - async handlePaste(pasteMode = 'mouse') { + async handlePaste(addMode = 'mouse') { try { if (!navigator.clipboard?.read) { log.info("Browser does not support clipboard read API. Falling back to internal paste."); @@ -86,15 +86,7 @@ export class CanvasLayers { reader.onload = (event) => { const img = new Image(); img.onload = async () => { - let layerProps = {}; - 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); + await this.addLayerWithImage(img, {}, addMode); }; 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) { 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(); await saveImage(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 = { image: image, imageId: imageId, - x: (this.canvasLayers.width - image.width) / 2, - y: (this.canvasLayers.height - image.height) / 2, - width: image.width, - height: image.height, + x: finalX, + y: finalY, + width: finalWidth, + height: finalHeight, rotation: 0, zIndex: this.canvasLayers.layers.length, blendMode: 'normal', diff --git a/js/CanvasView.js b/js/CanvasView.js index a4a377b..47063f8 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -527,6 +527,8 @@ async function createCanvasWidget(node, widget, app) { textContent: "Add Image", title: "Add image from file", onclick: () => { + const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add"); + const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center'; const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; @@ -537,7 +539,7 @@ async function createCanvasWidget(node, widget, app) { reader.onload = (event) => { const img = new Image(); img.onload = () => { - canvas.addLayer(img); + canvas.addLayer(img, addMode); }; img.src = event.target.result; }; @@ -555,7 +557,11 @@ async function createCanvasWidget(node, widget, app) { $el("button.painter-button.primary", { textContent: "Paste Image", 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); + } }), ]),