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 { imageDataToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||
import {
|
||||
CA_LAYER_IMAGE_NAME,
|
||||
LAYER_BBOX_NAME,
|
||||
RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||
RG_LAYER_OBJECT_GROUP_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
||||
import type { LayerData } from 'features/controlLayers/store/types';
|
||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||
import type { ControlAdapterData, LayerData, RegionalGuidanceData } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { IRect } from 'konva/lib/types';
|
||||
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 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.
|
||||
* @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
|
||||
*/
|
||||
export const updateBboxes = (
|
||||
stage: Konva.Stage,
|
||||
layerStates: LayerData[],
|
||||
entityStates: (ControlAdapterData | LayerData | RegionalGuidanceData)[],
|
||||
onBboxChanged: (layerId: string, bbox: IRect | null) => void
|
||||
): void => {
|
||||
for (const layerState of layerStates) {
|
||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${layerState.id}`);
|
||||
assert(konvaLayer, `Layer ${layerState.id} not found in stage`);
|
||||
for (const entityState of entityStates) {
|
||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${entityState.id}`);
|
||||
assert(konvaLayer, `Layer ${entityState.id} not found in stage`);
|
||||
// We only need to recalculate the bbox if the layer has changed
|
||||
if (layerState.bboxNeedsUpdate) {
|
||||
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layerState, konvaLayer);
|
||||
if (entityState.bboxNeedsUpdate) {
|
||||
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
|
||||
const visible = bboxRect.visible();
|
||||
bboxRect.visible(false);
|
||||
|
||||
if (layerState.objects.length === 0) {
|
||||
// No objects - no bbox to calculate
|
||||
onBboxChanged(layerState.id, null);
|
||||
} else {
|
||||
// Calculate the bbox by rendering the layer and checking its pixels
|
||||
const filterChildren = isRegionalGuidanceLayer(layerState) ? filterRGChildren : filterRasterChildren;
|
||||
onBboxChanged(layerState.id, getLayerBboxPixels(konvaLayer, filterChildren));
|
||||
if (entityState.type === 'layer') {
|
||||
if (entityState.objects.length === 0) {
|
||||
// No objects - no bbox to calculate
|
||||
onBboxChanged(entityState.id, null);
|
||||
} else {
|
||||
onBboxChanged(entityState.id, getLayerBboxPixels(konvaLayer, filterLayerChildren));
|
||||
}
|
||||
} 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
|
||||
|
@ -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