Files
Comfyui-LayerForge/js/MaskTool.js
Dariusz L a0ceb3b97c Remove auto mask loading docs and clean up code comments
Deleted documentation files related to automatic mask loading. Cleaned up and streamlined comments across Canvas.js, CanvasInteractions.js, CanvasLayers.js, CanvasView.js, MaskTool.js, mask_utils.js, and example scripts for improved clarity and maintainability. No functional changes to core logic.
2025-07-01 06:42:20 +02:00

303 lines
10 KiB
JavaScript

import {createModuleLogger} from "./utils/LoggerUtils.js";
const log = createModuleLogger('Mask_tool');
export class MaskTool {
constructor(canvasInstance, callbacks = {}) {
this.canvasInstance = canvasInstance;
this.mainCanvas = canvasInstance.canvas;
this.onStateChange = callbacks.onStateChange || null;
this.maskCanvas = document.createElement('canvas');
this.maskCtx = this.maskCanvas.getContext('2d');
this.x = 0;
this.y = 0;
this.isActive = false;
this.brushSize = 20;
this.brushStrength = 0.5;
this.brushHardness = 0.5;
this.isDrawing = false;
this.lastPosition = null;
this.previewCanvas = document.createElement('canvas');
this.previewCtx = this.previewCanvas.getContext('2d');
this.previewVisible = false;
this.previewCanvasInitialized = false;
this.initMaskCanvas();
}
initPreviewCanvas() {
if (this.previewCanvas.parentElement) {
this.previewCanvas.parentElement.removeChild(this.previewCanvas);
}
this.previewCanvas.width = this.canvasInstance.canvas.width;
this.previewCanvas.height = this.canvasInstance.canvas.height;
this.previewCanvas.style.position = 'absolute';
this.previewCanvas.style.left = `${this.canvasInstance.canvas.offsetLeft}px`;
this.previewCanvas.style.top = `${this.canvasInstance.canvas.offsetTop}px`;
this.previewCanvas.style.pointerEvents = 'none';
this.previewCanvas.style.zIndex = '10';
this.canvasInstance.canvas.parentElement.appendChild(this.previewCanvas);
}
setBrushHardness(hardness) {
this.brushHardness = Math.max(0, Math.min(1, hardness));
}
initMaskCanvas() {
const extraSpace = 2000; // Allow for a generous drawing area outside the output area
this.maskCanvas.width = this.canvasInstance.width + extraSpace;
this.maskCanvas.height = this.canvasInstance.height + extraSpace;
this.x = -extraSpace / 2;
this.y = -extraSpace / 2;
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
log.info(`Initialized mask canvas with extended size: ${this.maskCanvas.width}x${this.maskCanvas.height}, origin at (${this.x}, ${this.y})`);
}
activate() {
if (!this.previewCanvasInitialized) {
this.initPreviewCanvas();
this.previewCanvasInitialized = true;
}
this.isActive = true;
this.previewCanvas.style.display = 'block';
this.canvasInstance.interaction.mode = 'drawingMask';
if (this.canvasInstance.canvasState && this.canvasInstance.canvasState.maskUndoStack.length === 0) {
this.canvasInstance.canvasState.saveMaskState();
}
this.canvasInstance.updateHistoryButtons();
log.info("Mask tool activated");
}
deactivate() {
this.isActive = false;
this.previewCanvas.style.display = 'none';
this.canvasInstance.interaction.mode = 'none';
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, viewCoords) {
if (!this.isActive) return;
this.isDrawing = true;
this.lastPosition = worldCoords;
this.draw(worldCoords);
this.clearPreview();
}
handleMouseMove(worldCoords, viewCoords) {
if (this.isActive) {
this.drawBrushPreview(viewCoords);
}
if (!this.isActive || !this.isDrawing) return;
this.draw(worldCoords);
this.lastPosition = worldCoords;
}
handleMouseLeave() {
this.previewVisible = false;
this.clearPreview();
}
handleMouseEnter() {
this.previewVisible = true;
}
handleMouseUp(viewCoords) {
if (!this.isActive) return;
if (this.isDrawing) {
this.isDrawing = false;
this.lastPosition = null;
if (this.canvasInstance.canvasState) {
this.canvasInstance.canvasState.saveMaskState();
}
if (this.onStateChange) {
this.onStateChange();
}
this.drawBrushPreview(viewCoords);
}
}
draw(worldCoords) {
if (!this.lastPosition) {
this.lastPosition = worldCoords;
}
const canvasLastX = this.lastPosition.x - this.x;
const canvasLastY = this.lastPosition.y - this.y;
const canvasX = worldCoords.x - this.x;
const canvasY = worldCoords.y - this.y;
const canvasWidth = this.maskCanvas.width;
const canvasHeight = this.maskCanvas.height;
if (canvasX >= 0 && canvasX < canvasWidth &&
canvasY >= 0 && canvasY < canvasHeight &&
canvasLastX >= 0 && canvasLastX < canvasWidth &&
canvasLastY >= 0 && canvasLastY < canvasHeight) {
this.maskCtx.beginPath();
this.maskCtx.moveTo(canvasLastX, canvasLastY);
this.maskCtx.lineTo(canvasX, canvasY);
const gradientRadius = this.brushSize / 2;
if (this.brushHardness === 1) {
this.maskCtx.strokeStyle = `rgba(255, 255, 255, ${this.brushStrength})`;
} else {
const innerRadius = gradientRadius * this.brushHardness;
const gradient = this.maskCtx.createRadialGradient(
canvasX, canvasY, innerRadius,
canvasX, canvasY, 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();
} else {
log.debug(`Drawing outside mask canvas bounds: (${canvasX}, ${canvasY})`);
}
}
drawBrushPreview(viewCoords) {
if (!this.previewVisible || this.isDrawing) {
this.clearPreview();
return;
}
this.clearPreview();
const zoom = this.canvasInstance.viewport.zoom;
const radius = (this.brushSize / 2) * zoom;
this.previewCtx.beginPath();
this.previewCtx.arc(viewCoords.x, viewCoords.y, radius, 0, 2 * Math.PI);
this.previewCtx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
this.previewCtx.lineWidth = 1;
this.previewCtx.setLineDash([2, 4]);
this.previewCtx.stroke();
}
clearPreview() {
this.previewCtx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);
}
clear() {
this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
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');
tempCtx.drawImage(this.maskCanvas, 0, 0);
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const alpha = data[i];
data[i] = 255;
data[i + 1] = 255;
data[i + 2] = 255;
data[i + 3] = alpha;
}
tempCtx.putImageData(imageData, 0, 0);
const maskImage = new Image();
maskImage.src = tempCanvas.toDataURL();
return maskImage;
}
resize(width, height) {
this.initPreviewCanvas();
const oldMask = this.maskCanvas;
const oldX = this.x;
const oldY = this.y;
const oldWidth = oldMask.width;
const oldHeight = oldMask.height;
const isIncreasingWidth = width > (this.canvasInstance.width);
const isIncreasingHeight = height > (this.canvasInstance.height);
this.maskCanvas = document.createElement('canvas');
const extraSpace = 2000;
const newWidth = isIncreasingWidth ? width + extraSpace : Math.max(oldWidth, width + extraSpace);
const newHeight = isIncreasingHeight ? height + extraSpace : Math.max(oldHeight, height + extraSpace);
this.maskCanvas.width = newWidth;
this.maskCanvas.height = newHeight;
this.maskCtx = this.maskCanvas.getContext('2d');
if (oldMask.width > 0 && oldMask.height > 0) {
const offsetX = this.x - oldX;
const offsetY = this.y - oldY;
this.maskCtx.drawImage(oldMask, offsetX, offsetY);
log.debug(`Preserved mask content with offset (${offsetX}, ${offsetY})`);
}
log.info(`Mask canvas resized to ${this.maskCanvas.width}x${this.maskCanvas.height}, position (${this.x}, ${this.y})`);
log.info(`Canvas size change: width ${isIncreasingWidth ? 'increased' : 'decreased'}, height ${isIncreasingHeight ? 'increased' : 'decreased'}`);
}
updatePosition(dx, dy) {
this.x += dx;
this.y += dy;
log.info(`Mask position updated to (${this.x}, ${this.y})`);
}
setMask(image) {
const destX = -this.x;
const destY = -this.y;
this.maskCtx.clearRect(destX, destY, this.canvasInstance.width, this.canvasInstance.height);
this.maskCtx.drawImage(image, destX, destY);
if (this.onStateChange) {
this.onStateChange();
}
this.canvasInstance.render(); // Wymuś odświeżenie, aby zobaczyć zmianę
log.info(`MaskTool updated with a new mask image at correct canvas position (${destX}, ${destY}).`);
}
}