fix(ui): caching broke layer rendering

This commit is contained in:
psychedelicious 2024-04-12 18:39:40 +10:00 committed by Kent Keirsey
parent de7ecc8e3e
commit cf4c1750cb
2 changed files with 28 additions and 7 deletions

View File

@ -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<KonvaNodeConfigType>) => boolean
filterChildren?: (item: KonvaNodeType<KonvaNodeConfigType>) => 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);

View File

@ -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<Record<string, Blob>> => {
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<Blob>((resolve) => {