From c3cc33c71117e7786d4d75427d5b16754dfd4328 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Wed, 25 Jun 2025 07:05:44 +0200 Subject: [PATCH] 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. --- js/Canvas_view.js | 193 ++-------------------------------------------- js/ImageCache.js | 25 ++++++ js/ImageUtils.js | 158 +++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 188 deletions(-) create mode 100644 js/ImageCache.js create mode 100644 js/ImageUtils.js diff --git a/js/Canvas_view.js b/js/Canvas_view.js index 0fca669..cf2023d 100644 --- a/js/Canvas_view.js +++ b/js/Canvas_view.js @@ -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) { diff --git a/js/ImageCache.js b/js/ImageCache.js new file mode 100644 index 0000000..c7c6cf8 --- /dev/null +++ b/js/ImageCache.js @@ -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(); + } +} \ No newline at end of file diff --git a/js/ImageUtils.js b/js/ImageUtils.js new file mode 100644 index 0000000..cf1d441 --- /dev/null +++ b/js/ImageUtils.js @@ -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}`); + } +} \ No newline at end of file