From a402d02b005d47c4dd4e4253eae21707b403f997 Mon Sep 17 00:00:00 2001 From: CMonk Date: Sat, 27 Apr 2024 00:00:46 +0800 Subject: [PATCH] Add support for sending folder layer Photoshop plugin will first clone and merge the selected folder layer, send, then delete the cloned and merged layer, all in one modal execution. --- comfy/__init__.py | 27 +++++++------- photoshop/dist/index.js | 49 +++++++++++++++++++++---- photoshop/src/system/ComfyConnection.js | 48 ++++++++++++++++++++---- 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/comfy/__init__.py b/comfy/__init__.py index efef3c9..bbe2a30 100644 --- a/comfy/__init__.py +++ b/comfy/__init__.py @@ -127,19 +127,20 @@ class PhotoshopInstance: def update_comfyui_last_value(self, layer, use_layer_bounds, value): layer_bounds_combo = f"{layer}{use_layer_bounds}" self.comfyui_last_value_tracker[layer_bounds_combo] = value - + + # need to update history id after internal operation otherwise it might cause infinite change loop + async def update_history_state_id_after_internal_change(self, history_state_id=None): + if history_state_id is None: # need to get new id after operation, it causes history change + history_state_id = await self.get_active_history_state_id() + if self.get_img_state_id is not None: # udpate all the change trackers with the new history state id + self.change_tracker = {k: history_state_id for k, v in self.change_tracker.items() if v == self.get_img_state_id} + self.get_img_state_id = history_state_id # it gets into a loop if get img state is not updated + async def update_layer_bounds_history_state_id(self, layer, use_layer_bounds): layer_bounds_combo = f"{layer}{use_layer_bounds}" - latest_state_id = self.get_push_history_state_id() or await self.get_active_history_state_id() + latest_state_id = await self.get_active_history_state_id() self.change_tracker[layer_bounds_combo] = latest_state_id return latest_state_id - - async def update_history_state_id_after_send(self): - old_history_state_id = self.get_img_state_id - history_state_id = await self.get_active_history_state_id() # need to get new id after operation, it causes history change - self.get_img_state_id = history_state_id # it gets into a loop if get img state is not updated - if old_history_state_id is not None: # udpate all the change trackers with the new history state id - self.change_tracker = {k: history_state_id for k, v in self.change_tracker.items() if v == old_history_state_id} async def get_layers(self): result = await self.wsCallsManager.call('get_layers', {}) @@ -151,10 +152,10 @@ class PhotoshopInstance: if not is_changed and self.last_get_img_id is not None: return self.last_get_img_id result = await self.wsCallsManager.call('get_image', {'layer_id': layer_id, 'use_layer_bounds': bounds_id}, timeout=60) - id = result['upload_name'] - layer_opacity = result['layer_opacity'] history_state_id = await self.update_layer_bounds_history_state_id(layer_id, bounds_id) - self.get_img_state_id = history_state_id + await self.update_history_state_id_after_internal_change(history_state_id) + layer_opacity = result['layer_opacity'] + id = result['upload_name'] self.last_get_img_id = id return id, layer_opacity @@ -167,7 +168,7 @@ class PhotoshopInstance: async def send_images(self, image_ids, layer_name=""): result = await self.wsCallsManager.call('send_images', {'image_ids': image_ids, 'layer_name': layer_name}) - await self.update_history_state_id_after_send() + await self.update_history_state_id_after_internal_change() return result def get_push_history_state_id(self): diff --git a/photoshop/dist/index.js b/photoshop/dist/index.js index c03a890..82bafd7 100644 --- a/photoshop/dist/index.js +++ b/photoshop/dist/index.js @@ -267,7 +267,7 @@ async function getPixelsData(layer, desireBounds) { return await getPixelsDataHelper(null, desireBounds); } // normal layer - return getPixelsDataHelper(layer, desireBounds); + return await getPixelsDataHelper(layer, desireBounds); } // ps returns trimmed data so need padding @@ -286,6 +286,22 @@ function padAndTrimLayerDataToDesireBounds(layer, pixelDataFromAPI, desireBounds (0,_util__WEBPACK_IMPORTED_MODULE_2__.unTrimImageData)(pixelDataFromAPI, pixelDataForReturn, bounds, desireBounds); return pixelDataForReturn; } +function isLayerFolder(layer) { + return layer.layers && layer.layers.length > 0; +} +async function findLayer(layerID) { + let layer; + let isFolder = false; + if (layerID <= 0) return [layer, isFolder]; + layer = (0,_util__WEBPACK_IMPORTED_MODULE_2__.findInAllSubLayer)(photoshop__WEBPACK_IMPORTED_MODULE_0__.app.activeDocument, layerID); + if (!layer) throw new Error(`Layer(id: ${layerID}) not found`); + if (!isLayerFolder(layer)) return [layer, isFolder]; + // layer is folder + const dupLayer = await layer.duplicate(); + const mergedLayer = await dupLayer.merge(); + isFolder = true; + return [mergedLayer, isFolder]; +} class ComfyConnection { static instance = null; static _connectStateCallbacks = []; @@ -423,15 +439,23 @@ class ComfyConnection { } else if (payload.action == 'get_image') { const layerID = payload.params.layer_id; const boundsLayerID = payload.params.use_layer_bounds; - await executeAsModalUntilSuccess(async () => { + await executeAsModalUntilSuccess(async executionContext => { + let hostControl; + let suspensionID; const startTime = Date.now(); let uploadName = 0; + let layer; + let layerOpacity = 100; + let isFolder = false; + const activeLayers = photoshop__WEBPACK_IMPORTED_MODULE_0__.app.activeDocument.activeLayers; try { - let layer; - if (layerID > 0) { - layer = (0,_util__WEBPACK_IMPORTED_MODULE_2__.findInAllSubLayer)(photoshop__WEBPACK_IMPORTED_MODULE_0__.app.activeDocument, layerID); - if (!layer) throw new Error(`Layer(id: ${layerID}) not found`); - } + hostControl = executionContext.hostControl; + suspensionID = await hostControl.suspendHistory({ + "documentID": photoshop__WEBPACK_IMPORTED_MODULE_0__.app.activeDocument.id, + "name": "Image To ComfyUI" + }); + [layer, isFolder] = await findLayer(layerID); + layerOpacity = layer?.opacity ?? 100; const desireBounds = getDesiredBounds(boundsLayerID); const pixelDataFromAPI = await getPixelsData(layer, desireBounds); const pixelDataForReturn = padAndTrimLayerDataToDesireBounds(layer, pixelDataFromAPI, desireBounds); @@ -476,11 +500,20 @@ class ComfyConnection { error: e.message })); throw e; + } finally { + if (layer && isFolder) layer.delete(); + if (activeLayers && activeLayers.length > 0) { + for (let i = 0; i < activeLayers.length; i++) { + activeLayers[i].selected = true; + } + } + if (hostControl && suspensionID) await hostControl.resumeHistory(suspensionID); } this.socket.send(JSON.stringify({ call_id: payload.call_id, result: { - upload_name: uploadName + upload_name: uploadName, + layer_opacity: layerOpacity } })); }, { diff --git a/photoshop/src/system/ComfyConnection.js b/photoshop/src/system/ComfyConnection.js index 19aa449..2c60979 100644 --- a/photoshop/src/system/ComfyConnection.js +++ b/photoshop/src/system/ComfyConnection.js @@ -93,7 +93,7 @@ async function getPixelsData(layer, desireBounds) { return await getPixelsDataHelper(null, desireBounds); } // normal layer - return getPixelsDataHelper(layer, desireBounds); + return await getPixelsDataHelper(layer, desireBounds); } // ps returns trimmed data so need padding @@ -118,6 +118,24 @@ function padAndTrimLayerDataToDesireBounds(layer, pixelDataFromAPI, desireBounds return pixelDataForReturn; } +function isLayerFolder(layer){ + return layer.layers && layer.layers.length > 0; +} + +async function findLayer(layerID) { + let layer; + let isFolder = false; + if (layerID <= 0) return [layer, isFolder]; + layer = findInAllSubLayer(app.activeDocument, layerID) + if (!layer) throw new Error(`Layer(id: ${layerID}) not found`); + if (!isLayerFolder(layer)) return [layer, isFolder]; + // layer is folder + const dupLayer = await layer.duplicate(); + const mergedLayer = await dupLayer.merge() + isFolder = true; + return [mergedLayer, isFolder]; +} + class ComfyConnection { static instance = null; @@ -272,15 +290,23 @@ class ComfyConnection { const layerID = payload.params.layer_id const boundsLayerID = payload.params.use_layer_bounds - await executeAsModalUntilSuccess(async () => { + await executeAsModalUntilSuccess(async (executionContext) => { + let hostControl; + let suspensionID; const startTime = Date.now(); let uploadName = 0; let layer; + let layerOpacity = 100; + let isFolder = false; + const activeLayers = app.activeDocument.activeLayers; try { - if (layerID > 0) { - layer = findInAllSubLayer(app.activeDocument, layerID) - if (!layer) throw new Error(`Layer(id: ${layerID}) not found`); - } + hostControl = executionContext.hostControl; + suspensionID = await hostControl.suspendHistory({ + "documentID": app.activeDocument.id, + "name": "Image To ComfyUI" + }); + [layer, isFolder] = await findLayer(layerID); + layerOpacity = layer?.opacity ?? 100; const desireBounds = getDesiredBounds(boundsLayerID); const pixelDataFromAPI = await getPixelsData(layer, desireBounds) const pixelDataForReturn = padAndTrimLayerDataToDesireBounds(layer, pixelDataFromAPI, desireBounds) @@ -329,6 +355,14 @@ class ComfyConnection { }) ) throw e + } finally { + if (layer && isFolder) layer.delete(); + if (activeLayers && activeLayers.length > 0) { + for (let i = 0; i < activeLayers.length; i++) { + activeLayers[i].selected = true; + } + } + if (hostControl && suspensionID) await hostControl.resumeHistory(suspensionID); } this.socket.send( @@ -336,7 +370,7 @@ class ComfyConnection { call_id: payload.call_id, result: { upload_name: uploadName, - layer_opacity: layer?.opacity ?? 100 + layer_opacity: layerOpacity, } }) )