mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): fix renderer stuff
This commit is contained in:
parent
426f1b6f9a
commit
04a44c8ea7
@ -1,13 +1,13 @@
|
|||||||
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
||||||
import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL';
|
import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||||
import {
|
import {
|
||||||
|
CA_LAYER_IMAGE_NAME,
|
||||||
LAYER_BBOX_NAME,
|
LAYER_BBOX_NAME,
|
||||||
RASTER_LAYER_OBJECT_GROUP_NAME,
|
RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||||
RG_LAYER_OBJECT_GROUP_NAME,
|
RG_LAYER_OBJECT_GROUP_NAME,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
||||||
import type { LayerData } from 'features/controlLayers/store/types';
|
import type { ControlAdapterData, LayerData, RegionalGuidanceData } from 'features/controlLayers/store/types';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
@ -175,37 +175,52 @@ export const getLayerBboxFast = (layer: Konva.Layer): IRect => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const filterRGChildren = (node: Konva.Node): boolean => node.name() === RG_LAYER_OBJECT_GROUP_NAME;
|
const filterRGChildren = (node: Konva.Node): boolean => node.name() === RG_LAYER_OBJECT_GROUP_NAME;
|
||||||
const filterRasterChildren = (node: Konva.Node): boolean => node.name() === RASTER_LAYER_OBJECT_GROUP_NAME;
|
const filterLayerChildren = (node: Konva.Node): boolean => node.name() === RASTER_LAYER_OBJECT_GROUP_NAME;
|
||||||
|
const filterCAChildren = (node: Konva.Node): boolean => node.name() === CA_LAYER_IMAGE_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the bbox of each regional guidance layer. Only calculates if the mask has changed.
|
* Calculates the bbox of each regional guidance layer. Only calculates if the mask has changed.
|
||||||
* @param stage The konva stage
|
* @param stage The konva stage
|
||||||
* @param layerStates An array of layers to calculate bboxes for
|
* @param entityStates An array of layers to calculate bboxes for
|
||||||
* @param onBboxChanged Callback for when the bounding box changes
|
* @param onBboxChanged Callback for when the bounding box changes
|
||||||
*/
|
*/
|
||||||
export const updateBboxes = (
|
export const updateBboxes = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
layerStates: LayerData[],
|
entityStates: (ControlAdapterData | LayerData | RegionalGuidanceData)[],
|
||||||
onBboxChanged: (layerId: string, bbox: IRect | null) => void
|
onBboxChanged: (layerId: string, bbox: IRect | null) => void
|
||||||
): void => {
|
): void => {
|
||||||
for (const layerState of layerStates) {
|
for (const entityState of entityStates) {
|
||||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${layerState.id}`);
|
const konvaLayer = stage.findOne<Konva.Layer>(`#${entityState.id}`);
|
||||||
assert(konvaLayer, `Layer ${layerState.id} not found in stage`);
|
assert(konvaLayer, `Layer ${entityState.id} not found in stage`);
|
||||||
// We only need to recalculate the bbox if the layer has changed
|
// We only need to recalculate the bbox if the layer has changed
|
||||||
if (layerState.bboxNeedsUpdate) {
|
if (entityState.bboxNeedsUpdate) {
|
||||||
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layerState, konvaLayer);
|
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(entityState, konvaLayer);
|
||||||
|
|
||||||
// Hide the bbox while we calculate the new bbox, else the bbox will be included in the calculation
|
// Hide the bbox while we calculate the new bbox, else the bbox will be included in the calculation
|
||||||
const visible = bboxRect.visible();
|
const visible = bboxRect.visible();
|
||||||
bboxRect.visible(false);
|
bboxRect.visible(false);
|
||||||
|
|
||||||
if (layerState.objects.length === 0) {
|
if (entityState.type === 'layer') {
|
||||||
|
if (entityState.objects.length === 0) {
|
||||||
// No objects - no bbox to calculate
|
// No objects - no bbox to calculate
|
||||||
onBboxChanged(layerState.id, null);
|
onBboxChanged(entityState.id, null);
|
||||||
} else {
|
} else {
|
||||||
// Calculate the bbox by rendering the layer and checking its pixels
|
onBboxChanged(entityState.id, getLayerBboxPixels(konvaLayer, filterLayerChildren));
|
||||||
const filterChildren = isRegionalGuidanceLayer(layerState) ? filterRGChildren : filterRasterChildren;
|
}
|
||||||
onBboxChanged(layerState.id, getLayerBboxPixels(konvaLayer, filterChildren));
|
} else if (entityState.type === 'control_adapter') {
|
||||||
|
if (!entityState.image && !entityState.processedImage) {
|
||||||
|
// No objects - no bbox to calculate
|
||||||
|
onBboxChanged(entityState.id, null);
|
||||||
|
} else {
|
||||||
|
onBboxChanged(entityState.id, getLayerBboxPixels(konvaLayer, filterCAChildren));
|
||||||
|
}
|
||||||
|
} else if (entityState.type === 'regional_guidance') {
|
||||||
|
if (entityState.objects.length === 0) {
|
||||||
|
// No objects - no bbox to calculate
|
||||||
|
onBboxChanged(entityState.id, null);
|
||||||
|
} else {
|
||||||
|
onBboxChanged(entityState.id, getLayerBboxPixels(konvaLayer, filterRGChildren));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore the visibility of the bbox
|
// Restore the visibility of the bbox
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
import {
|
|
||||||
getCALayerImageId,
|
|
||||||
getIILayerImageId,
|
|
||||||
INITIAL_IMAGE_LAYER_IMAGE_NAME,
|
|
||||||
INITIAL_IMAGE_LAYER_NAME,
|
|
||||||
} from 'features/controlLayers/konva/naming';
|
|
||||||
import type { InitialImageLayer } from 'features/controlLayers/store/types';
|
|
||||||
import Konva from 'konva';
|
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logic for creating and rendering initial image layers. Well, just the one, actually, because it's a singleton.
|
|
||||||
* TODO(psyche): Raster layers effectively supersede the initial image layer type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an initial image konva layer.
|
|
||||||
* @param stage The konva stage
|
|
||||||
* @param layerState The initial image layer state
|
|
||||||
*/
|
|
||||||
const createIILayer = (stage: Konva.Stage, layerState: InitialImageLayer): Konva.Layer => {
|
|
||||||
const konvaLayer = new Konva.Layer({
|
|
||||||
id: layerState.id,
|
|
||||||
name: INITIAL_IMAGE_LAYER_NAME,
|
|
||||||
imageSmoothingEnabled: true,
|
|
||||||
listening: false,
|
|
||||||
});
|
|
||||||
stage.add(konvaLayer);
|
|
||||||
return konvaLayer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the konva image for an initial image layer.
|
|
||||||
* @param konvaLayer The konva layer
|
|
||||||
* @param imageEl The image element
|
|
||||||
*/
|
|
||||||
const createIILayerImage = (konvaLayer: Konva.Layer, imageEl: HTMLImageElement): Konva.Image => {
|
|
||||||
const konvaImage = new Konva.Image({
|
|
||||||
name: INITIAL_IMAGE_LAYER_IMAGE_NAME,
|
|
||||||
image: imageEl,
|
|
||||||
});
|
|
||||||
konvaLayer.add(konvaImage);
|
|
||||||
return konvaImage;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an initial image layer's attributes (width, height, opacity, visibility).
|
|
||||||
* @param stage The konva stage
|
|
||||||
* @param konvaImage The konva image
|
|
||||||
* @param layerState The initial image layer state
|
|
||||||
*/
|
|
||||||
const updateIILayerImageAttrs = (stage: Konva.Stage, konvaImage: Konva.Image, layerState: InitialImageLayer): void => {
|
|
||||||
// Konva erroneously reports NaN for width and height when the stage is hidden. This causes errors when caching,
|
|
||||||
// but it doesn't seem to break anything.
|
|
||||||
// TODO(psyche): Investigate and report upstream.
|
|
||||||
const newWidth = stage.width() / stage.scaleX();
|
|
||||||
const newHeight = stage.height() / stage.scaleY();
|
|
||||||
if (
|
|
||||||
konvaImage.width() !== newWidth ||
|
|
||||||
konvaImage.height() !== newHeight ||
|
|
||||||
konvaImage.visible() !== layerState.isEnabled
|
|
||||||
) {
|
|
||||||
konvaImage.setAttrs({
|
|
||||||
opacity: layerState.opacity,
|
|
||||||
scaleX: 1,
|
|
||||||
scaleY: 1,
|
|
||||||
width: stage.width() / stage.scaleX(),
|
|
||||||
height: stage.height() / stage.scaleY(),
|
|
||||||
visible: layerState.isEnabled,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (konvaImage.opacity() !== layerState.opacity) {
|
|
||||||
konvaImage.opacity(layerState.opacity);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an initial image layer's image source when the image changes.
|
|
||||||
* @param stage The konva stage
|
|
||||||
* @param konvaLayer The konva layer
|
|
||||||
* @param layerState The initial image layer state
|
|
||||||
* @param getImageDTO A function to retrieve an image DTO from the server, used to update the image source
|
|
||||||
*/
|
|
||||||
const updateIILayerImageSource = async (
|
|
||||||
stage: Konva.Stage,
|
|
||||||
konvaLayer: Konva.Layer,
|
|
||||||
layerState: InitialImageLayer,
|
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
|
||||||
): Promise<void> => {
|
|
||||||
if (layerState.image) {
|
|
||||||
const imageName = layerState.image.name;
|
|
||||||
const imageDTO = await getImageDTO(imageName);
|
|
||||||
if (!imageDTO) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const imageEl = new Image();
|
|
||||||
const imageId = getIILayerImageId(layerState.id, imageName);
|
|
||||||
imageEl.onload = () => {
|
|
||||||
// Find the existing image or create a new one - must find using the name, bc the id may have just changed
|
|
||||||
const konvaImage =
|
|
||||||
konvaLayer.findOne<Konva.Image>(`.${INITIAL_IMAGE_LAYER_IMAGE_NAME}`) ??
|
|
||||||
createIILayerImage(konvaLayer, imageEl);
|
|
||||||
|
|
||||||
// Update the image's attributes
|
|
||||||
konvaImage.setAttrs({
|
|
||||||
id: imageId,
|
|
||||||
image: imageEl,
|
|
||||||
});
|
|
||||||
updateIILayerImageAttrs(stage, konvaImage, layerState);
|
|
||||||
imageEl.id = imageId;
|
|
||||||
};
|
|
||||||
imageEl.src = imageDTO.image_url;
|
|
||||||
} else {
|
|
||||||
konvaLayer.findOne(`.${INITIAL_IMAGE_LAYER_IMAGE_NAME}`)?.destroy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an initial image layer.
|
|
||||||
* @param stage The konva stage
|
|
||||||
* @param layerState The initial image layer state
|
|
||||||
* @param getImageDTO A function to retrieve an image DTO from the server, used to update the image source
|
|
||||||
*/
|
|
||||||
export const renderIILayer = (
|
|
||||||
stage: Konva.Stage,
|
|
||||||
layerState: InitialImageLayer,
|
|
||||||
zIndex: number,
|
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
|
||||||
): void => {
|
|
||||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${layerState.id}`) ?? createIILayer(stage, layerState);
|
|
||||||
|
|
||||||
konvaLayer.zIndex(zIndex);
|
|
||||||
|
|
||||||
const konvaImage = konvaLayer.findOne<Konva.Image>(`.${INITIAL_IMAGE_LAYER_IMAGE_NAME}`);
|
|
||||||
const canvasImageSource = konvaImage?.image();
|
|
||||||
|
|
||||||
let imageSourceNeedsUpdate = false;
|
|
||||||
|
|
||||||
if (canvasImageSource instanceof HTMLImageElement) {
|
|
||||||
const image = layerState.image;
|
|
||||||
if (image && canvasImageSource.id !== getCALayerImageId(layerState.id, image.name)) {
|
|
||||||
imageSourceNeedsUpdate = true;
|
|
||||||
} else if (!image) {
|
|
||||||
imageSourceNeedsUpdate = true;
|
|
||||||
}
|
|
||||||
} else if (!canvasImageSource) {
|
|
||||||
imageSourceNeedsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageSourceNeedsUpdate) {
|
|
||||||
updateIILayerImageSource(stage, konvaLayer, layerState, getImageDTO);
|
|
||||||
} else if (konvaImage) {
|
|
||||||
updateIILayerImageAttrs(stage, konvaImage, layerState);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user