Refactor mask editor cropping and formatting in Canvas.js

Updated the mask processing logic to crop the mask based on the viewport pan position instead of scaling, ensuring accurate mask editing. Also standardized object formatting in logging and context creation for consistency.
This commit is contained in:
Dariusz L
2025-07-02 00:11:45 +02:00
parent faa60d4c28
commit 4966069b67

View File

@@ -26,7 +26,7 @@ export class Canvas {
this.node = node; this.node = node;
this.widget = widget; this.widget = widget;
this.canvas = document.createElement('canvas'); this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d', { willReadFrequently: true }); this.ctx = this.canvas.getContext('2d', {willReadFrequently: true});
this.width = 512; this.width = 512;
this.height = 512; this.height = 512;
this.layers = []; this.layers = [];
@@ -60,7 +60,7 @@ export class Canvas {
log.debug('Canvas widget element:', this.node); log.debug('Canvas widget element:', this.node);
log.info('Canvas initialized', { log.info('Canvas initialized', {
nodeId: this.node.id, nodeId: this.node.id,
dimensions: { width: this.width, height: this.height }, dimensions: {width: this.width, height: this.height},
viewport: this.viewport viewport: this.viewport
}); });
@@ -187,7 +187,7 @@ export class Canvas {
* @param {boolean} replaceLast - Czy zastąpić ostatni stan w historii * @param {boolean} replaceLast - Czy zastąpić ostatni stan w historii
*/ */
saveState(replaceLast = false) { saveState(replaceLast = false) {
log.debug('Saving canvas state', { replaceLast, layersCount: this.layers.length }); log.debug('Saving canvas state', {replaceLast, layersCount: this.layers.length});
this.canvasState.saveState(replaceLast); this.canvasState.saveState(replaceLast);
this.incrementOperationCount(); this.incrementOperationCount();
this._notifyStateChange(); this._notifyStateChange();
@@ -683,7 +683,7 @@ export class Canvas {
throw new Error("Old mask editor canvas not found"); throw new Error("Old mask editor canvas not found");
} }
const maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true }); const maskCtx = maskCanvas.getContext('2d', {willReadFrequently: true});
const maskColor = {r: 255, g: 255, b: 255}; const maskColor = {r: 255, g: 255, b: 255};
const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor); const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor);
@@ -699,59 +699,58 @@ export class Canvas {
* @param {number} targetHeight - Docelowa wysokość * @param {number} targetHeight - Docelowa wysokość
* @param {Object} maskColor - Kolor maski {r, g, b} * @param {Object} maskColor - Kolor maski {r, g, b}
* @returns {HTMLCanvasElement} Przetworzona maska * @returns {HTMLCanvasElement} Przetworzona maska
*/ */async processMaskForEditor(maskData, targetWidth, targetHeight, maskColor) {
async processMaskForEditor(maskData, targetWidth, targetHeight, maskColor) { // Współrzędne przesunięcia (pan) widoku edytora
const originalWidth = maskData.width || maskData.naturalWidth || this.width; const panX = this.maskTool.x;
const originalHeight = maskData.height || maskData.naturalHeight || this.height; const panY = this.maskTool.y;
log.info("Processing mask for editor:", { log.info("Processing mask for editor:", {
originalSize: {width: originalWidth, height: originalHeight}, sourceSize: {width: maskData.width, height: maskData.height},
targetSize: {width: targetWidth, height: targetHeight}, targetSize: {width: targetWidth, height: targetHeight},
canvasSize: {width: this.width, height: this.height} viewportPan: {x: panX, y: panY}
}); });
const tempCanvas = document.createElement('canvas'); const tempCanvas = document.createElement('canvas');
tempCanvas.width = targetWidth; tempCanvas.width = targetWidth;
tempCanvas.height = targetHeight; tempCanvas.height = targetHeight;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); const tempCtx = tempCanvas.getContext('2d', {willReadFrequently: true});
tempCtx.clearRect(0, 0, targetWidth, targetHeight); const sourceX = -panX;
const sourceY = -panY;
tempCtx.drawImage(
maskData, // Źródło: pełna maska z "output area"
sourceX, // sx: Prawdziwa współrzędna X na dużej masce (np. 1000)
sourceY, // sy: Prawdziwa współrzędna Y na dużej masce (np. 1000)
targetWidth, // sWidth: Szerokość wycinanego fragmentu
targetHeight, // sHeight: Wysokość wycinanego fragmentu
0, // dx: Gdzie wkleić w płótnie docelowym (zawsze 0)
0, // dy: Gdzie wkleić w płótnie docelowym (zawsze 0)
targetWidth, // dWidth: Szerokość wklejanego obrazu
targetHeight // dHeight: Wysokość wklejanego obrazu
);
const scaleToOriginal = Math.min(originalWidth / this.width, originalHeight / this.height); log.info("Mask viewport cropped correctly.", {
source: "maskData",
const scaledWidth = this.width * scaleToOriginal; cropArea: {x: sourceX, y: sourceY, width: targetWidth, height: targetHeight}
const scaledHeight = this.height * scaleToOriginal;
const offsetX = (targetWidth - scaledWidth) / 2;
const offsetY = (targetHeight - scaledHeight) / 2;
tempCtx.drawImage(maskData, offsetX, offsetY, scaledWidth, scaledHeight);
log.info("Mask drawn scaled to original image size:", {
originalSize: {width: originalWidth, height: originalHeight},
targetSize: {width: targetWidth, height: targetHeight},
canvasSize: {width: this.width, height: this.height},
scaleToOriginal: scaleToOriginal,
finalSize: {width: scaledWidth, height: scaledHeight},
offset: {x: offsetX, y: offsetY}
}); });
// Reszta kodu (zmiana koloru) pozostaje bez zmian
const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight); const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
const data = imageData.data; const data = imageData.data;
for (let i = 0; i < data.length; i += 4) { for (let i = 0; i < data.length; i += 4) {
const alpha = data[i + 3]; // Oryginalny kanał alpha const alpha = data[i + 3];
if (alpha > 0) {
data[i] = maskColor.r; // R data[i] = maskColor.r;
data[i + 1] = maskColor.g; // G data[i + 1] = maskColor.g;
data[i + 2] = maskColor.b; // B data[i + 2] = maskColor.b;
data[i + 3] = alpha; // Zachowaj oryginalny alpha }
} }
tempCtx.putImageData(imageData, 0, 0); tempCtx.putImageData(imageData, 0, 0);
log.info("Mask processing completed - full size scaling applied"); log.info("Mask processing completed - color applied.");
return tempCanvas; return tempCanvas;
} }
@@ -784,6 +783,7 @@ export class Canvas {
setTimeout(this.waitWhileMaskEditing.bind(this), 100); setTimeout(this.waitWhileMaskEditing.bind(this), 100);
} }
} }
/** /**
* Zapisuje obecny stan maski przed otwarciem editora * Zapisuje obecny stan maski przed otwarciem editora
* @returns {Object} Zapisany stan maski * @returns {Object} Zapisany stan maski
@@ -797,7 +797,7 @@ export class Canvas {
const savedCanvas = document.createElement('canvas'); const savedCanvas = document.createElement('canvas');
savedCanvas.width = maskCanvas.width; savedCanvas.width = maskCanvas.width;
savedCanvas.height = maskCanvas.height; savedCanvas.height = maskCanvas.height;
const savedCtx = savedCanvas.getContext('2d', { willReadFrequently: true }); const savedCtx = savedCanvas.getContext('2d', {willReadFrequently: true});
savedCtx.drawImage(maskCanvas, 0, 0); savedCtx.drawImage(maskCanvas, 0, 0);
return { return {
@@ -893,7 +893,7 @@ export class Canvas {
const tempCanvas = document.createElement('canvas'); const tempCanvas = document.createElement('canvas');
tempCanvas.width = this.width; tempCanvas.width = this.width;
tempCanvas.height = this.height; tempCanvas.height = this.height;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); const tempCtx = tempCanvas.getContext('2d', {willReadFrequently: true});
tempCtx.drawImage(resultImage, 0, 0, this.width, this.height); tempCtx.drawImage(resultImage, 0, 0, this.width, this.height);
@@ -920,7 +920,7 @@ export class Canvas {
const destX = -this.maskTool.x; const destX = -this.maskTool.x;
const destY = -this.maskTool.y; const destY = -this.maskTool.y;
log.debug("Applying mask to canvas", { destX, destY }); log.debug("Applying mask to canvas", {destX, destY});
maskCtx.globalCompositeOperation = 'source-over'; maskCtx.globalCompositeOperation = 'source-over';
maskCtx.clearRect(destX, destY, this.width, this.height); maskCtx.clearRect(destX, destY, this.width, this.height);