mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-26 06:45:44 -03:00
Refactor JS code for consistent formatting and style
Standardized spacing and object literal formatting across multiple JS files for improved readability and consistency. No functional changes were made. Minor Python formatting adjustment for line length in canvas_node.py.
This commit is contained in:
@@ -251,7 +251,8 @@ class CanvasNode:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Wczytaj obraz bez maski
|
# Wczytaj obraz bez maski
|
||||||
path_image_without_mask = folder_paths.get_annotated_filepath(canvas_image.replace('.png', '_without_mask.png'))
|
path_image_without_mask = folder_paths.get_annotated_filepath(
|
||||||
|
canvas_image.replace('.png', '_without_mask.png'))
|
||||||
i = Image.open(path_image_without_mask)
|
i = Image.open(path_image_without_mask)
|
||||||
i = ImageOps.exif_transpose(i)
|
i = ImageOps.exif_transpose(i)
|
||||||
if i.mode not in ['RGB', 'RGBA']:
|
if i.mode not in ['RGB', 'RGBA']:
|
||||||
|
|||||||
30
js/Canvas.js
30
js/Canvas.js
@@ -1,5 +1,5 @@
|
|||||||
import { getCanvasState, setCanvasState, removeCanvasState } from "./db.js";
|
import {getCanvasState, setCanvasState, removeCanvasState} from "./db.js";
|
||||||
import { MaskTool } from "./Mask_tool.js";
|
import {MaskTool} from "./Mask_tool.js";
|
||||||
|
|
||||||
export class Canvas {
|
export class Canvas {
|
||||||
constructor(node, widget) {
|
constructor(node, widget) {
|
||||||
@@ -103,8 +103,8 @@ export class Canvas {
|
|||||||
|
|
||||||
this.width = savedState.width || 512;
|
this.width = savedState.width || 512;
|
||||||
this.height = savedState.height || 512;
|
this.height = savedState.height || 512;
|
||||||
this.viewport = savedState.viewport || { x: -(this.width / 4), y: -(this.height / 4), zoom: 0.8 };
|
this.viewport = savedState.viewport || {x: -(this.width / 4), y: -(this.height / 4), zoom: 0.8};
|
||||||
|
|
||||||
this.updateCanvasSize(this.width, this.height, false);
|
this.updateCanvasSize(this.width, this.height, false);
|
||||||
console.log(`Canvas resized to ${this.width}x${this.height} and viewport set.`);
|
console.log(`Canvas resized to ${this.width}x${this.height} and viewport set.`);
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export class Canvas {
|
|||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
console.log(`Layer ${index}: Image loaded successfully.`);
|
console.log(`Layer ${index}: Image loaded successfully.`);
|
||||||
const newLayer = { ...layerData, image: img };
|
const newLayer = {...layerData, image: img};
|
||||||
delete newLayer.imageSrc;
|
delete newLayer.imageSrc;
|
||||||
resolve(newLayer);
|
resolve(newLayer);
|
||||||
};
|
};
|
||||||
@@ -126,7 +126,7 @@ export class Canvas {
|
|||||||
img.src = layerData.imageSrc;
|
img.src = layerData.imageSrc;
|
||||||
} else {
|
} else {
|
||||||
console.log(`Layer ${index}: No imageSrc found, resolving layer data.`);
|
console.log(`Layer ${index}: No imageSrc found, resolving layer data.`);
|
||||||
resolve({ ...layerData });
|
resolve({...layerData});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -134,7 +134,7 @@ export class Canvas {
|
|||||||
const loadedLayers = await Promise.all(imagePromises);
|
const loadedLayers = await Promise.all(imagePromises);
|
||||||
this.layers = loadedLayers.filter(l => l !== null);
|
this.layers = loadedLayers.filter(l => l !== null);
|
||||||
console.log(`Loaded ${this.layers.length} layers.`);
|
console.log(`Loaded ${this.layers.length} layers.`);
|
||||||
|
|
||||||
this.updateSelectionAfterHistory();
|
this.updateSelectionAfterHistory();
|
||||||
this.render();
|
this.render();
|
||||||
console.log("Canvas state loaded successfully from localStorage for node", this.node.id);
|
console.log("Canvas state loaded successfully from localStorage for node", this.node.id);
|
||||||
@@ -156,7 +156,7 @@ export class Canvas {
|
|||||||
try {
|
try {
|
||||||
const state = {
|
const state = {
|
||||||
layers: this.layers.map((layer, index) => {
|
layers: this.layers.map((layer, index) => {
|
||||||
const newLayer = { ...layer };
|
const newLayer = {...layer};
|
||||||
if (layer.image instanceof HTMLImageElement) {
|
if (layer.image instanceof HTMLImageElement) {
|
||||||
console.log(`Layer ${index}: Serializing image to data:URL.`);
|
console.log(`Layer ${index}: Serializing image to data:URL.`);
|
||||||
newLayer.imageSrc = layer.image.src;
|
newLayer.imageSrc = layer.image.src;
|
||||||
@@ -189,7 +189,7 @@ export class Canvas {
|
|||||||
|
|
||||||
cloneLayers(layers) {
|
cloneLayers(layers) {
|
||||||
return layers.map(layer => {
|
return layers.map(layer => {
|
||||||
const newLayer = { ...layer };
|
const newLayer = {...layer};
|
||||||
// Obiekty Image nie są klonowane, aby oszczędzać pamięć.
|
// Obiekty Image nie są klonowane, aby oszczędzać pamięć.
|
||||||
// Zakładamy, że same dane obrazu się nie zmieniają.
|
// Zakładamy, że same dane obrazu się nie zmieniają.
|
||||||
return newLayer;
|
return newLayer;
|
||||||
@@ -198,7 +198,7 @@ export class Canvas {
|
|||||||
|
|
||||||
getStateSignature(layers) {
|
getStateSignature(layers) {
|
||||||
return JSON.stringify(layers.map(layer => {
|
return JSON.stringify(layers.map(layer => {
|
||||||
const sig = { ...layer };
|
const sig = {...layer};
|
||||||
if (sig.image instanceof HTMLImageElement) {
|
if (sig.image instanceof HTMLImageElement) {
|
||||||
sig.imageSrc = sig.image.src;
|
sig.imageSrc = sig.image.src;
|
||||||
}
|
}
|
||||||
@@ -251,13 +251,13 @@ export class Canvas {
|
|||||||
this.render();
|
this.render();
|
||||||
this.updateHistoryButtons();
|
this.updateHistoryButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectionAfterHistory() {
|
updateSelectionAfterHistory() {
|
||||||
const newSelectedLayers = [];
|
const newSelectedLayers = [];
|
||||||
if (this.selectedLayers) {
|
if (this.selectedLayers) {
|
||||||
this.selectedLayers.forEach(sl => {
|
this.selectedLayers.forEach(sl => {
|
||||||
const found = this.layers.find(l => l.id === sl.id);
|
const found = this.layers.find(l => l.id === sl.id);
|
||||||
if(found) newSelectedLayers.push(found);
|
if (found) newSelectedLayers.push(found);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.updateSelection(newSelectedLayers);
|
this.updateSelection(newSelectedLayers);
|
||||||
@@ -713,7 +713,7 @@ export class Canvas {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedLayer) {
|
if (this.selectedLayer) {
|
||||||
if (e.key === 'Delete') {
|
if (e.key === 'Delete') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -986,9 +986,9 @@ export class Canvas {
|
|||||||
const newAspectRatio = Math.abs(newWidth / newHeight);
|
const newAspectRatio = Math.abs(newWidth / newHeight);
|
||||||
|
|
||||||
if (Math.abs(newWidth) > Math.abs(newHeight) * originalAspectRatio) {
|
if (Math.abs(newWidth) > Math.abs(newHeight) * originalAspectRatio) {
|
||||||
newHeight = (Math.sign(newHeight) || 1) * Math.abs(newWidth) / originalAspectRatio;
|
newHeight = (Math.sign(newHeight) || 1) * Math.abs(newWidth) / originalAspectRatio;
|
||||||
} else {
|
} else {
|
||||||
newWidth = (Math.sign(newWidth) || 1) * Math.abs(newHeight) * originalAspectRatio;
|
newWidth = (Math.sign(newWidth) || 1) * Math.abs(newHeight) * originalAspectRatio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {app} from "../../scripts/app.js";
|
|||||||
import {api} from "../../scripts/api.js";
|
import {api} from "../../scripts/api.js";
|
||||||
import {$el} from "../../scripts/ui.js";
|
import {$el} from "../../scripts/ui.js";
|
||||||
import {Canvas} from "./Canvas.js";
|
import {Canvas} from "./Canvas.js";
|
||||||
import { clearAllCanvasStates } from "./db.js";
|
import {clearAllCanvasStates} from "./db.js";
|
||||||
|
|
||||||
async function createCanvasWidget(node, widget, app) {
|
async function createCanvasWidget(node, widget, app) {
|
||||||
const canvas = new Canvas(node, widget);
|
const canvas = new Canvas(node, widget);
|
||||||
@@ -333,13 +333,13 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}, [
|
}, [
|
||||||
// --- Group: Help & I/O ---
|
// --- Group: Help & I/O ---
|
||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button", {
|
$el("button.painter-button", {
|
||||||
id: `open-editor-btn-${node.id}`,
|
id: `open-editor-btn-${node.id}`,
|
||||||
textContent: "⛶",
|
textContent: "⛶",
|
||||||
title: "Open in Editor",
|
title: "Open in Editor",
|
||||||
style: { minWidth: "40px", maxWidth: "40px", fontWeight: "bold" },
|
style: {minWidth: "40px", maxWidth: "40px", fontWeight: "bold"},
|
||||||
}),
|
}),
|
||||||
$el("button.painter-button", {
|
$el("button.painter-button", {
|
||||||
textContent: "?",
|
textContent: "?",
|
||||||
title: "Show shortcuts",
|
title: "Show shortcuts",
|
||||||
style: {
|
style: {
|
||||||
@@ -401,89 +401,89 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
|
|
||||||
// --- Group: Canvas & Layers ---
|
// --- Group: Canvas & Layers ---
|
||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button", {
|
$el("button.painter-button", {
|
||||||
textContent: "Canvas Size",
|
textContent: "Canvas Size",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
const dialog = $el("div.painter-dialog", {
|
const dialog = $el("div.painter-dialog", {
|
||||||
style: {
|
|
||||||
position: 'fixed',
|
|
||||||
left: '50%',
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
zIndex: '1000'
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
$el("div", {
|
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
position: 'fixed',
|
||||||
marginBottom: "10px"
|
left: '50%',
|
||||||
|
top: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
zIndex: '1000'
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
$el("label", {
|
$el("div", {
|
||||||
style: {
|
style: {
|
||||||
marginRight: "5px"
|
color: "white",
|
||||||
|
marginBottom: "10px"
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
$el("span", {}, ["Width: "])
|
$el("label", {
|
||||||
|
style: {
|
||||||
|
marginRight: "5px"
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
$el("span", {}, ["Width: "])
|
||||||
|
]),
|
||||||
|
$el("input", {
|
||||||
|
type: "number",
|
||||||
|
id: "canvas-width",
|
||||||
|
value: canvas.width,
|
||||||
|
min: "1",
|
||||||
|
max: "4096"
|
||||||
|
})
|
||||||
]),
|
]),
|
||||||
$el("input", {
|
$el("div", {
|
||||||
type: "number",
|
|
||||||
id: "canvas-width",
|
|
||||||
value: canvas.width,
|
|
||||||
min: "1",
|
|
||||||
max: "4096"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
$el("div", {
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
marginBottom: "10px"
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
$el("label", {
|
|
||||||
style: {
|
style: {
|
||||||
marginRight: "5px"
|
color: "white",
|
||||||
|
marginBottom: "10px"
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
$el("span", {}, ["Height: "])
|
$el("label", {
|
||||||
|
style: {
|
||||||
|
marginRight: "5px"
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
$el("span", {}, ["Height: "])
|
||||||
|
]),
|
||||||
|
$el("input", {
|
||||||
|
type: "number",
|
||||||
|
id: "canvas-height",
|
||||||
|
value: canvas.height,
|
||||||
|
min: "1",
|
||||||
|
max: "4096"
|
||||||
|
})
|
||||||
]),
|
]),
|
||||||
$el("input", {
|
$el("div", {
|
||||||
type: "number",
|
style: {
|
||||||
id: "canvas-height",
|
textAlign: "right"
|
||||||
value: canvas.height,
|
}
|
||||||
min: "1",
|
}, [
|
||||||
max: "4096"
|
$el("button", {
|
||||||
})
|
id: "cancel-size",
|
||||||
]),
|
textContent: "Cancel"
|
||||||
$el("div", {
|
}),
|
||||||
style: {
|
$el("button", {
|
||||||
textAlign: "right"
|
id: "confirm-size",
|
||||||
}
|
textContent: "OK"
|
||||||
}, [
|
})
|
||||||
$el("button", {
|
])
|
||||||
id: "cancel-size",
|
]);
|
||||||
textContent: "Cancel"
|
document.body.appendChild(dialog);
|
||||||
}),
|
|
||||||
$el("button", {
|
|
||||||
id: "confirm-size",
|
|
||||||
textContent: "OK"
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
document.body.appendChild(dialog);
|
|
||||||
|
|
||||||
document.getElementById('confirm-size').onclick = () => {
|
document.getElementById('confirm-size').onclick = () => {
|
||||||
const width = parseInt(document.getElementById('canvas-width').value) || canvas.width;
|
const width = parseInt(document.getElementById('canvas-width').value) || canvas.width;
|
||||||
const height = parseInt(document.getElementById('canvas-height').value) || canvas.height;
|
const height = parseInt(document.getElementById('canvas-height').value) || canvas.height;
|
||||||
canvas.updateCanvasSize(width, height);
|
canvas.updateCanvasSize(width, height);
|
||||||
document.body.removeChild(dialog);
|
document.body.removeChild(dialog);
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('cancel-size').onclick = () => {
|
document.getElementById('cancel-size').onclick = () => {
|
||||||
document.body.removeChild(dialog);
|
document.body.removeChild(dialog);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("button.painter-button.requires-selection", {
|
$el("button.painter-button.requires-selection", {
|
||||||
textContent: "Remove Layer",
|
textContent: "Remove Layer",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -496,7 +496,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("button.painter-button.requires-selection", {
|
$el("button.painter-button.requires-selection", {
|
||||||
textContent: "Layer Up",
|
textContent: "Layer Up",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
canvas.moveLayerUp();
|
canvas.moveLayerUp();
|
||||||
@@ -509,16 +509,31 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$el("div.painter-separator"),
|
$el("div.painter-separator"),
|
||||||
|
|
||||||
// --- Group: Transform ---
|
// --- Group: Transform ---
|
||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button.requires-selection", { textContent: "Rotate +90°", onclick: () => canvas.rotateLayer(90) }),
|
$el("button.painter-button.requires-selection", {
|
||||||
$el("button.painter-button.requires-selection", { textContent: "Scale +5%", onclick: () => canvas.resizeLayer(1.05) }),
|
textContent: "Rotate +90°",
|
||||||
$el("button.painter-button.requires-selection", { textContent: "Scale -5%", onclick: () => canvas.resizeLayer(0.95) }),
|
onclick: () => canvas.rotateLayer(90)
|
||||||
$el("button.painter-button.requires-selection", { textContent: "Mirror H", onclick: () => canvas.mirrorHorizontal() }),
|
}),
|
||||||
$el("button.painter-button.requires-selection", { textContent: "Mirror V", onclick: () => canvas.mirrorVertical() }),
|
$el("button.painter-button.requires-selection", {
|
||||||
|
textContent: "Scale +5%",
|
||||||
|
onclick: () => canvas.resizeLayer(1.05)
|
||||||
|
}),
|
||||||
|
$el("button.painter-button.requires-selection", {
|
||||||
|
textContent: "Scale -5%",
|
||||||
|
onclick: () => canvas.resizeLayer(0.95)
|
||||||
|
}),
|
||||||
|
$el("button.painter-button.requires-selection", {
|
||||||
|
textContent: "Mirror H",
|
||||||
|
onclick: () => canvas.mirrorHorizontal()
|
||||||
|
}),
|
||||||
|
$el("button.painter-button.requires-selection", {
|
||||||
|
textContent: "Mirror V",
|
||||||
|
onclick: () => canvas.mirrorVertical()
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$el("div.painter-separator"),
|
$el("div.painter-separator"),
|
||||||
@@ -527,17 +542,17 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button.requires-selection.matting-button", {
|
$el("button.painter-button.requires-selection.matting-button", {
|
||||||
textContent: "Matting",
|
textContent: "Matting",
|
||||||
onclick: async (e) => {
|
onclick: async (e) => {
|
||||||
const button = e.target.closest('.matting-button');
|
const button = e.target.closest('.matting-button');
|
||||||
if (button.classList.contains('loading')) return;
|
if (button.classList.contains('loading')) return;
|
||||||
|
|
||||||
const spinner = $el("div.matting-spinner");
|
const spinner = $el("div.matting-spinner");
|
||||||
button.appendChild(spinner);
|
button.appendChild(spinner);
|
||||||
button.classList.add('loading');
|
button.classList.add('loading');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (canvas.selectedLayers.length !== 1) throw new Error("Please select exactly one image layer for matting.");
|
if (canvas.selectedLayers.length !== 1) throw new Error("Please select exactly one image layer for matting.");
|
||||||
|
|
||||||
const selectedLayer = canvas.selectedLayers[0];
|
const selectedLayer = canvas.selectedLayers[0];
|
||||||
const imageData = await canvas.getLayerImageData(selectedLayer);
|
const imageData = await canvas.getLayerImageData(selectedLayer);
|
||||||
const response = await fetch("/matting", {
|
const response = await fetch("/matting", {
|
||||||
@@ -547,13 +562,13 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) throw new Error(`Server error: ${response.status} - ${response.statusText}`);
|
if (!response.ok) throw new Error(`Server error: ${response.status} - ${response.statusText}`);
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
const mattedImage = new Image();
|
const mattedImage = new Image();
|
||||||
mattedImage.src = result.matted_image;
|
mattedImage.src = result.matted_image;
|
||||||
await mattedImage.decode();
|
await mattedImage.decode();
|
||||||
|
|
||||||
const newLayer = { ...selectedLayer, image: mattedImage, zIndex: canvas.layers.length };
|
const newLayer = {...selectedLayer, image: mattedImage, zIndex: canvas.layers.length};
|
||||||
canvas.layers.push(newLayer);
|
canvas.layers.push(newLayer);
|
||||||
canvas.updateSelection([newLayer]);
|
canvas.updateSelection([newLayer]);
|
||||||
canvas.render();
|
canvas.render();
|
||||||
@@ -569,135 +584,155 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("button.painter-button", { id: `undo-button-${node.id}`, textContent: "Undo", disabled: true, onclick: () => canvas.undo() }),
|
$el("button.painter-button", {
|
||||||
$el("button.painter-button", { id: `redo-button-${node.id}`, textContent: "Redo", disabled: true, onclick: () => canvas.redo() }),
|
id: `undo-button-${node.id}`,
|
||||||
|
textContent: "Undo",
|
||||||
|
disabled: true,
|
||||||
|
onclick: () => canvas.undo()
|
||||||
|
}),
|
||||||
|
$el("button.painter-button", {
|
||||||
|
id: `redo-button-${node.id}`,
|
||||||
|
textContent: "Redo",
|
||||||
|
disabled: true,
|
||||||
|
onclick: () => canvas.redo()
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$el("div.painter-separator"),
|
$el("div.painter-separator"),
|
||||||
|
|
||||||
// --- Group: Tools & History ---
|
// --- Group: Tools & History ---
|
||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button.requires-selection.matting-button", {
|
$el("button.painter-button.requires-selection.matting-button", {
|
||||||
textContent: "Matting",
|
textContent: "Matting",
|
||||||
onclick: async (e) => {
|
onclick: async (e) => {
|
||||||
const button = e.target.closest('.matting-button');
|
const button = e.target.closest('.matting-button');
|
||||||
if (button.classList.contains('loading')) return;
|
if (button.classList.contains('loading')) return;
|
||||||
const spinner = $el("div.matting-spinner");
|
const spinner = $el("div.matting-spinner");
|
||||||
button.appendChild(spinner);
|
button.appendChild(spinner);
|
||||||
button.classList.add('loading');
|
button.classList.add('loading');
|
||||||
|
|
||||||
try {
|
|
||||||
if (canvas.selectedLayers.length !== 1) throw new Error("Please select exactly one image layer for matting.");
|
|
||||||
|
|
||||||
const selectedLayer = canvas.selectedLayers[0];
|
|
||||||
const imageData = await canvas.getLayerImageData(selectedLayer);
|
|
||||||
const response = await fetch("/matting", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {"Content-Type": "application/json"},
|
|
||||||
body: JSON.stringify({image: imageData})
|
|
||||||
});
|
|
||||||
if (!response.ok) throw new Error(`Server error: ${response.status} - ${response.statusText}`);
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
const mattedImage = new Image();
|
|
||||||
mattedImage.src = result.matted_image;
|
|
||||||
await mattedImage.decode();
|
|
||||||
const newLayer = { ...selectedLayer, image: mattedImage, zIndex: canvas.layers.length };
|
|
||||||
canvas.layers.push(newLayer);
|
|
||||||
canvas.updateSelection([newLayer]);
|
|
||||||
canvas.render();
|
|
||||||
canvas.saveState();
|
|
||||||
await canvas.saveToServer(widget.value);
|
|
||||||
app.graph.runStep();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Matting error:", error);
|
|
||||||
alert(`Error during matting process: ${error.message}`);
|
|
||||||
} finally {
|
|
||||||
button.classList.remove('loading');
|
|
||||||
button.removeChild(spinner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
$el("button.painter-button", { id: `undo-button-${node.id}`, textContent: "Undo", disabled: true, onclick: () => canvas.undo() }),
|
|
||||||
$el("button.painter-button", { id: `redo-button-${node.id}`, textContent: "Redo", disabled: true, onclick: () => canvas.redo() }),
|
|
||||||
]),
|
|
||||||
|
|
||||||
$el("div.painter-separator"),
|
|
||||||
|
|
||||||
// --- Group: Masking ---
|
try {
|
||||||
$el("div.painter-button-group", { id: "mask-controls" }, [
|
if (canvas.selectedLayers.length !== 1) throw new Error("Please select exactly one image layer for matting.");
|
||||||
$el("button.painter-button", {
|
|
||||||
id: "mask-mode-btn",
|
|
||||||
textContent: "Draw Mask",
|
|
||||||
onclick: () => {
|
|
||||||
const maskBtn = controlPanel.querySelector('#mask-mode-btn');
|
|
||||||
const maskControls = controlPanel.querySelector('#mask-controls');
|
|
||||||
|
|
||||||
if (canvas.maskTool.isActive) {
|
|
||||||
canvas.maskTool.deactivate();
|
|
||||||
maskBtn.classList.remove('primary');
|
|
||||||
maskControls.querySelectorAll('.mask-control').forEach(c => c.style.display = 'none');
|
|
||||||
} else {
|
|
||||||
canvas.maskTool.activate();
|
|
||||||
maskBtn.classList.add('primary');
|
|
||||||
maskControls.querySelectorAll('.mask-control').forEach(c => c.style.display = 'flex');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
$el("div.painter-slider-container.mask-control", { style: { display: 'none' } }, [
|
|
||||||
$el("label", { for: "brush-size-slider", textContent: "Size:" }),
|
|
||||||
$el("input", {
|
|
||||||
id: "brush-size-slider",
|
|
||||||
type: "range",
|
|
||||||
min: "1",
|
|
||||||
max: "200",
|
|
||||||
value: "20",
|
|
||||||
oninput: (e) => canvas.maskTool.setBrushSize(parseInt(e.target.value))
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
$el("div.painter-slider-container.mask-control", { style: { display: 'none' } }, [
|
|
||||||
$el("label", { for: "brush-strength-slider", textContent: "Strength:" }),
|
|
||||||
$el("input", {
|
|
||||||
id: "brush-strength-slider",
|
|
||||||
type: "range",
|
|
||||||
min: "0",
|
|
||||||
max: "1",
|
|
||||||
step: "0.05",
|
|
||||||
value: "0.5",
|
|
||||||
oninput: (e) => canvas.maskTool.setBrushStrength(parseFloat(e.target.value))
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
$el("div.painter-slider-container.mask-control", { style: { display: 'none' } }, [
|
|
||||||
$el("label", { for: "brush-softness-slider", textContent: "Softness:" }),
|
|
||||||
$el("input", {
|
|
||||||
id: "brush-softness-slider",
|
|
||||||
type: "range",
|
|
||||||
min: "0",
|
|
||||||
max: "1",
|
|
||||||
step: "0.05",
|
|
||||||
value: "0.5",
|
|
||||||
oninput: (e) => canvas.maskTool.setBrushSoftness(parseFloat(e.target.value))
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
$el("button.painter-button.mask-control", {
|
|
||||||
textContent: "Clear Mask",
|
|
||||||
style: { display: 'none' },
|
|
||||||
onclick: () => {
|
|
||||||
if (confirm("Are you sure you want to clear the mask?")) {
|
|
||||||
canvas.maskTool.clear();
|
|
||||||
canvas.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
|
|
||||||
$el("div.painter-separator"),
|
const selectedLayer = canvas.selectedLayers[0];
|
||||||
|
const imageData = await canvas.getLayerImageData(selectedLayer);
|
||||||
|
const response = await fetch("/matting", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: JSON.stringify({image: imageData})
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Server error: ${response.status} - ${response.statusText}`);
|
||||||
|
|
||||||
// --- Group: Cache ---
|
const result = await response.json();
|
||||||
|
const mattedImage = new Image();
|
||||||
|
mattedImage.src = result.matted_image;
|
||||||
|
await mattedImage.decode();
|
||||||
|
const newLayer = {...selectedLayer, image: mattedImage, zIndex: canvas.layers.length};
|
||||||
|
canvas.layers.push(newLayer);
|
||||||
|
canvas.updateSelection([newLayer]);
|
||||||
|
canvas.render();
|
||||||
|
canvas.saveState();
|
||||||
|
await canvas.saveToServer(widget.value);
|
||||||
|
app.graph.runStep();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Matting error:", error);
|
||||||
|
alert(`Error during matting process: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
button.classList.remove('loading');
|
||||||
|
button.removeChild(spinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("button.painter-button", {
|
||||||
|
id: `undo-button-${node.id}`,
|
||||||
|
textContent: "Undo",
|
||||||
|
disabled: true,
|
||||||
|
onclick: () => canvas.undo()
|
||||||
|
}),
|
||||||
|
$el("button.painter-button", {
|
||||||
|
id: `redo-button-${node.id}`,
|
||||||
|
textContent: "Redo",
|
||||||
|
disabled: true,
|
||||||
|
onclick: () => canvas.redo()
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
$el("div.painter-separator"),
|
||||||
|
|
||||||
|
// --- Group: Masking ---
|
||||||
|
$el("div.painter-button-group", {id: "mask-controls"}, [
|
||||||
|
$el("button.painter-button", {
|
||||||
|
id: "mask-mode-btn",
|
||||||
|
textContent: "Draw Mask",
|
||||||
|
onclick: () => {
|
||||||
|
const maskBtn = controlPanel.querySelector('#mask-mode-btn');
|
||||||
|
const maskControls = controlPanel.querySelector('#mask-controls');
|
||||||
|
|
||||||
|
if (canvas.maskTool.isActive) {
|
||||||
|
canvas.maskTool.deactivate();
|
||||||
|
maskBtn.classList.remove('primary');
|
||||||
|
maskControls.querySelectorAll('.mask-control').forEach(c => c.style.display = 'none');
|
||||||
|
} else {
|
||||||
|
canvas.maskTool.activate();
|
||||||
|
maskBtn.classList.add('primary');
|
||||||
|
maskControls.querySelectorAll('.mask-control').forEach(c => c.style.display = 'flex');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("div.painter-slider-container.mask-control", {style: {display: 'none'}}, [
|
||||||
|
$el("label", {for: "brush-size-slider", textContent: "Size:"}),
|
||||||
|
$el("input", {
|
||||||
|
id: "brush-size-slider",
|
||||||
|
type: "range",
|
||||||
|
min: "1",
|
||||||
|
max: "200",
|
||||||
|
value: "20",
|
||||||
|
oninput: (e) => canvas.maskTool.setBrushSize(parseInt(e.target.value))
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
$el("div.painter-slider-container.mask-control", {style: {display: 'none'}}, [
|
||||||
|
$el("label", {for: "brush-strength-slider", textContent: "Strength:"}),
|
||||||
|
$el("input", {
|
||||||
|
id: "brush-strength-slider",
|
||||||
|
type: "range",
|
||||||
|
min: "0",
|
||||||
|
max: "1",
|
||||||
|
step: "0.05",
|
||||||
|
value: "0.5",
|
||||||
|
oninput: (e) => canvas.maskTool.setBrushStrength(parseFloat(e.target.value))
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
$el("div.painter-slider-container.mask-control", {style: {display: 'none'}}, [
|
||||||
|
$el("label", {for: "brush-softness-slider", textContent: "Softness:"}),
|
||||||
|
$el("input", {
|
||||||
|
id: "brush-softness-slider",
|
||||||
|
type: "range",
|
||||||
|
min: "0",
|
||||||
|
max: "1",
|
||||||
|
step: "0.05",
|
||||||
|
value: "0.5",
|
||||||
|
oninput: (e) => canvas.maskTool.setBrushSoftness(parseFloat(e.target.value))
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
$el("button.painter-button.mask-control", {
|
||||||
|
textContent: "Clear Mask",
|
||||||
|
style: {display: 'none'},
|
||||||
|
onclick: () => {
|
||||||
|
if (confirm("Are you sure you want to clear the mask?")) {
|
||||||
|
canvas.maskTool.clear();
|
||||||
|
canvas.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
|
||||||
|
$el("div.painter-separator"),
|
||||||
|
|
||||||
|
// --- Group: Cache ---
|
||||||
$el("div.painter-button-group", {}, [
|
$el("div.painter-button-group", {}, [
|
||||||
$el("button.painter-button", {
|
$el("button.painter-button", {
|
||||||
textContent: "Clear Cache",
|
textContent: "Clear Cache",
|
||||||
style: { backgroundColor: "#c54747", borderColor: "#a53737" },
|
style: {backgroundColor: "#c54747", borderColor: "#a53737"},
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
if (confirm("Are you sure you want to clear all saved canvas states? This action cannot be undone.")) {
|
if (confirm("Are you sure you want to clear all saved canvas states? This action cannot be undone.")) {
|
||||||
try {
|
try {
|
||||||
@@ -712,7 +747,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
})
|
})
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
$el("div.painter-separator")
|
$el("div.painter-separator")
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -729,15 +764,15 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
canvas.onSelectionChange = updateButtonStates;
|
canvas.onSelectionChange = updateButtonStates;
|
||||||
|
|
||||||
const undoButton = controlPanel.querySelector(`#undo-button-${node.id}`);
|
const undoButton = controlPanel.querySelector(`#undo-button-${node.id}`);
|
||||||
const redoButton = controlPanel.querySelector(`#redo-button-${node.id}`);
|
const redoButton = controlPanel.querySelector(`#redo-button-${node.id}`);
|
||||||
|
|
||||||
canvas.onHistoryChange = ({ canUndo, canRedo }) => {
|
canvas.onHistoryChange = ({canUndo, canRedo}) => {
|
||||||
if(undoButton) undoButton.disabled = !canUndo;
|
if (undoButton) undoButton.disabled = !canUndo;
|
||||||
if(redoButton) redoButton.disabled = !canRedo;
|
if (redoButton) redoButton.disabled = !canRedo;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateButtonStates();
|
updateButtonStates();
|
||||||
canvas.updateHistoryButtons();
|
canvas.updateHistoryButtons();
|
||||||
|
|
||||||
@@ -764,7 +799,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
const origClick = button.onclick;
|
const origClick = button.onclick;
|
||||||
button.onclick = async (...args) => {
|
button.onclick = async (...args) => {
|
||||||
if (origClick) {
|
if (origClick) {
|
||||||
await origClick(...args);
|
await origClick(...args);
|
||||||
}
|
}
|
||||||
await updateOutput();
|
await updateOutput();
|
||||||
};
|
};
|
||||||
@@ -809,18 +844,18 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
}, [controlPanel, canvasContainer]);
|
}, [controlPanel, canvasContainer]);
|
||||||
const handleFileLoad = async (file) => {
|
const handleFileLoad = async (file) => {
|
||||||
console.log("File dropped:", file.name);
|
console.log("File dropped:", file.name);
|
||||||
if (!file.type.startsWith('image/')) {
|
if (!file.type.startsWith('image/')) {
|
||||||
console.log("Dropped file is not an image.");
|
console.log("Dropped file is not an image.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
console.log("FileReader finished loading dropped file as data:URL.");
|
console.log("FileReader finished loading dropped file as data:URL.");
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = async () => {
|
img.onload = async () => {
|
||||||
console.log("Image object loaded from dropped data:URL.");
|
console.log("Image object loaded from dropped data:URL.");
|
||||||
const scale = Math.min(
|
const scale = Math.min(
|
||||||
canvas.width / img.width,
|
canvas.width / img.width,
|
||||||
canvas.height / img.height
|
canvas.height / img.height
|
||||||
@@ -887,7 +922,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
const closeEditor = () => {
|
const closeEditor = () => {
|
||||||
originalParent.appendChild(mainContainer);
|
originalParent.appendChild(mainContainer);
|
||||||
document.body.removeChild(backdrop);
|
document.body.removeChild(backdrop);
|
||||||
|
|
||||||
isEditorOpen = false;
|
isEditorOpen = false;
|
||||||
openEditorBtn.textContent = "⛶";
|
openEditorBtn.textContent = "⛶";
|
||||||
openEditorBtn.title = "Open in Editor";
|
openEditorBtn.title = "Open in Editor";
|
||||||
@@ -912,11 +947,11 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
|
|
||||||
backdrop = $el("div.painter-modal-backdrop");
|
backdrop = $el("div.painter-modal-backdrop");
|
||||||
modalContent = $el("div.painter-modal-content");
|
modalContent = $el("div.painter-modal-content");
|
||||||
|
|
||||||
modalContent.appendChild(mainContainer);
|
modalContent.appendChild(mainContainer);
|
||||||
backdrop.appendChild(modalContent);
|
backdrop.appendChild(modalContent);
|
||||||
document.body.appendChild(backdrop);
|
document.body.appendChild(backdrop);
|
||||||
|
|
||||||
isEditorOpen = true;
|
isEditorOpen = true;
|
||||||
openEditorBtn.textContent = "X";
|
openEditorBtn.textContent = "X";
|
||||||
openEditorBtn.title = "Close Editor";
|
openEditorBtn.title = "Close Editor";
|
||||||
@@ -951,7 +986,7 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
canvas.loadInitialState();
|
canvas.loadInitialState();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canvas: canvas,
|
canvas: canvas,
|
||||||
panel: controlPanel
|
panel: controlPanel
|
||||||
@@ -1171,9 +1206,9 @@ app.registerExtension({
|
|||||||
// If modal is open when node is removed, ensure it's cleaned up
|
// If modal is open when node is removed, ensure it's cleaned up
|
||||||
const backdrop = document.querySelector('.painter-modal-backdrop');
|
const backdrop = document.querySelector('.painter-modal-backdrop');
|
||||||
if (backdrop && backdrop.contains(this.canvasWidget.canvas)) {
|
if (backdrop && backdrop.contains(this.canvasWidget.canvas)) {
|
||||||
document.body.removeChild(backdrop);
|
document.body.removeChild(backdrop);
|
||||||
}
|
}
|
||||||
|
|
||||||
return onRemoved?.apply(this, arguments);
|
return onRemoved?.apply(this, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ export class MaskTool {
|
|||||||
if (!this.lastPosition) {
|
if (!this.lastPosition) {
|
||||||
this.lastPosition = worldCoords;
|
this.lastPosition = worldCoords;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maskCtx.beginPath();
|
this.maskCtx.beginPath();
|
||||||
this.maskCtx.moveTo(this.lastPosition.x, this.lastPosition.y);
|
this.maskCtx.moveTo(this.lastPosition.x, this.lastPosition.y);
|
||||||
this.maskCtx.lineTo(worldCoords.x, worldCoords.y);
|
this.maskCtx.lineTo(worldCoords.x, worldCoords.y);
|
||||||
|
|
||||||
// Utwórz gradient radialny dla miękkości pędzla
|
// Utwórz gradient radialny dla miękkości pędzla
|
||||||
const gradientRadius = this.brushSize / 2;
|
const gradientRadius = this.brushSize / 2;
|
||||||
const softnessFactor = this.brushSoftness * gradientRadius;
|
const softnessFactor = this.brushSoftness * gradientRadius;
|
||||||
@@ -82,16 +82,16 @@ export class MaskTool {
|
|||||||
);
|
);
|
||||||
gradient.addColorStop(0, `rgba(255, 255, 255, ${this.brushStrength})`);
|
gradient.addColorStop(0, `rgba(255, 255, 255, ${this.brushStrength})`);
|
||||||
gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
|
gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
|
||||||
|
|
||||||
this.maskCtx.strokeStyle = gradient;
|
this.maskCtx.strokeStyle = gradient;
|
||||||
this.maskCtx.lineWidth = this.brushSize;
|
this.maskCtx.lineWidth = this.brushSize;
|
||||||
this.maskCtx.lineCap = 'round';
|
this.maskCtx.lineCap = 'round';
|
||||||
this.maskCtx.lineJoin = 'round';
|
this.maskCtx.lineJoin = 'round';
|
||||||
|
|
||||||
this.maskCtx.globalCompositeOperation = 'source-over';
|
this.maskCtx.globalCompositeOperation = 'source-over';
|
||||||
this.maskCtx.stroke();
|
this.maskCtx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
|
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ export class MaskTool {
|
|||||||
return maskImage;
|
return maskImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(width, height){
|
resize(width, height) {
|
||||||
const oldMask = this.maskCanvas;
|
const oldMask = this.maskCanvas;
|
||||||
this.maskCanvas = document.createElement('canvas');
|
this.maskCanvas = document.createElement('canvas');
|
||||||
this.maskCanvas.width = width;
|
this.maskCanvas.width = width;
|
||||||
|
|||||||
4
js/db.js
4
js/db.js
@@ -29,7 +29,7 @@ function openDB() {
|
|||||||
console.log("Upgrading IndexedDB...");
|
console.log("Upgrading IndexedDB...");
|
||||||
const db = event.target.result;
|
const db = event.target.result;
|
||||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||||
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
db.createObjectStore(STORE_NAME, {keyPath: 'id'});
|
||||||
console.log("Object store created:", STORE_NAME);
|
console.log("Object store created:", STORE_NAME);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -62,7 +62,7 @@ export async function setCanvasState(id, state) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
const store = transaction.objectStore(STORE_NAME);
|
||||||
const request = store.put({ id, state });
|
const request = store.put({id, state});
|
||||||
|
|
||||||
request.onerror = (event) => {
|
request.onerror = (event) => {
|
||||||
console.error("DB: Error setting canvas state:", event.target.error);
|
console.error("DB: Error setting canvas state:", event.target.error);
|
||||||
|
|||||||
Reference in New Issue
Block a user