mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
project migration to typescript
Project migration to typescript
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import {createModuleLogger} from "./utils/LoggerUtils.js";
|
||||
|
||||
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
||||
const log = createModuleLogger('CanvasRenderer');
|
||||
|
||||
export class CanvasRenderer {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
@@ -10,7 +8,6 @@ export class CanvasRenderer {
|
||||
this.renderInterval = 1000 / 60;
|
||||
this.isDirty = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.renderAnimationFrame) {
|
||||
this.isDirty = true;
|
||||
@@ -23,16 +20,15 @@ export class CanvasRenderer {
|
||||
this.actualRender();
|
||||
this.isDirty = false;
|
||||
}
|
||||
|
||||
if (this.isDirty) {
|
||||
this.renderAnimationFrame = null;
|
||||
this.render();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.renderAnimationFrame = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
actualRender() {
|
||||
if (this.canvas.offscreenCanvas.width !== this.canvas.canvas.clientWidth ||
|
||||
this.canvas.offscreenCanvas.height !== this.canvas.canvas.clientHeight) {
|
||||
@@ -41,21 +37,17 @@ export class CanvasRenderer {
|
||||
this.canvas.offscreenCanvas.width = newWidth;
|
||||
this.canvas.offscreenCanvas.height = newHeight;
|
||||
}
|
||||
|
||||
const ctx = this.canvas.offscreenCtx;
|
||||
|
||||
ctx.fillStyle = '#606060';
|
||||
ctx.fillRect(0, 0, this.canvas.offscreenCanvas.width, this.canvas.offscreenCanvas.height);
|
||||
|
||||
ctx.save();
|
||||
ctx.scale(this.canvas.viewport.zoom, this.canvas.viewport.zoom);
|
||||
ctx.translate(-this.canvas.viewport.x, -this.canvas.viewport.y);
|
||||
|
||||
this.drawGrid(ctx);
|
||||
|
||||
const sortedLayers = [...this.canvas.layers].sort((a, b) => a.zIndex - b.zIndex);
|
||||
sortedLayers.forEach(layer => {
|
||||
if (!layer.image) return;
|
||||
if (!layer.image)
|
||||
return;
|
||||
ctx.save();
|
||||
const currentTransform = ctx.getTransform();
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
@@ -68,11 +60,7 @@ export class CanvasRenderer {
|
||||
ctx.rotate(layer.rotation * Math.PI / 180);
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
ctx.drawImage(
|
||||
layer.image, -layer.width / 2, -layer.height / 2,
|
||||
layer.width,
|
||||
layer.height
|
||||
);
|
||||
ctx.drawImage(layer.image, -layer.width / 2, -layer.height / 2, layer.width, layer.height);
|
||||
if (layer.mask) {
|
||||
}
|
||||
if (this.canvas.canvasSelection.selectedLayers.includes(layer)) {
|
||||
@@ -80,51 +68,41 @@ export class CanvasRenderer {
|
||||
}
|
||||
ctx.restore();
|
||||
});
|
||||
|
||||
this.drawCanvasOutline(ctx);
|
||||
this.drawPendingGenerationAreas(ctx); // Draw snapshot outlines
|
||||
const maskImage = this.canvas.maskTool.getMask();
|
||||
if (maskImage && this.canvas.maskTool.isOverlayVisible) {
|
||||
|
||||
ctx.save();
|
||||
|
||||
if (this.canvas.maskTool.isActive) {
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
ctx.globalAlpha = 0.5;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
ctx.globalAlpha = 1.0;
|
||||
}
|
||||
|
||||
ctx.drawImage(maskImage, this.canvas.maskTool.x, this.canvas.maskTool.y);
|
||||
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
this.renderInteractionElements(ctx);
|
||||
this.renderLayerInfo(ctx);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
if (this.canvas.canvas.width !== this.canvas.offscreenCanvas.width ||
|
||||
this.canvas.canvas.height !== this.canvas.offscreenCanvas.height) {
|
||||
this.canvas.canvas.width = this.canvas.offscreenCanvas.width;
|
||||
this.canvas.canvas.height = this.canvas.offscreenCanvas.height;
|
||||
}
|
||||
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0);
|
||||
|
||||
// Update Batch Preview UI positions
|
||||
if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) {
|
||||
this.canvas.batchPreviewManagers.forEach(manager => {
|
||||
this.canvas.batchPreviewManagers.forEach((manager) => {
|
||||
manager.updateScreenPosition(this.canvas.viewport);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderInteractionElements(ctx) {
|
||||
const interaction = this.canvas.interaction;
|
||||
|
||||
if (interaction.mode === 'resizingCanvas' && interaction.canvasResizeRect) {
|
||||
const rect = interaction.canvasResizeRect;
|
||||
ctx.save();
|
||||
@@ -138,7 +116,6 @@ export class CanvasRenderer {
|
||||
const text = `${Math.round(rect.width)}x${Math.round(rect.height)}`;
|
||||
const textWorldX = rect.x + rect.width / 2;
|
||||
const textWorldY = rect.y + rect.height + (20 / this.canvas.viewport.zoom);
|
||||
|
||||
ctx.save();
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
||||
@@ -156,7 +133,6 @@ export class CanvasRenderer {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.mode === 'movingCanvas' && interaction.canvasMoveRect) {
|
||||
const rect = interaction.canvasMoveRect;
|
||||
ctx.save();
|
||||
@@ -166,11 +142,9 @@ export class CanvasRenderer {
|
||||
ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
||||
ctx.setLineDash([]);
|
||||
ctx.restore();
|
||||
|
||||
const text = `(${Math.round(rect.x)}, ${Math.round(rect.y)})`;
|
||||
const textWorldX = rect.x + rect.width / 2;
|
||||
const textWorldY = rect.y - (20 / this.canvas.viewport.zoom);
|
||||
|
||||
ctx.save();
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
||||
@@ -188,12 +162,11 @@ export class CanvasRenderer {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
renderLayerInfo(ctx) {
|
||||
if (this.canvas.canvasSelection.selectedLayer) {
|
||||
this.canvas.canvasSelection.selectedLayers.forEach(layer => {
|
||||
if (!layer.image) return;
|
||||
|
||||
this.canvas.canvasSelection.selectedLayers.forEach((layer) => {
|
||||
if (!layer.image)
|
||||
return;
|
||||
const layerIndex = this.canvas.layers.indexOf(layer);
|
||||
const currentWidth = Math.round(layer.width);
|
||||
const currentHeight = Math.round(layer.height);
|
||||
@@ -207,15 +180,13 @@ export class CanvasRenderer {
|
||||
const rad = layer.rotation * Math.PI / 180;
|
||||
const cos = Math.cos(rad);
|
||||
const sin = Math.sin(rad);
|
||||
|
||||
const halfW = layer.width / 2;
|
||||
const halfH = layer.height / 2;
|
||||
|
||||
const localCorners = [
|
||||
{x: -halfW, y: -halfH},
|
||||
{x: halfW, y: -halfH},
|
||||
{x: halfW, y: halfH},
|
||||
{x: -halfW, y: halfH}
|
||||
{ x: -halfW, y: -halfH },
|
||||
{ x: halfW, y: -halfH },
|
||||
{ x: halfW, y: halfH },
|
||||
{ x: -halfW, y: halfH }
|
||||
];
|
||||
const worldCorners = localCorners.map(p => ({
|
||||
x: centerX + p.x * cos - p.y * sin,
|
||||
@@ -232,10 +203,8 @@ export class CanvasRenderer {
|
||||
const textWorldY = maxY + padding;
|
||||
ctx.save();
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
||||
|
||||
ctx.font = "14px sans-serif";
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
@@ -244,59 +213,46 @@ export class CanvasRenderer {
|
||||
const textBgWidth = Math.max(...textMetrics.map(m => m.width)) + 10;
|
||||
const lineHeight = 18;
|
||||
const textBgHeight = lines.length * lineHeight + 4;
|
||||
|
||||
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
|
||||
ctx.fillRect(screenX - textBgWidth / 2, screenY - textBgHeight / 2, textBgWidth, textBgHeight);
|
||||
|
||||
ctx.fillStyle = "white";
|
||||
lines.forEach((line, index) => {
|
||||
const yPos = screenY - (textBgHeight / 2) + (lineHeight / 2) + (index * lineHeight) + 2;
|
||||
ctx.fillText(line, screenX, yPos);
|
||||
});
|
||||
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
drawGrid(ctx) {
|
||||
const gridSize = 64;
|
||||
const lineWidth = 0.5 / this.canvas.viewport.zoom;
|
||||
|
||||
const viewLeft = this.canvas.viewport.x;
|
||||
const viewTop = this.canvas.viewport.y;
|
||||
const viewRight = this.canvas.viewport.x + this.canvas.offscreenCanvas.width / this.canvas.viewport.zoom;
|
||||
const viewBottom = this.canvas.viewport.y + this.canvas.offscreenCanvas.height / this.canvas.viewport.zoom;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#707070';
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
for (let x = Math.floor(viewLeft / gridSize) * gridSize; x < viewRight; x += gridSize) {
|
||||
ctx.moveTo(x, viewTop);
|
||||
ctx.lineTo(x, viewBottom);
|
||||
}
|
||||
|
||||
for (let y = Math.floor(viewTop / gridSize) * gridSize; y < viewBottom; y += gridSize) {
|
||||
ctx.moveTo(viewLeft, y);
|
||||
ctx.lineTo(viewRight, y);
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
drawCanvasOutline(ctx) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
|
||||
ctx.lineWidth = 2 / this.canvas.viewport.zoom;
|
||||
ctx.setLineDash([10 / this.canvas.viewport.zoom, 5 / this.canvas.viewport.zoom]);
|
||||
|
||||
ctx.rect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
drawSelectionFrame(ctx, layer) {
|
||||
const lineWidth = 2 / this.canvas.viewport.zoom;
|
||||
const handleRadius = 5 / this.canvas.viewport.zoom;
|
||||
@@ -313,44 +269,36 @@ export class CanvasRenderer {
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.strokeStyle = '#000000';
|
||||
ctx.lineWidth = 1 / this.canvas.viewport.zoom;
|
||||
|
||||
for (const key in handles) {
|
||||
const point = handles[key];
|
||||
ctx.beginPath();
|
||||
const localX = point.x - (layer.x + layer.width / 2);
|
||||
const localY = point.y - (layer.y + layer.height / 2);
|
||||
|
||||
const rad = -layer.rotation * Math.PI / 180;
|
||||
const rotatedX = localX * Math.cos(rad) - localY * Math.sin(rad);
|
||||
const rotatedY = localX * Math.sin(rad) + localY * Math.cos(rad);
|
||||
|
||||
ctx.arc(rotatedX, rotatedY, handleRadius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
drawPendingGenerationAreas(ctx) {
|
||||
const areasToDraw = [];
|
||||
|
||||
// 1. Get areas from active managers
|
||||
if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) {
|
||||
this.canvas.batchPreviewManagers.forEach(manager => {
|
||||
this.canvas.batchPreviewManagers.forEach((manager) => {
|
||||
if (manager.generationArea) {
|
||||
areasToDraw.push(manager.generationArea);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Get the area from the pending context (if it exists)
|
||||
if (this.canvas.pendingBatchContext && this.canvas.pendingBatchContext.outputArea) {
|
||||
areasToDraw.push(this.canvas.pendingBatchContext.outputArea);
|
||||
}
|
||||
|
||||
if (areasToDraw.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Draw all collected areas
|
||||
areasToDraw.forEach(area => {
|
||||
ctx.save();
|
||||
|
||||
Reference in New Issue
Block a user