mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-24 14:02:11 -03:00
project migration to typescript
Project migration to typescript
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
import {getCanvasState, setCanvasState, saveImage, getImage} from "./db.js";
|
||||
import {createModuleLogger} from "./utils/LoggerUtils.js";
|
||||
import {generateUUID, cloneLayers, getStateSignature, debounce} from "./utils/CommonUtils.js";
|
||||
import {withErrorHandling} from "./ErrorHandler.js";
|
||||
|
||||
import { getCanvasState, setCanvasState, saveImage, getImage } from "./db.js";
|
||||
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
||||
import { generateUUID, cloneLayers, getStateSignature, debounce } from "./utils/CommonUtils.js";
|
||||
const log = createModuleLogger('CanvasState');
|
||||
|
||||
export class CanvasState {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
@@ -16,289 +13,302 @@ export class CanvasState {
|
||||
this.saveTimeout = null;
|
||||
this.lastSavedStateSignature = null;
|
||||
this._loadInProgress = null;
|
||||
|
||||
// Inicjalizacja Web Workera w sposób odporny na problemy ze ścieżkami
|
||||
this._debouncedSave = null;
|
||||
try {
|
||||
// new URL(..., import.meta.url) tworzy absolutną ścieżkę do workera
|
||||
// @ts-ignore
|
||||
this.stateSaverWorker = new Worker(new URL('./state-saver.worker.js', import.meta.url), { type: 'module' });
|
||||
log.info("State saver worker initialized successfully.");
|
||||
|
||||
this.stateSaverWorker.onmessage = (e) => {
|
||||
log.info("Message from state saver worker:", e.data);
|
||||
};
|
||||
this.stateSaverWorker.onerror = (e) => {
|
||||
log.error("Error in state saver worker:", e.message, e.filename, e.lineno);
|
||||
// Zapobiegaj dalszym próbom, jeśli worker nie działa
|
||||
this.stateSaverWorker = null;
|
||||
this.stateSaverWorker = null;
|
||||
};
|
||||
} catch (e) {
|
||||
}
|
||||
catch (e) {
|
||||
log.error("Failed to initialize state saver worker:", e);
|
||||
this.stateSaverWorker = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async loadStateFromDB() {
|
||||
if (this._loadInProgress) {
|
||||
log.warn("Load already in progress, waiting...");
|
||||
return this._loadInProgress;
|
||||
}
|
||||
|
||||
log.info("Attempting to load state from IndexedDB for node:", this.canvas.node.id);
|
||||
if (!this.canvas.node.id) {
|
||||
log.error("Node ID is not available for loading state from DB.");
|
||||
return false;
|
||||
}
|
||||
|
||||
this._loadInProgress = this._performLoad();
|
||||
|
||||
const loadPromise = this._performLoad();
|
||||
this._loadInProgress = loadPromise;
|
||||
try {
|
||||
const result = await this._loadInProgress;
|
||||
return result;
|
||||
} finally {
|
||||
const result = await loadPromise;
|
||||
this._loadInProgress = null;
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
this._loadInProgress = null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
_performLoad = withErrorHandling(async () => {
|
||||
const savedState = await getCanvasState(this.canvas.node.id);
|
||||
if (!savedState) {
|
||||
log.info("No saved state found in IndexedDB for node:", this.canvas.node.id);
|
||||
async _performLoad() {
|
||||
try {
|
||||
if (!this.canvas.node.id) {
|
||||
log.error("Node ID is not available for loading state from DB.");
|
||||
return false;
|
||||
}
|
||||
const savedState = await getCanvasState(String(this.canvas.node.id));
|
||||
if (!savedState) {
|
||||
log.info("No saved state found in IndexedDB for node:", this.canvas.node.id);
|
||||
return false;
|
||||
}
|
||||
log.info("Found saved state in IndexedDB.");
|
||||
this.canvas.width = savedState.width || 512;
|
||||
this.canvas.height = savedState.height || 512;
|
||||
this.canvas.viewport = savedState.viewport || {
|
||||
x: -(this.canvas.width / 4),
|
||||
y: -(this.canvas.height / 4),
|
||||
zoom: 0.8
|
||||
};
|
||||
this.canvas.canvasLayers.updateOutputAreaSize(this.canvas.width, this.canvas.height, false);
|
||||
log.debug(`Output Area resized to ${this.canvas.width}x${this.canvas.height} and viewport set.`);
|
||||
const loadedLayers = await this._loadLayers(savedState.layers);
|
||||
this.canvas.layers = loadedLayers.filter((l) => l !== null);
|
||||
log.info(`Loaded ${this.canvas.layers.length} layers.`);
|
||||
if (this.canvas.layers.length === 0) {
|
||||
log.warn("No valid layers loaded, state may be corrupted.");
|
||||
return false;
|
||||
}
|
||||
this.canvas.updateSelectionAfterHistory();
|
||||
this.canvas.render();
|
||||
log.info("Canvas state loaded successfully from IndexedDB for node", this.canvas.node.id);
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
log.error("Error during state load:", error);
|
||||
return false;
|
||||
}
|
||||
log.info("Found saved state in IndexedDB.");
|
||||
this.canvas.width = savedState.width || 512;
|
||||
this.canvas.height = savedState.height || 512;
|
||||
this.canvas.viewport = savedState.viewport || {
|
||||
x: -(this.canvas.width / 4),
|
||||
y: -(this.canvas.height / 4),
|
||||
zoom: 0.8
|
||||
};
|
||||
|
||||
this.canvas.updateOutputAreaSize(this.canvas.width, this.canvas.height, false);
|
||||
log.debug(`Output Area resized to ${this.canvas.width}x${this.canvas.height} and viewport set.`);
|
||||
const loadedLayers = await this._loadLayers(savedState.layers);
|
||||
this.canvas.layers = loadedLayers.filter(l => l !== null);
|
||||
log.info(`Loaded ${this.canvas.layers.length} layers.`);
|
||||
|
||||
if (this.canvas.layers.length === 0) {
|
||||
log.warn("No valid layers loaded, state may be corrupted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.canvas.updateSelectionAfterHistory();
|
||||
this.canvas.render();
|
||||
log.info("Canvas state loaded successfully from IndexedDB for node", this.canvas.node.id);
|
||||
return true;
|
||||
}, 'CanvasState._performLoad');
|
||||
|
||||
}
|
||||
/**
|
||||
* Ładuje warstwy z zapisanego stanu
|
||||
* @param {Array} layersData - Dane warstw do załadowania
|
||||
* @returns {Promise<Array>} Załadowane warstwy
|
||||
* @param {any[]} layersData - Dane warstw do załadowania
|
||||
* @returns {Promise<(Layer | null)[]>} Załadowane warstwy
|
||||
*/
|
||||
async _loadLayers(layersData) {
|
||||
const imagePromises = layersData.map((layerData, index) =>
|
||||
this._loadSingleLayer(layerData, index)
|
||||
);
|
||||
const imagePromises = layersData.map((layerData, index) => this._loadSingleLayer(layerData, index));
|
||||
return Promise.all(imagePromises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ładuje pojedynczą warstwę
|
||||
* @param {Object} layerData - Dane warstwy
|
||||
* @param {any} layerData - Dane warstwy
|
||||
* @param {number} index - Indeks warstwy
|
||||
* @returns {Promise<Object|null>} Załadowana warstwa lub null
|
||||
* @returns {Promise<Layer | null>} Załadowana warstwa lub null
|
||||
*/
|
||||
async _loadSingleLayer(layerData, index) {
|
||||
return new Promise((resolve) => {
|
||||
if (layerData.imageId) {
|
||||
this._loadLayerFromImageId(layerData, index, resolve);
|
||||
} else if (layerData.imageSrc) {
|
||||
}
|
||||
else if (layerData.imageSrc) {
|
||||
this._convertLegacyLayer(layerData, index, resolve);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log.error(`Layer ${index}: No imageId or imageSrc found, skipping layer.`);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ładuje warstwę z imageId
|
||||
* @param {Object} layerData - Dane warstwy
|
||||
* @param {any} layerData - Dane warstwy
|
||||
* @param {number} index - Indeks warstwy
|
||||
* @param {Function} resolve - Funkcja resolve
|
||||
* @param {(value: Layer | null) => void} resolve - Funkcja resolve
|
||||
*/
|
||||
_loadLayerFromImageId(layerData, index, resolve) {
|
||||
log.debug(`Layer ${index}: Loading image with id: ${layerData.imageId}`);
|
||||
|
||||
if (this.canvas.imageCache.has(layerData.imageId)) {
|
||||
log.debug(`Layer ${index}: Image found in cache.`);
|
||||
const imageSrc = this.canvas.imageCache.get(layerData.imageId);
|
||||
this._createLayerFromSrc(layerData, imageSrc, index, resolve);
|
||||
} else {
|
||||
const imageData = this.canvas.imageCache.get(layerData.imageId);
|
||||
if (imageData) {
|
||||
const imageSrc = URL.createObjectURL(new Blob([imageData.data]));
|
||||
this._createLayerFromSrc(layerData, imageSrc, index, resolve);
|
||||
}
|
||||
else {
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
getImage(layerData.imageId)
|
||||
.then(imageSrc => {
|
||||
if (imageSrc) {
|
||||
log.debug(`Layer ${index}: Loading image from data:URL...`);
|
||||
this.canvas.imageCache.set(layerData.imageId, imageSrc);
|
||||
this._createLayerFromSrc(layerData, imageSrc, index, resolve);
|
||||
} else {
|
||||
log.error(`Layer ${index}: Image not found in IndexedDB.`);
|
||||
resolve(null);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(`Layer ${index}: Error loading image from IndexedDB:`, err);
|
||||
if (imageSrc) {
|
||||
log.debug(`Layer ${index}: Loading image from data:URL...`);
|
||||
this._createLayerFromSrc(layerData, imageSrc, index, resolve);
|
||||
}
|
||||
else {
|
||||
log.error(`Layer ${index}: Image not found in IndexedDB.`);
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(`Layer ${index}: Error loading image from IndexedDB:`, err);
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konwertuje starą warstwę z imageSrc na nowy format
|
||||
* @param {Object} layerData - Dane warstwy
|
||||
* @param {any} layerData - Dane warstwy
|
||||
* @param {number} index - Indeks warstwy
|
||||
* @param {Function} resolve - Funkcja resolve
|
||||
* @param {(value: Layer | null) => void} resolve - Funkcja resolve
|
||||
*/
|
||||
_convertLegacyLayer(layerData, index, resolve) {
|
||||
log.info(`Layer ${index}: Found imageSrc, converting to new format with imageId.`);
|
||||
const imageId = generateUUID();
|
||||
|
||||
saveImage(imageId, layerData.imageSrc)
|
||||
.then(() => {
|
||||
log.info(`Layer ${index}: Image saved to IndexedDB with id: ${imageId}`);
|
||||
this.canvas.imageCache.set(imageId, layerData.imageSrc);
|
||||
const newLayerData = {...layerData, imageId};
|
||||
delete newLayerData.imageSrc;
|
||||
this._createLayerFromSrc(newLayerData, layerData.imageSrc, index, resolve);
|
||||
})
|
||||
log.info(`Layer ${index}: Image saved to IndexedDB with id: ${imageId}`);
|
||||
const newLayerData = { ...layerData, imageId };
|
||||
delete newLayerData.imageSrc;
|
||||
this._createLayerFromSrc(newLayerData, layerData.imageSrc, index, resolve);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(`Layer ${index}: Error saving image to IndexedDB:`, err);
|
||||
resolve(null);
|
||||
});
|
||||
log.error(`Layer ${index}: Error saving image to IndexedDB:`, err);
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy warstwę z src obrazu
|
||||
* @param {Object} layerData - Dane warstwy
|
||||
* @param {any} layerData - Dane warstwy
|
||||
* @param {string} imageSrc - Źródło obrazu
|
||||
* @param {number} index - Indeks warstwy
|
||||
* @param {Function} resolve - Funkcja resolve
|
||||
* @param {(value: Layer | null) => void} resolve - Funkcja resolve
|
||||
*/
|
||||
_createLayerFromSrc(layerData, imageSrc, index, resolve) {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
log.debug(`Layer ${index}: Image loaded successfully.`);
|
||||
const newLayer = {...layerData, image: img};
|
||||
delete newLayer.imageId;
|
||||
resolve(newLayer);
|
||||
};
|
||||
img.onerror = () => {
|
||||
log.error(`Layer ${index}: Failed to load image from src.`);
|
||||
resolve(null);
|
||||
};
|
||||
img.src = imageSrc;
|
||||
if (typeof imageSrc === 'string') {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
log.debug(`Layer ${index}: Image loaded successfully.`);
|
||||
const newLayer = { ...layerData, image: img };
|
||||
resolve(newLayer);
|
||||
};
|
||||
img.onerror = () => {
|
||||
log.error(`Layer ${index}: Failed to load image from src.`);
|
||||
resolve(null);
|
||||
};
|
||||
img.src = imageSrc;
|
||||
}
|
||||
else {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = imageSrc.width;
|
||||
canvas.height = imageSrc.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(imageSrc, 0, 0);
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
log.debug(`Layer ${index}: Image loaded successfully from ImageBitmap.`);
|
||||
const newLayer = { ...layerData, image: img };
|
||||
resolve(newLayer);
|
||||
};
|
||||
img.onerror = () => {
|
||||
log.error(`Layer ${index}: Failed to load image from ImageBitmap.`);
|
||||
resolve(null);
|
||||
};
|
||||
img.src = canvas.toDataURL();
|
||||
}
|
||||
else {
|
||||
log.error(`Layer ${index}: Failed to get 2d context from canvas.`);
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async saveStateToDB() {
|
||||
if (!this.canvas.node.id) {
|
||||
log.error("Node ID is not available for saving state to DB.");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Preparing state to be sent to worker...");
|
||||
const layers = await this._prepareLayers();
|
||||
const state = {
|
||||
layers: await this._prepareLayers(),
|
||||
layers: layers.filter(layer => layer !== null),
|
||||
viewport: this.canvas.viewport,
|
||||
width: this.canvas.width,
|
||||
height: this.canvas.height,
|
||||
};
|
||||
|
||||
state.layers = state.layers.filter(layer => layer !== null);
|
||||
if (state.layers.length === 0) {
|
||||
log.warn("No valid layers to save, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.stateSaverWorker) {
|
||||
log.info("Posting state to worker for background saving.");
|
||||
this.stateSaverWorker.postMessage({
|
||||
nodeId: this.canvas.node.id,
|
||||
nodeId: String(this.canvas.node.id),
|
||||
state: state
|
||||
});
|
||||
this.canvas.render();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log.warn("State saver worker not available. Saving on main thread.");
|
||||
await setCanvasState(this.canvas.node.id, state);
|
||||
await setCanvasState(String(this.canvas.node.id), state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Przygotowuje warstwy do zapisu
|
||||
* @returns {Promise<Array>} Przygotowane warstwy
|
||||
* @returns {Promise<(Omit<Layer, 'image'> & { imageId: string })[]>} Przygotowane warstwy
|
||||
*/
|
||||
async _prepareLayers() {
|
||||
return Promise.all(this.canvas.layers.map(async (layer, index) => {
|
||||
const newLayer = {...layer};
|
||||
const preparedLayers = await Promise.all(this.canvas.layers.map(async (layer, index) => {
|
||||
const newLayer = { ...layer, imageId: layer.imageId || '' };
|
||||
delete newLayer.image;
|
||||
if (layer.image instanceof HTMLImageElement) {
|
||||
log.debug(`Layer ${index}: Using imageId instead of serializing image.`);
|
||||
if (!layer.imageId) {
|
||||
layer.imageId = generateUUID();
|
||||
await saveImage(layer.imageId, layer.image.src);
|
||||
this.canvas.imageCache.set(layer.imageId, layer.image.src);
|
||||
newLayer.imageId = generateUUID();
|
||||
const imageBitmap = await createImageBitmap(layer.image);
|
||||
await saveImage(newLayer.imageId, imageBitmap);
|
||||
}
|
||||
newLayer.imageId = layer.imageId;
|
||||
} else if (!layer.imageId) {
|
||||
}
|
||||
else if (!layer.imageId) {
|
||||
log.error(`Layer ${index}: No image or imageId found, skipping layer.`);
|
||||
return null;
|
||||
}
|
||||
delete newLayer.image;
|
||||
return newLayer;
|
||||
}));
|
||||
return preparedLayers.filter((layer) => layer !== null);
|
||||
}
|
||||
|
||||
saveState(replaceLast = false) {
|
||||
if (this.canvas.maskTool && this.canvas.maskTool.isActive) {
|
||||
this.saveMaskState(replaceLast);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.saveLayersState(replaceLast);
|
||||
}
|
||||
}
|
||||
|
||||
saveLayersState(replaceLast = false) {
|
||||
if (replaceLast && this.layersUndoStack.length > 0) {
|
||||
this.layersUndoStack.pop();
|
||||
}
|
||||
|
||||
const currentState = cloneLayers(this.canvas.layers);
|
||||
const currentStateSignature = getStateSignature(currentState);
|
||||
|
||||
if (this.layersUndoStack.length > 0) {
|
||||
const lastState = this.layersUndoStack[this.layersUndoStack.length - 1];
|
||||
if (getStateSignature(lastState) === currentStateSignature) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.layersUndoStack.push(currentState);
|
||||
|
||||
if (this.layersUndoStack.length > this.historyLimit) {
|
||||
this.layersUndoStack.shift();
|
||||
}
|
||||
this.layersRedoStack = [];
|
||||
this.canvas.updateHistoryButtons();
|
||||
|
||||
// Użyj debouncingu, aby zapobiec zbyt częstym zapisom
|
||||
if (!this._debouncedSave) {
|
||||
this._debouncedSave = debounce(() => this.saveStateToDB(), 1000);
|
||||
this._debouncedSave = debounce(this.saveStateToDB.bind(this), 1000);
|
||||
}
|
||||
this._debouncedSave();
|
||||
}
|
||||
|
||||
saveMaskState(replaceLast = false) {
|
||||
if (!this.canvas.maskTool) return;
|
||||
|
||||
if (!this.canvas.maskTool)
|
||||
return;
|
||||
if (replaceLast && this.maskUndoStack.length > 0) {
|
||||
this.maskUndoStack.pop();
|
||||
}
|
||||
@@ -307,89 +317,92 @@ export class CanvasState {
|
||||
clonedCanvas.width = maskCanvas.width;
|
||||
clonedCanvas.height = maskCanvas.height;
|
||||
const clonedCtx = clonedCanvas.getContext('2d', { willReadFrequently: true });
|
||||
clonedCtx.drawImage(maskCanvas, 0, 0);
|
||||
|
||||
if (clonedCtx) {
|
||||
clonedCtx.drawImage(maskCanvas, 0, 0);
|
||||
}
|
||||
this.maskUndoStack.push(clonedCanvas);
|
||||
|
||||
if (this.maskUndoStack.length > this.historyLimit) {
|
||||
this.maskUndoStack.shift();
|
||||
}
|
||||
this.maskRedoStack = [];
|
||||
this.canvas.updateHistoryButtons();
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.canvas.maskTool && this.canvas.maskTool.isActive) {
|
||||
this.undoMaskState();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.undoLayersState();
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this.canvas.maskTool && this.canvas.maskTool.isActive) {
|
||||
this.redoMaskState();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.redoLayersState();
|
||||
}
|
||||
}
|
||||
|
||||
undoLayersState() {
|
||||
if (this.layersUndoStack.length <= 1) return;
|
||||
|
||||
if (this.layersUndoStack.length <= 1)
|
||||
return;
|
||||
const currentState = this.layersUndoStack.pop();
|
||||
this.layersRedoStack.push(currentState);
|
||||
if (currentState) {
|
||||
this.layersRedoStack.push(currentState);
|
||||
}
|
||||
const prevState = this.layersUndoStack[this.layersUndoStack.length - 1];
|
||||
this.canvas.layers = cloneLayers(prevState);
|
||||
this.canvas.updateSelectionAfterHistory();
|
||||
this.canvas.render();
|
||||
this.canvas.updateHistoryButtons();
|
||||
}
|
||||
|
||||
redoLayersState() {
|
||||
if (this.layersRedoStack.length === 0) return;
|
||||
|
||||
if (this.layersRedoStack.length === 0)
|
||||
return;
|
||||
const nextState = this.layersRedoStack.pop();
|
||||
this.layersUndoStack.push(nextState);
|
||||
this.canvas.layers = cloneLayers(nextState);
|
||||
this.canvas.updateSelectionAfterHistory();
|
||||
this.canvas.render();
|
||||
this.canvas.updateHistoryButtons();
|
||||
if (nextState) {
|
||||
this.layersUndoStack.push(nextState);
|
||||
this.canvas.layers = cloneLayers(nextState);
|
||||
this.canvas.updateSelectionAfterHistory();
|
||||
this.canvas.render();
|
||||
this.canvas.updateHistoryButtons();
|
||||
}
|
||||
}
|
||||
|
||||
undoMaskState() {
|
||||
if (!this.canvas.maskTool || this.maskUndoStack.length <= 1) return;
|
||||
|
||||
if (!this.canvas.maskTool || this.maskUndoStack.length <= 1)
|
||||
return;
|
||||
const currentState = this.maskUndoStack.pop();
|
||||
this.maskRedoStack.push(currentState);
|
||||
|
||||
if (currentState) {
|
||||
this.maskRedoStack.push(currentState);
|
||||
}
|
||||
if (this.maskUndoStack.length > 0) {
|
||||
const prevState = this.maskUndoStack[this.maskUndoStack.length - 1];
|
||||
const maskCanvas = this.canvas.maskTool.getMask();
|
||||
const maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true });
|
||||
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
|
||||
maskCtx.drawImage(prevState, 0, 0);
|
||||
|
||||
if (maskCtx) {
|
||||
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
|
||||
maskCtx.drawImage(prevState, 0, 0);
|
||||
}
|
||||
this.canvas.render();
|
||||
}
|
||||
|
||||
this.canvas.updateHistoryButtons();
|
||||
}
|
||||
|
||||
redoMaskState() {
|
||||
if (!this.canvas.maskTool || this.maskRedoStack.length === 0) return;
|
||||
|
||||
if (!this.canvas.maskTool || this.maskRedoStack.length === 0)
|
||||
return;
|
||||
const nextState = this.maskRedoStack.pop();
|
||||
this.maskUndoStack.push(nextState);
|
||||
const maskCanvas = this.canvas.maskTool.getMask();
|
||||
const maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true });
|
||||
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
|
||||
maskCtx.drawImage(nextState, 0, 0);
|
||||
|
||||
this.canvas.render();
|
||||
if (nextState) {
|
||||
this.maskUndoStack.push(nextState);
|
||||
const maskCanvas = this.canvas.maskTool.getMask();
|
||||
const maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true });
|
||||
if (maskCtx) {
|
||||
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
|
||||
maskCtx.drawImage(nextState, 0, 0);
|
||||
}
|
||||
this.canvas.render();
|
||||
}
|
||||
this.canvas.updateHistoryButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyści historię undo/redo
|
||||
*/
|
||||
@@ -397,17 +410,17 @@ export class CanvasState {
|
||||
if (this.canvas.maskTool && this.canvas.maskTool.isActive) {
|
||||
this.maskUndoStack = [];
|
||||
this.maskRedoStack = [];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.layersUndoStack = [];
|
||||
this.layersRedoStack = [];
|
||||
}
|
||||
this.canvas.updateHistoryButtons();
|
||||
log.info("History cleared");
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca informacje o historii
|
||||
* @returns {Object} Informacje o historii
|
||||
* @returns {HistoryInfo} Informacje o historii
|
||||
*/
|
||||
getHistoryInfo() {
|
||||
if (this.canvas.maskTool && this.canvas.maskTool.isActive) {
|
||||
@@ -418,7 +431,8 @@ export class CanvasState {
|
||||
canRedo: this.maskRedoStack.length > 0,
|
||||
historyLimit: this.historyLimit
|
||||
};
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return {
|
||||
undoCount: this.layersUndoStack.length,
|
||||
redoCount: this.layersRedoStack.length,
|
||||
|
||||
Reference in New Issue
Block a user