mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-22 21:12:12 -03:00
Refactors CanvasState to maintain independent undo/redo stacks for mask editing and layer editing. Updates all relevant logic to use the correct history depending on the active mode, ensuring undo/redo and history buttons work as expected in both modes. MaskTool now saves history on activation, clear, and mouse up, and history info is reported per mode. Improves user experience when switching between mask and layer editing.
183 lines
6.0 KiB
JavaScript
183 lines
6.0 KiB
JavaScript
import {createModuleLogger} from "./LoggerUtils.js";
|
|
|
|
// Inicjalizacja loggera dla modułu Mask_tool
|
|
const log = createModuleLogger('Mask_tool');
|
|
|
|
export class MaskTool {
|
|
constructor(canvasInstance) {
|
|
this.canvasInstance = canvasInstance;
|
|
this.mainCanvas = canvasInstance.canvas;
|
|
this.maskCanvas = document.createElement('canvas');
|
|
this.maskCtx = this.maskCanvas.getContext('2d');
|
|
|
|
this.isActive = false;
|
|
this.brushSize = 20;
|
|
this.brushStrength = 0.5;
|
|
this.brushSoftness = 0.5; // Domyślna miękkość pędzla (0 - twardy, 1 - bardzo miękki)
|
|
this.isDrawing = false;
|
|
this.lastPosition = null;
|
|
|
|
this.initMaskCanvas();
|
|
}
|
|
|
|
setBrushSoftness(softness) {
|
|
this.brushSoftness = Math.max(0, Math.min(1, softness));
|
|
}
|
|
|
|
initMaskCanvas() {
|
|
this.maskCanvas.width = this.mainCanvas.width;
|
|
this.maskCanvas.height = this.mainCanvas.height;
|
|
// Wyczyść canvas bez zapisywania do historii
|
|
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
|
|
}
|
|
|
|
activate() {
|
|
this.isActive = true;
|
|
this.canvasInstance.interaction.mode = 'drawingMask';
|
|
|
|
// Zapisz początkowy stan maski tylko jeśli historia jest pusta
|
|
if (this.canvasInstance.canvasState && this.canvasInstance.canvasState.maskUndoStack.length === 0) {
|
|
this.canvasInstance.canvasState.saveMaskState();
|
|
}
|
|
|
|
// Aktualizuj przyciski historii po przełączeniu na tryb maski
|
|
this.canvasInstance.updateHistoryButtons();
|
|
|
|
log.info("Mask tool activated");
|
|
}
|
|
|
|
deactivate() {
|
|
this.isActive = false;
|
|
this.canvasInstance.interaction.mode = 'none';
|
|
|
|
// Aktualizuj przyciski historii po przełączeniu z trybu maski
|
|
this.canvasInstance.updateHistoryButtons();
|
|
|
|
log.info("Mask tool deactivated");
|
|
}
|
|
|
|
setBrushSize(size) {
|
|
this.brushSize = Math.max(1, size);
|
|
}
|
|
|
|
setBrushStrength(strength) {
|
|
this.brushStrength = Math.max(0, Math.min(1, strength));
|
|
}
|
|
|
|
handleMouseDown(worldCoords) {
|
|
if (!this.isActive) return;
|
|
this.isDrawing = true;
|
|
this.lastPosition = worldCoords;
|
|
this.draw(worldCoords);
|
|
}
|
|
|
|
handleMouseMove(worldCoords) {
|
|
if (!this.isActive || !this.isDrawing) return;
|
|
this.draw(worldCoords);
|
|
this.lastPosition = worldCoords;
|
|
}
|
|
|
|
handleMouseUp() {
|
|
if (!this.isActive) return;
|
|
|
|
// Jeśli narzędzie rysowało, zapisz stan maski
|
|
if (this.isDrawing) {
|
|
// Zakończ rysowanie
|
|
this.isDrawing = false;
|
|
this.lastPosition = null;
|
|
|
|
// Zapisz stan maski do historii
|
|
if (this.canvasInstance.canvasState) {
|
|
this.canvasInstance.canvasState.saveMaskState();
|
|
}
|
|
}
|
|
}
|
|
|
|
draw(worldCoords) {
|
|
if (!this.lastPosition) {
|
|
this.lastPosition = worldCoords;
|
|
}
|
|
|
|
this.maskCtx.beginPath();
|
|
this.maskCtx.moveTo(this.lastPosition.x, this.lastPosition.y);
|
|
this.maskCtx.lineTo(worldCoords.x, worldCoords.y);
|
|
|
|
// Utwórz gradient radialny dla miękkości pędzla
|
|
const gradientRadius = this.brushSize / 2;
|
|
const softnessFactor = this.brushSoftness * gradientRadius;
|
|
const gradient = this.maskCtx.createRadialGradient(
|
|
worldCoords.x, worldCoords.y, gradientRadius - softnessFactor,
|
|
worldCoords.x, worldCoords.y, gradientRadius
|
|
);
|
|
gradient.addColorStop(0, `rgba(255, 255, 255, ${this.brushStrength})`);
|
|
gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
|
|
|
|
this.maskCtx.strokeStyle = gradient;
|
|
this.maskCtx.lineWidth = this.brushSize;
|
|
this.maskCtx.lineCap = 'round';
|
|
this.maskCtx.lineJoin = 'round';
|
|
|
|
this.maskCtx.globalCompositeOperation = 'source-over';
|
|
this.maskCtx.stroke();
|
|
}
|
|
|
|
clear() {
|
|
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
|
|
|
|
// Zapisz stan po wyczyszczeniu maski tylko jeśli narzędzie jest aktywne
|
|
if (this.isActive && this.canvasInstance.canvasState) {
|
|
this.canvasInstance.canvasState.saveMaskState();
|
|
}
|
|
}
|
|
|
|
getMask() {
|
|
return this.maskCanvas;
|
|
}
|
|
|
|
getMaskImageWithAlpha() {
|
|
const tempCanvas = document.createElement('canvas');
|
|
tempCanvas.width = this.maskCanvas.width;
|
|
tempCanvas.height = this.maskCanvas.height;
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
|
|
// Kopiuj maskę na tymczasowy canvas
|
|
tempCtx.drawImage(this.maskCanvas, 0, 0);
|
|
|
|
// Pobierz dane pikseli
|
|
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
|
|
const data = imageData.data;
|
|
|
|
// Modyfikuj kanał alfa, aby zachować zróżnicowaną przezroczystość
|
|
for (let i = 0; i < data.length; i += 4) {
|
|
const alpha = data[i]; // Wartość alfa (0-255)
|
|
data[i] = 255; // Czerwony
|
|
data[i + 1] = 255; // Zielony
|
|
data[i + 2] = 255; // Niebieski
|
|
data[i + 3] = alpha; // Alfa (zachowaj oryginalną wartość)
|
|
}
|
|
|
|
// Zapisz zmodyfikowane dane pikseli
|
|
tempCtx.putImageData(imageData, 0, 0);
|
|
|
|
// Utwórz obraz z tymczasowego canvasu
|
|
const maskImage = new Image();
|
|
maskImage.src = tempCanvas.toDataURL();
|
|
return maskImage;
|
|
}
|
|
|
|
resize(width, height) {
|
|
const oldMask = this.maskCanvas;
|
|
this.maskCanvas = document.createElement('canvas');
|
|
this.maskCanvas.width = width;
|
|
this.maskCanvas.height = height;
|
|
this.maskCtx = this.maskCanvas.getContext('2d');
|
|
|
|
// Zachowaj zawartość starej maski
|
|
if (oldMask.width > 0 && oldMask.height > 0) {
|
|
this.maskCtx.drawImage(oldMask, 0, 0);
|
|
}
|
|
|
|
log.info(`Mask canvas resized to ${width}x${height}`);
|
|
}
|
|
}
|