mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 12:52:10 -03:00
Add documentation for core modules and update Canvas.js
Added documentation files for ComfyApi, ComfyApp, LitegraphService, and MaskEditor, summarizing their main functions and usage. Refactored js/Canvas.js to improve mask processing logic, using viewport pan for cropping and applying mask color only to non-transparent pixels. Also made minor formatting and logging consistency improvements throughout Canvas.js.
This commit is contained in:
96
Doc/ComfyApi
Normal file
96
Doc/ComfyApi
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# ComfyApi - Function Documentation Summary import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
|
## Basic Information
|
||||||
|
|
||||||
|
ComfyApi is a class for communication with ComfyUI backend via WebSocket and REST API.
|
||||||
|
|
||||||
|
## Main Functions:
|
||||||
|
|
||||||
|
### Connection and Initialization
|
||||||
|
|
||||||
|
- constructor() - Initializes API, sets host and base path
|
||||||
|
- init() - Starts WebSocket connection for real-time updates
|
||||||
|
- #createSocket() - Creates and manages WebSocket connection
|
||||||
|
|
||||||
|
### URL Management
|
||||||
|
|
||||||
|
- internalURL(route) - Generates URL for internal endpoints
|
||||||
|
- apiURL(route) - Generates URL for public API endpoints
|
||||||
|
- fileURL(route) - Generates URL for static files
|
||||||
|
- fetchApi(route, options) - Performs HTTP requests with automatic user headers
|
||||||
|
|
||||||
|
### Event Handling
|
||||||
|
|
||||||
|
- addEventListener(type, callback) - Listens for API events (status, executing, progress, etc.)
|
||||||
|
- removeEventListener(type, callback) - Removes event listeners
|
||||||
|
- dispatchCustomEvent(type, detail) - Emits custom events
|
||||||
|
|
||||||
|
### Queue and Prompt Management
|
||||||
|
|
||||||
|
- queuePrompt(number, data) - Adds prompt to execution queue
|
||||||
|
- getQueue() - Gets current queue state (Running/Pending)
|
||||||
|
- interrupt() - Interrupts currently executing prompt
|
||||||
|
- clearItems(type) - Clears queue or history
|
||||||
|
- deleteItem(type, id) - Removes item from queue or history
|
||||||
|
|
||||||
|
### History and Statistics
|
||||||
|
|
||||||
|
- getHistory(max_items) - Gets history of executed prompts
|
||||||
|
- getSystemStats() - Gets system statistics (Python, OS, GPU, etc.)
|
||||||
|
- getLogs() - Gets system logs
|
||||||
|
- getRawLogs() - Gets raw logs
|
||||||
|
- subscribeLogs(enabled) - Enables/disables log subscription
|
||||||
|
|
||||||
|
### Model and Resource Management
|
||||||
|
|
||||||
|
- getNodeDefs(options) - Gets definitions of available nodes
|
||||||
|
- getExtensions() - List of installed extensions
|
||||||
|
- getEmbeddings() - List of available embeddings
|
||||||
|
- getModelFolders() - List of model folders
|
||||||
|
- getModels(folder) - List of models in given folder
|
||||||
|
- viewMetadata(folder, model) - Metadata of specific model
|
||||||
|
|
||||||
|
### Workflow Templates
|
||||||
|
|
||||||
|
- getWorkflowTemplates() - Gets workflow templates from custom nodes
|
||||||
|
- getCoreWorkflowTemplates() - Gets core workflow templates
|
||||||
|
|
||||||
|
### User Management
|
||||||
|
|
||||||
|
- getUserConfig() - Gets user configuration
|
||||||
|
- createUser(username) - Creates new user
|
||||||
|
- getSettings() - Gets all user settings
|
||||||
|
- getSetting(id) - Gets specific setting
|
||||||
|
- storeSettings(settings) - Saves settings dictionary
|
||||||
|
- storeSetting(id, value) - Saves single setting
|
||||||
|
|
||||||
|
### User Data
|
||||||
|
|
||||||
|
- getUserData(file) - Gets user data file
|
||||||
|
- storeUserData(file, data, options) - Saves user data
|
||||||
|
- deleteUserData(file) - Deletes user data file
|
||||||
|
- moveUserData(source, dest) - Moves data file
|
||||||
|
- listUserDataFullInfo(dir) - Lists files with full information
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- getFolderPaths() - Gets system folder paths
|
||||||
|
- getCustomNodesI18n() - Gets internationalization data for custom nodes
|
||||||
|
|
||||||
|
## Important Properties
|
||||||
|
|
||||||
|
- clientId - Client ID from WebSocket
|
||||||
|
- authToken - Authorization token for ComfyOrg account
|
||||||
|
- apiKey - API key for ComfyOrg account
|
||||||
|
- socket - Active WebSocket connection
|
||||||
|
|
||||||
|
## WebSocket Event Types
|
||||||
|
|
||||||
|
- status - System status
|
||||||
|
- executing - Currently executing node
|
||||||
|
- progress - Execution progress
|
||||||
|
- executed - Node executed
|
||||||
|
- execution_start/success/error/interrupted/cached - Execution events
|
||||||
|
- logs - System logs
|
||||||
|
- b_preview - Image preview (binary)
|
||||||
|
- reconnecting/reconnected - Connection events
|
||||||
72
Doc/ComfyApp
Normal file
72
Doc/ComfyApp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
## __Main ComfyApp Functions__ import { app, ComfyApp } from "../../scripts/app.js";
|
||||||
|
|
||||||
|
### __Application Management__
|
||||||
|
|
||||||
|
- `setup(canvasEl)` - Initializes the application on the page, loads extensions, registers nodes
|
||||||
|
- `resizeCanvas()` - Adjusts canvas size to window
|
||||||
|
- `clean()` - Clears application state (node outputs, image previews, errors)
|
||||||
|
|
||||||
|
### __Workflow Management__
|
||||||
|
|
||||||
|
- `loadGraphData(graphData, clean, restore_view, workflow, options)` - Loads workflow data from JSON
|
||||||
|
- `loadApiJson(apiData, fileName)` - Loads workflow from API format
|
||||||
|
- `graphToPrompt(graph, options)` - Converts graph to prompt for execution
|
||||||
|
- `handleFile(file)` - Handles file loading (PNG, WebP, JSON, MP3, MP4, SVG, etc.)
|
||||||
|
|
||||||
|
### __Execution__
|
||||||
|
|
||||||
|
- `queuePrompt(number, batchCount, queueNodeIds)` - Queues prompt for execution
|
||||||
|
- `registerNodes()` - Registers node definitions from backend
|
||||||
|
- `registerNodeDef(nodeId, nodeDef)` - Registers single node definition
|
||||||
|
- `refreshComboInNodes()` - Refreshes combo lists in nodes
|
||||||
|
|
||||||
|
### __Node Management__
|
||||||
|
|
||||||
|
- `registerExtension(extension)` - Registers ComfyUI extension
|
||||||
|
- `updateVueAppNodeDefs(defs)` - Updates node definitions in Vue app
|
||||||
|
- `revokePreviews(nodeId)` - Frees memory for node previews
|
||||||
|
|
||||||
|
### __Clipboard__
|
||||||
|
|
||||||
|
- `copyToClipspace(node)` - Copies node to clipboard
|
||||||
|
- `pasteFromClipspace(node)` - Pastes data from clipboard to node
|
||||||
|
|
||||||
|
### __Position Conversion__
|
||||||
|
|
||||||
|
- `clientPosToCanvasPos(pos)` - Converts client position to canvas position
|
||||||
|
- `canvasPosToClientPos(pos)` - Converts canvas position to client position
|
||||||
|
|
||||||
|
### __Error Handling__
|
||||||
|
|
||||||
|
- `showErrorOnFileLoad(file)` - Displays file loading error
|
||||||
|
- `#showMissingNodesError(missingNodeTypes)` - Shows missing nodes error
|
||||||
|
- `#showMissingModelsError(missingModels, paths)` - Shows missing models error
|
||||||
|
|
||||||
|
### __Internal Handlers__
|
||||||
|
|
||||||
|
- `#addDropHandler()` - Handles drag and drop of files
|
||||||
|
- `#addProcessKeyHandler()` - Handles keyboard input
|
||||||
|
- `#addDrawNodeHandler()` - Modifies node drawing behavior
|
||||||
|
- `#addApiUpdateHandlers()` - Handles API updates
|
||||||
|
- `#addConfigureHandler()` - Graph configuration flag
|
||||||
|
- `#addAfterConfigureHandler()` - Post-configuration handling
|
||||||
|
|
||||||
|
### __Deprecated Properties__
|
||||||
|
|
||||||
|
Many properties are marked as deprecated and redirect to appropriate stores:
|
||||||
|
|
||||||
|
- `lastNodeErrors` → `useExecutionStore().lastNodeErrors`
|
||||||
|
- `lastExecutionError` → `useExecutionStore().lastExecutionError`
|
||||||
|
- `runningNodeId` → `useExecutionStore().executingNodeId`
|
||||||
|
- `shiftDown` → `useWorkspaceStore().shiftDown`
|
||||||
|
- `widgets` → `useWidgetStore().widgets`
|
||||||
|
- `extensions` → `useExtensionStore().extensions`
|
||||||
|
|
||||||
|
### __Utility Functions__
|
||||||
|
|
||||||
|
- `sanitizeNodeName(string)` - Cleans node name from dangerous characters
|
||||||
|
- `getPreviewFormatParam()` - Returns preview format parameter
|
||||||
|
- `getRandParam()` - Returns random parameter for refresh
|
||||||
|
- `isApiJson(data)` - Checks if data is in API JSON format
|
||||||
|
|
||||||
|
This application uses Vue and TypeScript composition pattern, where many functionalities are separated into different services and stores (e.g., `useExecutionStore`, `useWorkflowService`, `useExtensionService`, etc.).
|
||||||
75
Doc/LitegraphService
Normal file
75
Doc/LitegraphService
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
LitegraphService Documentation
|
||||||
|
|
||||||
|
Main functions of useLitegraphService()
|
||||||
|
|
||||||
|
Node Registration and Creation Functions:
|
||||||
|
|
||||||
|
registerNodeDef(nodeId: string, nodeDefV1: ComfyNodeDefV1)
|
||||||
|
|
||||||
|
- Registers node definition in LiteGraph system
|
||||||
|
- Creates ComfyNode class with inputs, outputs and widgets
|
||||||
|
- Adds context menu, background drawing and keyboard handling
|
||||||
|
- Invokes extensions before registration
|
||||||
|
|
||||||
|
addNodeOnGraph(nodeDef, options)
|
||||||
|
|
||||||
|
- Adds new node to graph at specified position
|
||||||
|
- By default places node at canvas center
|
||||||
|
|
||||||
|
Navigation and View Functions:
|
||||||
|
|
||||||
|
getCanvasCenter(): Vector2
|
||||||
|
|
||||||
|
- Returns canvas center coordinates accounting for DPI
|
||||||
|
|
||||||
|
goToNode(nodeId: NodeId)
|
||||||
|
|
||||||
|
- Animates transition to specified node on canvas
|
||||||
|
|
||||||
|
resetView()
|
||||||
|
|
||||||
|
- Resets canvas view to default settings (scale 1, offset [0,0])
|
||||||
|
|
||||||
|
fitView()
|
||||||
|
|
||||||
|
- Fits canvas view to show all nodes
|
||||||
|
|
||||||
|
Node Handling Functions (internal):
|
||||||
|
|
||||||
|
addNodeContextMenuHandler(node)
|
||||||
|
|
||||||
|
- Adds context menu with options:
|
||||||
|
|
||||||
|
- Open/Copy/Save image (for image nodes)
|
||||||
|
- Bypass node
|
||||||
|
- Copy/Paste to Clipspace
|
||||||
|
- Open in MaskEditor (for image nodes)
|
||||||
|
|
||||||
|
addDrawBackgroundHandler(node)
|
||||||
|
|
||||||
|
- Adds node background drawing logic
|
||||||
|
- Handles image, animation and video previews
|
||||||
|
- Manages thumbnail display
|
||||||
|
|
||||||
|
addNodeKeyHandler(node)
|
||||||
|
|
||||||
|
- Adds keyboard handling:
|
||||||
|
|
||||||
|
- Left/Right arrows: navigate between images
|
||||||
|
- Escape: close image preview
|
||||||
|
|
||||||
|
ComfyNode Class (created by registerNodeDef):
|
||||||
|
|
||||||
|
Main methods:
|
||||||
|
|
||||||
|
- #addInputs() - adds inputs and widgets to node
|
||||||
|
- #addOutputs() - adds outputs to node
|
||||||
|
- configure() - configures node from serialized data
|
||||||
|
- #setupStrokeStyles() - sets border styles (errors, execution, etc.)
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
|
||||||
|
- comfyClass - ComfyUI class name
|
||||||
|
- nodeData - node definition
|
||||||
|
- Automatic yellow coloring for API nodes
|
||||||
|
|
||||||
76
Doc/MaskEditor
Normal file
76
Doc/MaskEditor
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
MASKEDITOR.TS FUNCTION DOCUMENTATION
|
||||||
|
|
||||||
|
MaskEditorDialog - Main mask editor class
|
||||||
|
|
||||||
|
- getInstance() - Singleton pattern, returns editor instance
|
||||||
|
- show() - Opens the mask editor
|
||||||
|
- save() - Saves mask to server
|
||||||
|
- destroy() - Closes and cleans up editor
|
||||||
|
- isOpened() - Checks if editor is open
|
||||||
|
|
||||||
|
CanvasHistory - Change history management
|
||||||
|
|
||||||
|
- saveState() - Saves current canvas state
|
||||||
|
- undo() - Undo last operation
|
||||||
|
- redo() - Redo undone operation
|
||||||
|
- clearStates() - Clears history
|
||||||
|
|
||||||
|
BrushTool - Brush tool
|
||||||
|
|
||||||
|
- setBrushSize(size) - Sets brush size
|
||||||
|
- setBrushOpacity(opacity) - Sets brush opacity
|
||||||
|
- setBrushHardness(hardness) - Sets brush hardness
|
||||||
|
- setBrushType(type) - Sets brush shape (circle/square)
|
||||||
|
- startDrawing() - Starts drawing
|
||||||
|
- handleDrawing() - Handles drawing during movement
|
||||||
|
- drawEnd() - Ends drawing
|
||||||
|
|
||||||
|
PaintBucketTool - Fill tool
|
||||||
|
|
||||||
|
- floodFill(point) - Fills area with color from point
|
||||||
|
- setTolerance(tolerance) - Sets color tolerance
|
||||||
|
- setFillOpacity(opacity) - Sets fill opacity
|
||||||
|
- invertMask() - Inverts mask
|
||||||
|
|
||||||
|
ColorSelectTool - Color selection tool
|
||||||
|
|
||||||
|
- fillColorSelection(point) - Selects similar colors
|
||||||
|
- setTolerance(tolerance) - Sets selection tolerance
|
||||||
|
- setLivePreview(enabled) - Enables/disables live preview
|
||||||
|
- setComparisonMethod(method) - Sets color comparison method
|
||||||
|
- setApplyWholeImage(enabled) - Applies to whole image
|
||||||
|
- setSelectOpacity(opacity) - Sets selection opacity
|
||||||
|
|
||||||
|
UIManager - Interface management
|
||||||
|
|
||||||
|
- updateBrushPreview() - Updates brush preview
|
||||||
|
- setBrushVisibility(visible) - Shows/hides brush
|
||||||
|
- screenToCanvas(coords) - Converts screen coordinates to canvas
|
||||||
|
- getMaskColor() - Returns mask color
|
||||||
|
- setSaveButtonEnabled(enabled) - Enables/disables save button
|
||||||
|
|
||||||
|
ToolManager - Tool management
|
||||||
|
|
||||||
|
- setTool(tool) - Sets active tool
|
||||||
|
- getCurrentTool() - Returns active tool
|
||||||
|
- handlePointerDown/Move/Up() - Handles mouse/touch events
|
||||||
|
|
||||||
|
PanAndZoomManager - View management
|
||||||
|
|
||||||
|
- zoom(event) - Zooms in/out canvas
|
||||||
|
- handlePanStart/Move() - Handles canvas panning
|
||||||
|
- initializeCanvasPanZoom() - Initializes canvas view
|
||||||
|
- smoothResetView() - Smoothly resets view
|
||||||
|
|
||||||
|
MessageBroker - Communication system
|
||||||
|
|
||||||
|
- publish(topic, data) - Publishes message
|
||||||
|
- subscribe(topic, callback) - Subscribes to topic
|
||||||
|
- pull(topic, data) - Pulls data from topic
|
||||||
|
- createPullTopic/PushTopic() - Creates communication topics
|
||||||
|
|
||||||
|
KeyboardManager - Keyboard handling
|
||||||
|
|
||||||
|
- addListeners() - Adds keyboard listeners
|
||||||
|
- removeListeners() - Removes listeners
|
||||||
|
- isKeyDown(key) - Checks if key is pressed
|
||||||
106
js/Canvas.js
106
js/Canvas.js
@@ -26,7 +26,7 @@ export class Canvas {
|
|||||||
this.node = node;
|
this.node = node;
|
||||||
this.widget = widget;
|
this.widget = widget;
|
||||||
this.canvas = document.createElement('canvas');
|
this.canvas = document.createElement('canvas');
|
||||||
this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });
|
this.ctx = this.canvas.getContext('2d', {willReadFrequently: true});
|
||||||
this.width = 512;
|
this.width = 512;
|
||||||
this.height = 512;
|
this.height = 512;
|
||||||
this.layers = [];
|
this.layers = [];
|
||||||
@@ -60,7 +60,7 @@ export class Canvas {
|
|||||||
log.debug('Canvas widget element:', this.node);
|
log.debug('Canvas widget element:', this.node);
|
||||||
log.info('Canvas initialized', {
|
log.info('Canvas initialized', {
|
||||||
nodeId: this.node.id,
|
nodeId: this.node.id,
|
||||||
dimensions: { width: this.width, height: this.height },
|
dimensions: {width: this.width, height: this.height},
|
||||||
viewport: this.viewport
|
viewport: this.viewport
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ export class Canvas {
|
|||||||
* @param {boolean} replaceLast - Czy zastąpić ostatni stan w historii
|
* @param {boolean} replaceLast - Czy zastąpić ostatni stan w historii
|
||||||
*/
|
*/
|
||||||
saveState(replaceLast = false) {
|
saveState(replaceLast = false) {
|
||||||
log.debug('Saving canvas state', { replaceLast, layersCount: this.layers.length });
|
log.debug('Saving canvas state', {replaceLast, layersCount: this.layers.length});
|
||||||
this.canvasState.saveState(replaceLast);
|
this.canvasState.saveState(replaceLast);
|
||||||
this.incrementOperationCount();
|
this.incrementOperationCount();
|
||||||
this._notifyStateChange();
|
this._notifyStateChange();
|
||||||
@@ -200,11 +200,11 @@ export class Canvas {
|
|||||||
log.info('Performing undo operation');
|
log.info('Performing undo operation');
|
||||||
const historyInfo = this.canvasState.getHistoryInfo();
|
const historyInfo = this.canvasState.getHistoryInfo();
|
||||||
log.debug('History state before undo:', historyInfo);
|
log.debug('History state before undo:', historyInfo);
|
||||||
|
|
||||||
this.canvasState.undo();
|
this.canvasState.undo();
|
||||||
this.incrementOperationCount();
|
this.incrementOperationCount();
|
||||||
this._notifyStateChange();
|
this._notifyStateChange();
|
||||||
|
|
||||||
log.debug('Undo completed, layers count:', this.layers.length);
|
log.debug('Undo completed, layers count:', this.layers.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,11 +216,11 @@ export class Canvas {
|
|||||||
log.info('Performing redo operation');
|
log.info('Performing redo operation');
|
||||||
const historyInfo = this.canvasState.getHistoryInfo();
|
const historyInfo = this.canvasState.getHistoryInfo();
|
||||||
log.debug('History state before redo:', historyInfo);
|
log.debug('History state before redo:', historyInfo);
|
||||||
|
|
||||||
this.canvasState.redo();
|
this.canvasState.redo();
|
||||||
this.incrementOperationCount();
|
this.incrementOperationCount();
|
||||||
this._notifyStateChange();
|
this._notifyStateChange();
|
||||||
|
|
||||||
log.debug('Redo completed, layers count:', this.layers.length);
|
log.debug('Redo completed, layers count:', this.layers.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,17 +246,17 @@ export class Canvas {
|
|||||||
*/
|
*/
|
||||||
removeSelectedLayers() {
|
removeSelectedLayers() {
|
||||||
if (this.selectedLayers.length > 0) {
|
if (this.selectedLayers.length > 0) {
|
||||||
log.info('Removing selected layers', {
|
log.info('Removing selected layers', {
|
||||||
layersToRemove: this.selectedLayers.length,
|
layersToRemove: this.selectedLayers.length,
|
||||||
totalLayers: this.layers.length
|
totalLayers: this.layers.length
|
||||||
});
|
});
|
||||||
|
|
||||||
this.saveState();
|
this.saveState();
|
||||||
this.layers = this.layers.filter(l => !this.selectedLayers.includes(l));
|
this.layers = this.layers.filter(l => !this.selectedLayers.includes(l));
|
||||||
this.updateSelection([]);
|
this.updateSelection([]);
|
||||||
this.render();
|
this.render();
|
||||||
this.saveState();
|
this.saveState();
|
||||||
|
|
||||||
log.debug('Layers removed successfully, remaining layers:', this.layers.length);
|
log.debug('Layers removed successfully, remaining layers:', this.layers.length);
|
||||||
} else {
|
} else {
|
||||||
log.debug('No layers selected for removal');
|
log.debug('No layers selected for removal');
|
||||||
@@ -271,13 +271,13 @@ export class Canvas {
|
|||||||
const previousSelection = this.selectedLayers.length;
|
const previousSelection = this.selectedLayers.length;
|
||||||
this.selectedLayers = newSelection || [];
|
this.selectedLayers = newSelection || [];
|
||||||
this.selectedLayer = this.selectedLayers.length > 0 ? this.selectedLayers[this.selectedLayers.length - 1] : null;
|
this.selectedLayer = this.selectedLayers.length > 0 ? this.selectedLayers[this.selectedLayers.length - 1] : null;
|
||||||
|
|
||||||
log.debug('Selection updated', {
|
log.debug('Selection updated', {
|
||||||
previousCount: previousSelection,
|
previousCount: previousSelection,
|
||||||
newCount: this.selectedLayers.length,
|
newCount: this.selectedLayers.length,
|
||||||
selectedLayerIds: this.selectedLayers.map(l => l.id || 'unknown')
|
selectedLayerIds: this.selectedLayers.map(l => l.id || 'unknown')
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.onSelectionChange) {
|
if (this.onSelectionChange) {
|
||||||
this.onSelectionChange();
|
this.onSelectionChange();
|
||||||
}
|
}
|
||||||
@@ -321,10 +321,10 @@ export class Canvas {
|
|||||||
* @param {boolean} sendCleanImage - Czy wysłać czysty obraz (bez maski) do editora
|
* @param {boolean} sendCleanImage - Czy wysłać czysty obraz (bez maski) do editora
|
||||||
*/
|
*/
|
||||||
async startMaskEditor(predefinedMask = null, sendCleanImage = true) {
|
async startMaskEditor(predefinedMask = null, sendCleanImage = true) {
|
||||||
log.info('Starting mask editor', {
|
log.info('Starting mask editor', {
|
||||||
hasPredefinedMask: !!predefinedMask,
|
hasPredefinedMask: !!predefinedMask,
|
||||||
sendCleanImage,
|
sendCleanImage,
|
||||||
layersCount: this.layers.length
|
layersCount: this.layers.length
|
||||||
});
|
});
|
||||||
|
|
||||||
this.savedMaskState = await this.saveMaskState();
|
this.savedMaskState = await this.saveMaskState();
|
||||||
@@ -683,7 +683,7 @@ export class Canvas {
|
|||||||
throw new Error("Old mask editor canvas not found");
|
throw new Error("Old mask editor canvas not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true });
|
const maskCtx = maskCanvas.getContext('2d', {willReadFrequently: true});
|
||||||
|
|
||||||
const maskColor = {r: 255, g: 255, b: 255};
|
const maskColor = {r: 255, g: 255, b: 255};
|
||||||
const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor);
|
const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor);
|
||||||
@@ -699,59 +699,58 @@ export class Canvas {
|
|||||||
* @param {number} targetHeight - Docelowa wysokość
|
* @param {number} targetHeight - Docelowa wysokość
|
||||||
* @param {Object} maskColor - Kolor maski {r, g, b}
|
* @param {Object} maskColor - Kolor maski {r, g, b}
|
||||||
* @returns {HTMLCanvasElement} Przetworzona maska
|
* @returns {HTMLCanvasElement} Przetworzona maska
|
||||||
*/
|
*/async processMaskForEditor(maskData, targetWidth, targetHeight, maskColor) {
|
||||||
async processMaskForEditor(maskData, targetWidth, targetHeight, maskColor) {
|
// Współrzędne przesunięcia (pan) widoku edytora
|
||||||
const originalWidth = maskData.width || maskData.naturalWidth || this.width;
|
const panX = this.maskTool.x;
|
||||||
const originalHeight = maskData.height || maskData.naturalHeight || this.height;
|
const panY = this.maskTool.y;
|
||||||
|
|
||||||
log.info("Processing mask for editor:", {
|
log.info("Processing mask for editor:", {
|
||||||
originalSize: {width: originalWidth, height: originalHeight},
|
sourceSize: {width: maskData.width, height: maskData.height},
|
||||||
targetSize: {width: targetWidth, height: targetHeight},
|
targetSize: {width: targetWidth, height: targetHeight},
|
||||||
canvasSize: {width: this.width, height: this.height}
|
viewportPan: {x: panX, y: panY}
|
||||||
});
|
});
|
||||||
|
|
||||||
const tempCanvas = document.createElement('canvas');
|
const tempCanvas = document.createElement('canvas');
|
||||||
tempCanvas.width = targetWidth;
|
tempCanvas.width = targetWidth;
|
||||||
tempCanvas.height = targetHeight;
|
tempCanvas.height = targetHeight;
|
||||||
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
|
const tempCtx = tempCanvas.getContext('2d', {willReadFrequently: true});
|
||||||
|
|
||||||
tempCtx.clearRect(0, 0, targetWidth, targetHeight);
|
const sourceX = -panX;
|
||||||
|
const sourceY = -panY;
|
||||||
|
|
||||||
|
tempCtx.drawImage(
|
||||||
|
maskData, // Źródło: pełna maska z "output area"
|
||||||
|
sourceX, // sx: Prawdziwa współrzędna X na dużej masce (np. 1000)
|
||||||
|
sourceY, // sy: Prawdziwa współrzędna Y na dużej masce (np. 1000)
|
||||||
|
targetWidth, // sWidth: Szerokość wycinanego fragmentu
|
||||||
|
targetHeight, // sHeight: Wysokość wycinanego fragmentu
|
||||||
|
0, // dx: Gdzie wkleić w płótnie docelowym (zawsze 0)
|
||||||
|
0, // dy: Gdzie wkleić w płótnie docelowym (zawsze 0)
|
||||||
|
targetWidth, // dWidth: Szerokość wklejanego obrazu
|
||||||
|
targetHeight // dHeight: Wysokość wklejanego obrazu
|
||||||
|
);
|
||||||
|
|
||||||
const scaleToOriginal = Math.min(originalWidth / this.width, originalHeight / this.height);
|
log.info("Mask viewport cropped correctly.", {
|
||||||
|
source: "maskData",
|
||||||
const scaledWidth = this.width * scaleToOriginal;
|
cropArea: {x: sourceX, y: sourceY, width: targetWidth, height: targetHeight}
|
||||||
const scaledHeight = this.height * scaleToOriginal;
|
|
||||||
|
|
||||||
const offsetX = (targetWidth - scaledWidth) / 2;
|
|
||||||
const offsetY = (targetHeight - scaledHeight) / 2;
|
|
||||||
|
|
||||||
tempCtx.drawImage(maskData, offsetX, offsetY, scaledWidth, scaledHeight);
|
|
||||||
|
|
||||||
log.info("Mask drawn scaled to original image size:", {
|
|
||||||
originalSize: {width: originalWidth, height: originalHeight},
|
|
||||||
targetSize: {width: targetWidth, height: targetHeight},
|
|
||||||
canvasSize: {width: this.width, height: this.height},
|
|
||||||
scaleToOriginal: scaleToOriginal,
|
|
||||||
finalSize: {width: scaledWidth, height: scaledHeight},
|
|
||||||
offset: {x: offsetX, y: offsetY}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reszta kodu (zmiana koloru) pozostaje bez zmian
|
||||||
const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight);
|
||||||
const data = imageData.data;
|
const data = imageData.data;
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
const alpha = data[i + 3]; // Oryginalny kanał alpha
|
const alpha = data[i + 3];
|
||||||
|
if (alpha > 0) {
|
||||||
data[i] = maskColor.r; // R
|
data[i] = maskColor.r;
|
||||||
data[i + 1] = maskColor.g; // G
|
data[i + 1] = maskColor.g;
|
||||||
data[i + 2] = maskColor.b; // B
|
data[i + 2] = maskColor.b;
|
||||||
data[i + 3] = alpha; // Zachowaj oryginalny alpha
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempCtx.putImageData(imageData, 0, 0);
|
tempCtx.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
log.info("Mask processing completed - full size scaling applied");
|
log.info("Mask processing completed - color applied.");
|
||||||
return tempCanvas;
|
return tempCanvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,6 +783,7 @@ export class Canvas {
|
|||||||
setTimeout(this.waitWhileMaskEditing.bind(this), 100);
|
setTimeout(this.waitWhileMaskEditing.bind(this), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zapisuje obecny stan maski przed otwarciem editora
|
* Zapisuje obecny stan maski przed otwarciem editora
|
||||||
* @returns {Object} Zapisany stan maski
|
* @returns {Object} Zapisany stan maski
|
||||||
@@ -797,7 +797,7 @@ export class Canvas {
|
|||||||
const savedCanvas = document.createElement('canvas');
|
const savedCanvas = document.createElement('canvas');
|
||||||
savedCanvas.width = maskCanvas.width;
|
savedCanvas.width = maskCanvas.width;
|
||||||
savedCanvas.height = maskCanvas.height;
|
savedCanvas.height = maskCanvas.height;
|
||||||
const savedCtx = savedCanvas.getContext('2d', { willReadFrequently: true });
|
const savedCtx = savedCanvas.getContext('2d', {willReadFrequently: true});
|
||||||
savedCtx.drawImage(maskCanvas, 0, 0);
|
savedCtx.drawImage(maskCanvas, 0, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -878,7 +878,7 @@ export class Canvas {
|
|||||||
resultImage.onload = resolve;
|
resultImage.onload = resolve;
|
||||||
resultImage.onerror = reject;
|
resultImage.onerror = reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
log.debug("Result image loaded successfully", {
|
log.debug("Result image loaded successfully", {
|
||||||
width: resultImage.width,
|
width: resultImage.width,
|
||||||
height: resultImage.height
|
height: resultImage.height
|
||||||
@@ -893,7 +893,7 @@ export class Canvas {
|
|||||||
const tempCanvas = document.createElement('canvas');
|
const tempCanvas = document.createElement('canvas');
|
||||||
tempCanvas.width = this.width;
|
tempCanvas.width = this.width;
|
||||||
tempCanvas.height = this.height;
|
tempCanvas.height = this.height;
|
||||||
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
|
const tempCtx = tempCanvas.getContext('2d', {willReadFrequently: true});
|
||||||
|
|
||||||
tempCtx.drawImage(resultImage, 0, 0, this.width, this.height);
|
tempCtx.drawImage(resultImage, 0, 0, this.width, this.height);
|
||||||
|
|
||||||
@@ -920,7 +920,7 @@ export class Canvas {
|
|||||||
const destX = -this.maskTool.x;
|
const destX = -this.maskTool.x;
|
||||||
const destY = -this.maskTool.y;
|
const destY = -this.maskTool.y;
|
||||||
|
|
||||||
log.debug("Applying mask to canvas", { destX, destY });
|
log.debug("Applying mask to canvas", {destX, destY});
|
||||||
|
|
||||||
maskCtx.globalCompositeOperation = 'source-over';
|
maskCtx.globalCompositeOperation = 'source-over';
|
||||||
maskCtx.clearRect(destX, destY, this.width, this.height);
|
maskCtx.clearRect(destX, destY, this.width, this.height);
|
||||||
|
|||||||
Reference in New Issue
Block a user