diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx
index c0c221c099..a3bbcdb875 100644
--- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx
@@ -26,7 +26,7 @@ export const LayerColorPicker = ({ id }: Props) => {
-
+
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx
index 030df4943f..4a21abcbfe 100644
--- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx
@@ -5,7 +5,13 @@ import { LayerBoundingBox } from 'features/regionalPrompts/components/LayerBound
import { LineComponent } from 'features/regionalPrompts/components/LineComponent';
import { RectComponent } from 'features/regionalPrompts/components/RectComponent';
import { useLayer } from 'features/regionalPrompts/hooks/layerStateHooks';
-import { $stage, layerBboxChanged, layerTranslated } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import {
+ $stage,
+ layerBboxChanged,
+ layerTranslated,
+ REGIONAL_PROMPT_LAYER_NAME,
+ REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME,
+} from 'features/regionalPrompts/store/regionalPromptsSlice';
import { getKonvaLayerBbox } from 'features/regionalPrompts/util/bbox';
import type { Group as KonvaGroupType } from 'konva/lib/Group';
import type { Layer as KonvaLayerType } from 'konva/lib/Layer';
@@ -19,7 +25,8 @@ type Props = {
id: string;
};
-const filterChildren = (item: KonvaNodeType) => item.name() !== 'regionalPromptLayerObjectGroup';
+export const selectPromptLayerObjectGroup = (item: KonvaNodeType) =>
+ item.name() !== REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME;
export const LayerComponent: React.FC = ({ id }) => {
const dispatch = useAppDispatch();
@@ -74,7 +81,7 @@ export const LayerComponent: React.FC = ({ id }) => {
onChangeBbox(null);
return;
}
- onChangeBbox(getKonvaLayerBbox(layerRef.current, filterChildren));
+ onChangeBbox(getKonvaLayerBbox(layerRef.current, selectPromptLayerObjectGroup));
}, [tool, layer.objects, onChangeBbox]);
if (!layer.isVisible) {
@@ -85,8 +92,8 @@ export const LayerComponent: React.FC = ({ id }) => {
<>
= ({ id }) => {
>
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx
index b39ab6587b..e161a4780f 100644
--- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx
@@ -8,19 +8,23 @@ import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem
import { RegionalPromptsStage } from 'features/regionalPrompts/components/RegionalPromptsStage';
import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser';
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
-import { getLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
+import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs';
import { ImageSizeLinear } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSizeLinear';
const selectLayerIdsReversed = createSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
regionalPrompts.layers.map((l) => l.id).reverse()
);
+const debugBlobs = () => {
+ getRegionalPromptLayerBlobs(true);
+};
+
export const RegionalPromptsEditor = () => {
const layerIdsReversed = useAppSelector(selectLayerIdsReversed);
return (
-
+
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts
index faafffa936..a3b1025a12 100644
--- a/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts
+++ b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts
@@ -270,3 +270,5 @@ export const getStage = (): Konva.Stage => {
assert(stage);
return stage;
};
+export const REGIONAL_PROMPT_LAYER_NAME = 'regionalPromptLayer';
+export const REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME = 'regionalPromptLayerObjectGroup';
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts b/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts
index a491de6a59..c7e9f324dc 100644
--- a/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts
+++ b/invokeai/frontend/web/src/features/regionalPrompts/util/getLayerBlobs.ts
@@ -1,26 +1,51 @@
import { getStore } from 'app/store/nanostores/store';
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
-import { $stage } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import { selectPromptLayerObjectGroup } from 'features/regionalPrompts/components/LayerComponent';
+import { $stage, REGIONAL_PROMPT_LAYER_NAME } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import Konva from 'konva';
import { assert } from 'tsafe';
-export const getLayerBlobs = async () => {
+export const getRegionalPromptLayerBlobs = async (preview: boolean = false): Promise> => {
const state = getStore().getState();
const stage = $stage.get();
assert(stage !== null, 'Stage is null');
- const stageLayers = stage.getLayers().filter((l) => l.name() === 'regionalPromptLayer');
- for (const layer of stageLayers) {
+ const regionalPromptLayers = stage.getLayers().filter((l) => l.name() === REGIONAL_PROMPT_LAYER_NAME);
+
+ // We need to reconstruct each layer to only output the desired data. This logic mirrors the logic in
+ // `getKonvaLayerBbox()` in `invokeai/frontend/web/src/features/regionalPrompts/util/bbox.ts`
+ const offscreenStageContainer = document.createElement('div');
+ const offscreenStage = new Konva.Stage({
+ container: offscreenStageContainer,
+ width: stage.width(),
+ height: stage.height(),
+ });
+
+ const blobs: Record = {};
+
+ for (const layer of regionalPromptLayers) {
+ const layerClone = layer.clone();
+ for (const child of layerClone.getChildren(selectPromptLayerObjectGroup)) {
+ child.destroy();
+ }
+ offscreenStage.add(layerClone);
const blob = await new Promise((resolve) => {
- layer.toBlob({
+ offscreenStage.toBlob({
callback: (blob) => {
assert(blob, 'Blob is null');
resolve(blob);
},
});
});
- const base64 = await blobToDataURL(blob);
- const prompt = state.regionalPrompts.layers.find((l) => l.id === layer.id())?.prompt;
- assert(prompt !== undefined, 'Prompt is undefined');
- openBase64ImageInTab([{ base64, caption: prompt }]);
+ blobs[layer.id()] = blob;
+
+ if (preview) {
+ const base64 = await blobToDataURL(blob);
+ const prompt = state.regionalPrompts.layers.find((l) => l.id === layer.id())?.prompt;
+ openBase64ImageInTab([{ base64, caption: prompt ?? '' }]);
+ }
+ layerClone.destroy();
}
+
+ return blobs;
};