diff --git a/invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts b/invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts index eceac762a3..6536b3161b 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts +++ b/invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts @@ -1,3 +1,5 @@ +import openBase64ImageInTab from 'common/util/openBase64ImageInTab'; +import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL'; import Konva from 'konva'; import type { Layer as KonvaLayerType } from 'konva/lib/Layer'; import type { Node as KonvaNodeType, NodeConfig as KonvaNodeConfigType } from 'konva/lib/Node'; @@ -35,10 +37,12 @@ export const getImageDataBbox = (imageData: ImageData) => { * Get the bounding box of a regional prompt konva layer. This function has special handling for regional prompt layers. * @param layer The konva layer to get the bounding box of. * @param filterChildren Optional filter function to exclude certain children from the bounding box calculation. Defaults to including all children. + * @param preview Whether to open a new tab displaying the rendered layer, which is used to calculate the bbox. */ export const getKonvaLayerBbox = ( layer: KonvaLayerType, - filterChildren?: (item: KonvaNodeType) => boolean + filterChildren?: (item: KonvaNodeType) => boolean, + preview: boolean = false ): IRect => { // To calculate the layer's bounding box, we must first render it to a pixel array, then do some math. // We can't use konva's `layer.getClientRect()`, because this includes all shapes, not just visible ones. @@ -57,14 +61,17 @@ export const getKonvaLayerBbox = ( // TODO: Would be more efficient to create a totally new layer and add only the children we want, but possibly less // accurate, as we wouldn't get the original layer's config and such. const layerClone = layer.clone(); - if (filterChildren) { - for (const child of layerClone.getChildren(filterChildren)) { + offscreenStage.add(layerClone); + + for (const child of layerClone.getChildren()) { + if (filterChildren && filterChildren(child)) { child.destroy(); + } else { + // We need to re-cache to handle children with transparency and multiple objects - like prompt region layers. + child.cache(); } } - offscreenStage.add(layerClone.clone()); - // Get the layer's image data, ensuring we capture an area large enough to include the full layer, including any // portions that are outside the current stage bounds. const layerRect = layerClone.getClientRect(); @@ -82,6 +89,10 @@ export const getKonvaLayerBbox = ( ?.getImageData(0, 0, width, height); assert(layerImageData, "Unable to get layer's image data"); + if (preview) { + openBase64ImageInTab([{ base64: imageDataToDataURL(layerImageData), caption: layer.id() }]); + } + // Calculate the layer's bounding box. const layerBbox = getImageDataBbox(layerImageData); diff --git a/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts b/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts index e8af53bf55..3bec845da9 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts +++ b/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts @@ -6,6 +6,11 @@ import { $stage, REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/sto import Konva from 'konva'; import { assert } from 'tsafe'; +/** + * Get the blobs of all regional prompt layers. + * @param preview Whether to open a new tab displaying each layer. + * @returns A map of layer IDs to blobs. + */ export const getRegionalPromptLayerBlobs = async (preview: boolean = false): Promise> => { const state = getStore().getState(); const stage = $stage.get(); @@ -27,8 +32,13 @@ export const getRegionalPromptLayerBlobs = async (preview: boolean = false): Pro for (const layer of regionalPromptLayers) { const layerClone = layer.clone(); - for (const child of layerClone.getChildren(selectPromptLayerObjectGroup)) { - child.destroy(); + for (const child of layerClone.getChildren()) { + if (selectPromptLayerObjectGroup(child)) { + child.destroy(); + } else { + // We need to re-cache to handle children with transparency and multiple objects - like prompt region layers. + child.cache(); + } } offscreenStage.add(layerClone); const blob = await new Promise((resolve) => {