Files
2025-05-24 21:35:21 +08:00

251 lines
11 KiB
JavaScript

import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { ComfyWidgets } from "../../scripts/widgets.js";
app.registerExtension({
name: "ImagePreview.Adjustment",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name === "ImageSizeAdjustment") {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const result = onNodeCreated?.apply(this, arguments);
this.widgets_start_y = 30;
this.setupWebSocket();
// 创建偏移量控制器
const createOffsetWidget = (name, defaultValue = 0) => {
const widget = ComfyWidgets.INT(this, name, ["INT", {
default: defaultValue,
min: -9999,
max: 9999,
step: 8
}]);
// 设置值变化回调
widget.widget.callback = (value) => {
// 将值调整为8的倍数
value = Math.floor(value / 8) * 8;
if (this.originalImageData) {
const size = name === "x_offset"
? this.originalImageData[0]?.length
: this.originalImageData.length;
if (value < 0 && Math.abs(value) > size) {
value = -size;
this.widgets.find(w => w.name === name).value = value;
}
}
this[name] = value;
this.updatePreview(true);
};
// 设置拖动事件
widget.widget.onDragStart = function() {
this.node.isAdjusting = true;
};
widget.widget.onDragEnd = function() {
this.node.isAdjusting = false;
this.node.updatePreview(false);
};
return widget;
};
// 创建 x 和 y 偏移控制器
createOffsetWidget("x_offset");
createOffsetWidget("y_offset");
return result;
};
// 添加WebSocket设置方法
nodeType.prototype.setupWebSocket = function() {
api.addEventListener("image_preview_update", async (event) => {
const data = event.detail;
if (data && data.node_id === this.id.toString()) {
console.log(`[ImagePreview] 节点 ${this.id} 接收到更新数据`);
if (data.image_data) {
this.loadImageFromBase64(data.image_data);
}
}
});
};
// 更新预览方法
nodeType.prototype.updatePreview = function(onlyPreview = false) {
if (!this.originalImageData || !this.canvas) {
return;
}
if (this.updateTimeout) {
clearTimeout(this.updateTimeout);
}
this.updateTimeout = setTimeout(() => {
const ctx = this.canvas.getContext("2d");
const originalWidth = this.originalImageData[0].length;
const originalHeight = this.originalImageData.length;
let x_offset = this.x_offset || 0;
let y_offset = this.y_offset || 0;
// 计算新的宽度和高度
const newWidth = Math.max(1, originalWidth + x_offset);
const newHeight = Math.max(1, originalHeight + y_offset);
// 同步设置画布和容器的尺寸
this.canvas.width = newWidth;
this.canvas.height = newHeight;
ctx.clearRect(0, 0, newWidth, newHeight);
if (!this.tempCanvas) {
this.tempCanvas = document.createElement('canvas');
}
if (!this.originalImageRendered) {
this.tempCanvas.width = originalWidth;
this.tempCanvas.height = originalHeight;
const tempCtx = this.tempCanvas.getContext('2d');
const imgData = new ImageData(originalWidth, originalHeight);
for (let y = 0; y < originalHeight; y++) {
for (let x = 0; x < originalWidth; x++) {
const dstIdx = (y * originalWidth + x) * 4;
const srcPixel = this.originalImageData[y][x];
imgData.data[dstIdx] = srcPixel[0]; // R
imgData.data[dstIdx + 1] = srcPixel[1]; // G
imgData.data[dstIdx + 2] = srcPixel[2]; // B
imgData.data[dstIdx + 3] = 255; // A
}
}
tempCtx.putImageData(imgData, 0, 0);
this.originalImageRendered = true;
}
ctx.drawImage(
this.tempCanvas,
0, 0,
originalWidth, originalHeight,
0, 0,
newWidth, newHeight
);
if (!onlyPreview && !this.isAdjusting) {
const adjustedData = ctx.getImageData(0, 0, newWidth, newHeight);
this.sendAdjustedData(adjustedData);
}
}, this.isAdjusting ? 50 : 0);
};
// 发送调整后的数据
nodeType.prototype.sendAdjustedData = async function(adjustedData) {
try {
const endpoint = '/image_preview/apply';
const nodeId = String(this.id);
console.log(`[ImagePreview] 发送调整后的数据 - 尺寸: ${adjustedData.width}x${adjustedData.height}`);
const canvas = document.createElement('canvas');
canvas.width = adjustedData.width;
canvas.height = adjustedData.height;
const ctx = canvas.getContext('2d');
ctx.putImageData(adjustedData, 0, 0);
const blob = await new Promise(resolve => {
canvas.toBlob(resolve, 'image/jpeg', 0.9);
});
const formData = new FormData();
formData.append('node_id', nodeId);
formData.append('width', adjustedData.width);
formData.append('height', adjustedData.height);
formData.append('image_data', blob, 'adjusted_image.jpg');
api.fetchApi(endpoint, {
method: 'POST',
body: formData
});
} catch (error) {
console.error('发送数据时出错:', error);
}
};
// 添加从base64加载图像的方法
nodeType.prototype.loadImageFromBase64 = function(base64Data) {
const img = new Image();
img.onload = () => {
this.originalImageRendered = false;
const tempCanvas = document.createElement('canvas');
tempCanvas.width = img.width;
tempCanvas.height = img.height;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(img, 0, 0);
const imageData = tempCtx.getImageData(0, 0, img.width, img.height);
const pixelArray = [];
for (let y = 0; y < img.height; y++) {
const row = [];
for (let x = 0; x < img.width; x++) {
const idx = (y * img.width + x) * 4;
row.push([
imageData.data[idx], // R
imageData.data[idx + 1], // G
imageData.data[idx + 2] // B
]);
}
pixelArray.push(row);
}
this.originalImageData = pixelArray;
this.updatePreview();
};
img.src = base64Data;
};
// 添加节点时的处理
const onAdded = nodeType.prototype.onAdded;
nodeType.prototype.onAdded = function() {
const result = onAdded?.apply(this, arguments);
if (!this.previewElement && this.id !== undefined && this.id !== -1) {
const previewContainer = document.createElement("div");
previewContainer.style.position = "relative";
previewContainer.style.width = "100%";
previewContainer.style.height = "100%";
previewContainer.style.backgroundColor = "#333";
previewContainer.style.borderRadius = "8px";
previewContainer.style.overflow = "hidden";
const canvas = document.createElement("canvas");
canvas.style.width = "100%";
canvas.style.height = "100%";
canvas.style.objectFit = "contain";
previewContainer.appendChild(canvas);
this.canvas = canvas;
this.previewElement = previewContainer;
this.widgets ||= [];
this.widgets_up = true;
requestAnimationFrame(() => {
if (this.widgets) {
this.previewWidget = this.addDOMWidget("preview", "preview", previewContainer);
this.setDirtyCanvas(true, true);
}
});
}
return result;
};
}
}
});