Files
Bjornulf_custom_nodes/web/js/images_compare.js
justumen 3ebd5cbb92 0.71
2025-02-16 20:58:39 +01:00

210 lines
7.6 KiB
JavaScript

import { app } from "../../../scripts/app.js";
import { api } from "../../../scripts/api.js";
function imageDataToUrl(data) {
return api.apiURL(
`/view?filename=${encodeURIComponent(data.filename)}&type=${data.type}&subfolder=${data.subfolder}${app.getPreviewFormatParam()}`
);
}
app.registerExtension({
name: "Bjornulf.FourImageViewer",
async nodeCreated(node) {
if (node.comfyClass !== "Bjornulf_FourImageViewer") return;
const marginTop = 90;
const verticalOffset = -30;
const minSize = 256; // Minimum size for the node
const maxSize = 2048; // Maximum size for the node
node.size = [512, 512 + marginTop];
node.images = new Array(4).fill(null);
const padding = 10;
// Add resize handles
node.flags |= LiteGraph.RESIZABLE;
node.onResize = function(size) {
// Ensure square aspect ratio (excluding marginTop)
const minDimension = Math.max(minSize, Math.min(size[0], size[1] - marginTop));
const maxDimension = Math.min(maxSize, minDimension);
size[0] = maxDimension;
size[1] = maxDimension + marginTop;
// Update slider positions proportionally
const fullImgWidth = size[0] - 3 * padding;
const fullImgHeight = size[1] - padding - marginTop;
// Only update sliders if they exist (node has been initialized)
if (node.hasOwnProperty('sliderX')) {
const oldWidth = node.size[0] - 3 * padding;
const oldHeight = node.size[1] - padding - marginTop;
// Calculate relative positions (0 to 1)
const relativeX = (node.sliderX - padding) / oldWidth;
const relativeY = (node.sliderY - marginTop) / oldHeight;
// Update slider positions
node.sliderX = padding + (fullImgWidth * relativeX);
node.sliderY = marginTop + (fullImgHeight * relativeY);
} else {
// Initial slider positions
node.sliderX = padding + fullImgWidth / 2;
node.sliderY = marginTop + fullImgHeight / 2;
}
node.size = size;
return size;
};
// Full area where images get drawn
const fullImgWidth = node.size[0] - 3 * padding;
const fullImgHeight = node.size[1] - padding - marginTop;
node.sliderX = padding + fullImgWidth / 2;
node.sliderY = marginTop + fullImgHeight / 2;
node.onMouseDown = function(e) {
const rect = node.getBounding();
const [clickX, clickY] = [
e.canvasX - rect[0],
e.canvasY - rect[1] + verticalOffset
];
const imgWidth = rect[2] - 3 * padding;
const imgHeight = rect[3] - padding - marginTop;
const xStart = padding;
const xEnd = xStart + imgWidth;
const yStart = marginTop;
const yEnd = yStart + imgHeight;
if (clickX >= xStart && clickX <= xEnd && clickY >= yStart && clickY <= yEnd) {
const hasImage2 = node.images[1] !== null;
const hasImage3 = node.images[2] !== null;
const hasImage4 = node.images[3] !== null;
if (hasImage2 && !hasImage3 && !hasImage4) {
node.sliderY = yEnd;
}
node.sliderX = Math.max(xStart, Math.min(clickX, xEnd));
node.sliderY = hasImage3 || hasImage4
? Math.max(yStart, Math.min(clickY, yEnd))
: yEnd;
app.graph.setDirtyCanvas(true, true);
return true;
}
return false;
};
node.onExecuted = async function(message) {
node.images = new Array(4).fill(null);
for (let i = 1; i <= 4; i++) {
const images = message[`images_${i}`] || [];
if (images.length) {
const imgData = images[0];
const img = new Image();
img.src = imageDataToUrl(imgData);
await new Promise(resolve => (img.onload = img.onerror = resolve));
node.images[i - 1] = img;
}
}
app.graph.setDirtyCanvas(true, true);
};
node.onDrawForeground = function(ctx) {
const padding = 10;
const xStart = padding;
const xEnd = xStart + (node.size[0] - 3 * padding);
const yStart = marginTop;
const yEnd = yStart + (node.size[1] - padding - marginTop);
const fullImgWidth = node.size[0] - 3 * padding;
const fullImgHeight = node.size[1] - padding - marginTop;
function getFittedDestRect(dx, dy, dWidth, dHeight, targetRatio) {
let newWidth = dWidth;
let newHeight = dWidth / targetRatio;
if (newHeight > dHeight) {
newHeight = dHeight;
newWidth = dHeight * targetRatio;
}
const offsetX = dx + (dWidth - newWidth) / 2;
const offsetY = dy + (dHeight - newHeight) / 2;
return [offsetX, offsetY, newWidth, newHeight];
}
function drawCroppedImage(img, dx, dy, dWidth, dHeight) {
if (!img) return;
let targetRatio = dWidth / dHeight;
if (node.images[0] && node.images[0].naturalWidth && node.images[0].naturalHeight) {
targetRatio = node.images[0].naturalWidth / node.images[0].naturalHeight;
}
const [ndx, ndy, ndWidth, ndHeight] = getFittedDestRect(dx, dy, dWidth, dHeight, targetRatio);
const imgRatio = img.naturalWidth / img.naturalHeight;
let sx = 0, sy = 0, sWidth = img.naturalWidth, sHeight = img.naturalHeight;
if (imgRatio > targetRatio) {
sWidth = img.naturalHeight * targetRatio;
sx = (img.naturalWidth - sWidth) / 2;
} else if (imgRatio < targetRatio) {
sHeight = img.naturalWidth / targetRatio;
sy = (img.naturalHeight - sHeight) / 2;
}
ctx.drawImage(img, sx, sy, sWidth, sHeight, ndx, ndy, ndWidth, ndHeight);
}
const connectedImages = node.images.slice(1).filter(img => img !== null).length;
if (connectedImages === 0) {
if (node.images[0]) {
drawCroppedImage(node.images[0], xStart, yStart, fullImgWidth, fullImgHeight);
}
} else if (connectedImages === 1 && node.images[1]) {
const splitX = node.sliderX;
ctx.save();
ctx.beginPath();
ctx.rect(xStart, yStart, splitX - xStart, fullImgHeight);
ctx.clip();
drawCroppedImage(node.images[0], xStart, yStart, fullImgWidth, fullImgHeight);
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.rect(splitX, yStart, xEnd - splitX, fullImgHeight);
ctx.clip();
drawCroppedImage(node.images[1], xStart, yStart, fullImgWidth, fullImgHeight);
ctx.restore();
} else {
const drawQuadrant = (imgIndex, clipX, clipY, clipW, clipH) => {
if (!node.images[imgIndex]) return;
ctx.save();
ctx.beginPath();
ctx.rect(clipX, clipY, clipW, clipH);
ctx.clip();
drawCroppedImage(node.images[imgIndex], xStart, yStart, fullImgWidth, fullImgHeight);
ctx.restore();
};
drawQuadrant(0, xStart, yStart, node.sliderX - xStart, node.sliderY - yStart);
drawQuadrant(1, node.sliderX, yStart, xEnd - node.sliderX, node.sliderY - yStart);
if (node.images[3] === null) {
drawQuadrant(2, xStart, node.sliderY, xEnd - xStart, yEnd - node.sliderY);
} else {
drawQuadrant(2, xStart, node.sliderY, node.sliderX - xStart, yEnd - node.sliderY);
drawQuadrant(3, node.sliderX, node.sliderY, xEnd - node.sliderX, yEnd - node.sliderY);
}
}
ctx.strokeStyle = "#FFF";
ctx.lineWidth = 1;
if (connectedImages > 0) {
ctx.beginPath();
ctx.moveTo(node.sliderX, yStart);
ctx.lineTo(node.sliderX, yEnd);
if (connectedImages >= 2) {
ctx.moveTo(xStart, node.sliderY);
ctx.lineTo(xEnd, node.sliderY);
}
ctx.stroke();
}
};
},
});