mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Refactor image utilities and cache to separate modules
Moved image data validation, conversion, mask application, and preparation functions from Canvas_view.js to a new ImageUtils.js module. Extracted the image cache logic into a new ImageCache.js class. Updated Canvas_view.js to use the new modules and refactored relevant imports and usage.
This commit is contained in:
@@ -4,9 +4,12 @@ import {$el} from "../../scripts/ui.js";
|
||||
|
||||
import {Canvas} from "./Canvas.js";
|
||||
import {clearAllCanvasStates} from "./db.js";
|
||||
import {ImageCache} from "./ImageCache.js";
|
||||
import { validateImageData, convertImageData, applyMaskToImageData, prepareImageForCanvas } from "./ImageUtils.js";
|
||||
|
||||
async function createCanvasWidget(node, widget, app) {
|
||||
const canvas = new Canvas(node, widget);
|
||||
const imageCache = new ImageCache();
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@@ -411,7 +414,7 @@ async function createCanvasWidget(node, widget, app) {
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
zIndex: '1000'
|
||||
zIndex: '9999'
|
||||
}
|
||||
}, [
|
||||
$el("div", {
|
||||
@@ -911,7 +914,7 @@ async function createCanvasWidget(node, widget, app) {
|
||||
const linkId = node.inputs[0].link;
|
||||
const inputData = app.nodeOutputs[linkId];
|
||||
if (inputData) {
|
||||
ImageCache.set(linkId, inputData);
|
||||
imageCache.set(linkId, inputData);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -935,192 +938,6 @@ async function createCanvasWidget(node, widget, app) {
|
||||
}
|
||||
|
||||
|
||||
function validateImageData(data) {
|
||||
|
||||
console.log("Validating data structure:", {
|
||||
hasData: !!data,
|
||||
type: typeof data,
|
||||
isArray: Array.isArray(data),
|
||||
keys: data ? Object.keys(data) : null,
|
||||
shape: data?.shape,
|
||||
dataType: data?.data ? data.data.constructor.name : null,
|
||||
fullData: data
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
console.log("Data is null or undefined");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
console.log("Data is array, getting first element");
|
||||
data = data[0];
|
||||
}
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
console.log("Invalid data type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.data) {
|
||||
console.log("Missing data property");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(data.data instanceof Float32Array)) {
|
||||
|
||||
try {
|
||||
data.data = new Float32Array(data.data);
|
||||
} catch (e) {
|
||||
console.log("Failed to convert data to Float32Array:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function convertImageData(data) {
|
||||
console.log("Converting image data:", data);
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
data = data[0];
|
||||
}
|
||||
|
||||
const shape = data.shape;
|
||||
const height = shape[1];
|
||||
const width = shape[2];
|
||||
const channels = shape[3];
|
||||
const floatData = new Float32Array(data.data);
|
||||
|
||||
console.log("Processing dimensions:", {height, width, channels});
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const tensorIndex = (h * width + w) * channels;
|
||||
|
||||
for (let c = 0; c < channels; c++) {
|
||||
const value = floatData[tensorIndex + c];
|
||||
rgbaData[pixelIndex + c] = Math.max(0, Math.min(255, Math.round(value * 255)));
|
||||
}
|
||||
|
||||
rgbaData[pixelIndex + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
function applyMaskToImageData(imageData, maskData) {
|
||||
console.log("Applying mask to image data");
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(imageData.data);
|
||||
const width = imageData.width;
|
||||
const height = imageData.height;
|
||||
|
||||
const maskShape = maskData.shape;
|
||||
const maskFloatData = new Float32Array(maskData.data);
|
||||
|
||||
console.log(`Applying mask of shape: ${maskShape}`);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const maskIndex = h * width + w;
|
||||
|
||||
const alpha = maskFloatData[maskIndex];
|
||||
rgbaData[pixelIndex + 3] = Math.max(0, Math.min(255, Math.round(alpha * 255)));
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Mask application completed");
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
const ImageCache = {
|
||||
cache: new Map(),
|
||||
|
||||
set(key, imageData) {
|
||||
console.log("Caching image data for key:", key);
|
||||
this.cache.set(key, imageData);
|
||||
},
|
||||
|
||||
get(key) {
|
||||
const data = this.cache.get(key);
|
||||
console.log("Retrieved cached data for key:", key, !!data);
|
||||
return data;
|
||||
},
|
||||
|
||||
has(key) {
|
||||
return this.cache.has(key);
|
||||
},
|
||||
|
||||
clear() {
|
||||
console.log("Clearing image cache");
|
||||
this.cache.clear();
|
||||
}
|
||||
};
|
||||
|
||||
function prepareImageForCanvas(inputImage) {
|
||||
console.log("Preparing image for canvas:", inputImage);
|
||||
|
||||
try {
|
||||
|
||||
if (Array.isArray(inputImage)) {
|
||||
inputImage = inputImage[0];
|
||||
}
|
||||
|
||||
if (!inputImage || !inputImage.shape || !inputImage.data) {
|
||||
throw new Error("Invalid input image format");
|
||||
}
|
||||
|
||||
const shape = inputImage.shape;
|
||||
const height = shape[1];
|
||||
const width = shape[2];
|
||||
const channels = shape[3];
|
||||
const floatData = new Float32Array(inputImage.data);
|
||||
|
||||
console.log("Image dimensions:", {height, width, channels});
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const tensorIndex = (h * width + w) * channels;
|
||||
|
||||
for (let c = 0; c < channels; c++) {
|
||||
const value = floatData[tensorIndex + c];
|
||||
rgbaData[pixelIndex + c] = Math.max(0, Math.min(255, Math.round(value * 255)));
|
||||
}
|
||||
|
||||
rgbaData[pixelIndex + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error preparing image:", error);
|
||||
throw new Error(`Failed to prepare image: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.CanvasNode",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
|
||||
25
js/ImageCache.js
Normal file
25
js/ImageCache.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export class ImageCache {
|
||||
constructor() {
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
set(key, imageData) {
|
||||
console.log("Caching image data for key:", key);
|
||||
this.cache.set(key, imageData);
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const data = this.cache.get(key);
|
||||
console.log("Retrieved cached data for key:", key, !!data);
|
||||
return data;
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.cache.has(key);
|
||||
}
|
||||
|
||||
clear() {
|
||||
console.log("Clearing image cache");
|
||||
this.cache.clear();
|
||||
}
|
||||
}
|
||||
158
js/ImageUtils.js
Normal file
158
js/ImageUtils.js
Normal file
@@ -0,0 +1,158 @@
|
||||
export function validateImageData(data) {
|
||||
console.log("Validating data structure:", {
|
||||
hasData: !!data,
|
||||
type: typeof data,
|
||||
isArray: Array.isArray(data),
|
||||
keys: data ? Object.keys(data) : null,
|
||||
shape: data?.shape,
|
||||
dataType: data?.data ? data.data.constructor.name : null,
|
||||
fullData: data
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
console.log("Data is null or undefined");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
console.log("Data is array, getting first element");
|
||||
data = data[0];
|
||||
}
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
console.log("Invalid data type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.data) {
|
||||
console.log("Missing data property");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(data.data instanceof Float32Array)) {
|
||||
try {
|
||||
data.data = new Float32Array(data.data);
|
||||
} catch (e) {
|
||||
console.log("Failed to convert data to Float32Array:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function convertImageData(data) {
|
||||
console.log("Converting image data:", data);
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
data = data[0];
|
||||
}
|
||||
|
||||
const shape = data.shape;
|
||||
const height = shape[1];
|
||||
const width = shape[2];
|
||||
const channels = shape[3];
|
||||
const floatData = new Float32Array(data.data);
|
||||
|
||||
console.log("Processing dimensions:", {height, width, channels});
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const tensorIndex = (h * width + w) * channels;
|
||||
|
||||
for (let c = 0; c < channels; c++) {
|
||||
const value = floatData[tensorIndex + c];
|
||||
rgbaData[pixelIndex + c] = Math.max(0, Math.min(255, Math.round(value * 255)));
|
||||
}
|
||||
|
||||
rgbaData[pixelIndex + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
export function applyMaskToImageData(imageData, maskData) {
|
||||
console.log("Applying mask to image data");
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(imageData.data);
|
||||
const width = imageData.width;
|
||||
const height = imageData.height;
|
||||
|
||||
const maskShape = maskData.shape;
|
||||
const maskFloatData = new Float32Array(maskData.data);
|
||||
|
||||
console.log(`Applying mask of shape: ${maskShape}`);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const maskIndex = h * width + w;
|
||||
|
||||
const alpha = maskFloatData[maskIndex];
|
||||
rgbaData[pixelIndex + 3] = Math.max(0, Math.min(255, Math.round(alpha * 255)));
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Mask application completed");
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
export function prepareImageForCanvas(inputImage) {
|
||||
console.log("Preparing image for canvas:", inputImage);
|
||||
|
||||
try {
|
||||
if (Array.isArray(inputImage)) {
|
||||
inputImage = inputImage[0];
|
||||
}
|
||||
|
||||
if (!inputImage || !inputImage.shape || !inputImage.data) {
|
||||
throw new Error("Invalid input image format");
|
||||
}
|
||||
|
||||
const shape = inputImage.shape;
|
||||
const height = shape[1];
|
||||
const width = shape[2];
|
||||
const channels = shape[3];
|
||||
const floatData = new Float32Array(inputImage.data);
|
||||
|
||||
console.log("Image dimensions:", {height, width, channels});
|
||||
|
||||
const rgbaData = new Uint8ClampedArray(width * height * 4);
|
||||
|
||||
for (let h = 0; h < height; h++) {
|
||||
for (let w = 0; w < width; w++) {
|
||||
const pixelIndex = (h * width + w) * 4;
|
||||
const tensorIndex = (h * width + w) * channels;
|
||||
|
||||
for (let c = 0; c < channels; c++) {
|
||||
const value = floatData[tensorIndex + c];
|
||||
rgbaData[pixelIndex + c] = Math.max(0, Math.min(255, Math.round(value * 255)));
|
||||
}
|
||||
|
||||
rgbaData[pixelIndex + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data: rgbaData,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error preparing image:", error);
|
||||
throw new Error(`Failed to prepare image: ${error.message}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user