mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): de-jank staging area and progress images
This commit is contained in:
parent
ef4d6c26f6
commit
af815cf7eb
@ -1,6 +1,7 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import {
|
import {
|
||||||
|
$lastProgressEvent,
|
||||||
layerAddedFromStagingArea,
|
layerAddedFromStagingArea,
|
||||||
sessionStagingAreaImageAccepted,
|
sessionStagingAreaImageAccepted,
|
||||||
sessionStagingAreaReset,
|
sessionStagingAreaReset,
|
||||||
@ -25,6 +26,9 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
|
|||||||
);
|
);
|
||||||
const { canceled } = await req.unwrap();
|
const { canceled } = await req.unwrap();
|
||||||
req.reset();
|
req.reset();
|
||||||
|
|
||||||
|
$lastProgressEvent.set(null);
|
||||||
|
|
||||||
if (canceled > 0) {
|
if (canceled > 0) {
|
||||||
log.debug(`Canceled ${canceled} canvas batches`);
|
log.debug(`Canceled ${canceled} canvas batches`);
|
||||||
toast({
|
toast({
|
||||||
|
@ -76,11 +76,8 @@ export class CanvasControlAdapter {
|
|||||||
didDraw = true;
|
didDraw = true;
|
||||||
}
|
}
|
||||||
} else if (!this.image) {
|
} else if (!this.image) {
|
||||||
this.image = await new CanvasImage(imageObject, {
|
this.image = await new CanvasImage(imageObject);
|
||||||
onLoad: () => {
|
this.updateGroup(true);
|
||||||
this.updateGroup(true);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.objectsGroup.add(this.image.konvaImageGroup);
|
this.objectsGroup.add(this.image.konvaImageGroup);
|
||||||
await this.image.updateImageSource(imageObject.image.name);
|
await this.image.updateImageSource(imageObject.image.name);
|
||||||
} else if (!this.image.isLoading && !this.image.isError) {
|
} else if (!this.image.isLoading && !this.image.isError) {
|
||||||
|
@ -3,8 +3,7 @@ import { loadImage } from 'features/controlLayers/konva/util';
|
|||||||
import type { ImageObject } from 'features/controlLayers/store/types';
|
import type { ImageObject } from 'features/controlLayers/store/types';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { getImageDTO as defaultGetImageDTO } from 'services/api/endpoints/images';
|
import { getImageDTO } from 'services/api/endpoints/images';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
export class CanvasImage {
|
export class CanvasImage {
|
||||||
@ -17,23 +16,10 @@ export class CanvasImage {
|
|||||||
konvaImage: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
konvaImage: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>;
|
|
||||||
onLoading: () => void;
|
|
||||||
onLoad: (imageName: string, imageEl: HTMLImageElement) => void;
|
|
||||||
onError: () => void;
|
|
||||||
lastImageObject: ImageObject;
|
lastImageObject: ImageObject;
|
||||||
|
|
||||||
constructor(
|
constructor(imageObject: ImageObject) {
|
||||||
imageObject: ImageObject,
|
const { id, width, height, x, y } = imageObject;
|
||||||
options?: {
|
|
||||||
getImageDTO?: (imageName: string) => Promise<ImageDTO | null>;
|
|
||||||
onLoading?: () => void;
|
|
||||||
onLoad?: (konvaImage: Konva.Image) => void;
|
|
||||||
onError?: () => void;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { getImageDTO, onLoading, onLoad, onError } = options ?? {};
|
|
||||||
const { id, width, height, x, y, filters } = imageObject;
|
|
||||||
this.konvaImageGroup = new Konva.Group({ id, listening: false, x, y });
|
this.konvaImageGroup = new Konva.Group({ id, listening: false, x, y });
|
||||||
this.konvaPlaceholderGroup = new Konva.Group({ listening: false });
|
this.konvaPlaceholderGroup = new Konva.Group({ listening: false });
|
||||||
this.konvaPlaceholderRect = new Konva.Rect({
|
this.konvaPlaceholderRect = new Konva.Rect({
|
||||||
@ -64,19 +50,23 @@ export class CanvasImage {
|
|||||||
this.konvaImage = null;
|
this.konvaImage = null;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.isError = false;
|
this.isError = false;
|
||||||
this.getImageDTO = getImageDTO ?? defaultGetImageDTO;
|
this.lastImageObject = imageObject;
|
||||||
this.onLoading = function () {
|
}
|
||||||
|
|
||||||
|
async updateImageSource(imageName: string) {
|
||||||
|
try {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
this.konvaImageGroup.visible(true);
|
||||||
|
|
||||||
if (!this.konvaImage) {
|
if (!this.konvaImage) {
|
||||||
this.konvaPlaceholderGroup.visible(true);
|
this.konvaPlaceholderGroup.visible(true);
|
||||||
this.konvaPlaceholderText.text(t('common.loadingImage', 'Loading Image'));
|
this.konvaPlaceholderText.text(t('common.loadingImage', 'Loading Image'));
|
||||||
}
|
}
|
||||||
this.konvaImageGroup.visible(true);
|
|
||||||
if (onLoading) {
|
const imageDTO = await getImageDTO(imageName);
|
||||||
onLoading();
|
assert(imageDTO !== null, 'imageDTO is null');
|
||||||
}
|
const imageEl = await loadImage(imageDTO.image_url);
|
||||||
};
|
|
||||||
this.onLoad = function (imageName: string, imageEl: HTMLImageElement) {
|
|
||||||
if (this.konvaImage) {
|
if (this.konvaImage) {
|
||||||
this.konvaImage.setAttrs({
|
this.konvaImage.setAttrs({
|
||||||
image: imageEl,
|
image: imageEl,
|
||||||
@ -86,52 +76,31 @@ export class CanvasImage {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
listening: false,
|
listening: false,
|
||||||
image: imageEl,
|
image: imageEl,
|
||||||
width,
|
width: this.lastImageObject.width,
|
||||||
height,
|
height: this.lastImageObject.height,
|
||||||
});
|
});
|
||||||
this.konvaImageGroup.add(this.konvaImage);
|
this.konvaImageGroup.add(this.konvaImage);
|
||||||
}
|
}
|
||||||
if (filters.length > 0) {
|
|
||||||
|
if (this.lastImageObject.filters.length > 0) {
|
||||||
this.konvaImage.cache();
|
this.konvaImage.cache();
|
||||||
this.konvaImage.filters(filters.map((f) => FILTER_MAP[f]));
|
this.konvaImage.filters(this.lastImageObject.filters.map((f) => FILTER_MAP[f]));
|
||||||
} else {
|
} else {
|
||||||
this.konvaImage.clearCache();
|
this.konvaImage.clearCache();
|
||||||
this.konvaImage.filters([]);
|
this.konvaImage.filters([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.imageName = imageName;
|
this.imageName = imageName;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.isError = false;
|
this.isError = false;
|
||||||
this.konvaPlaceholderGroup.visible(false);
|
this.konvaPlaceholderGroup.visible(false);
|
||||||
this.konvaImageGroup.visible(true);
|
} catch {
|
||||||
|
this.konvaImage?.visible(false);
|
||||||
if (onLoad) {
|
|
||||||
onLoad(this.konvaImage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.onError = function () {
|
|
||||||
this.imageName = null;
|
this.imageName = null;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
this.konvaPlaceholderGroup.visible(true);
|
|
||||||
this.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
this.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||||
this.konvaImageGroup.visible(true);
|
this.konvaPlaceholderGroup.visible(true);
|
||||||
|
|
||||||
if (onError) {
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.lastImageObject = imageObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateImageSource(imageName: string) {
|
|
||||||
try {
|
|
||||||
this.onLoading();
|
|
||||||
const imageDTO = await this.getImageDTO(imageName);
|
|
||||||
assert(imageDTO !== null, 'imageDTO is null');
|
|
||||||
const imageEl = await loadImage(imageDTO.image_url);
|
|
||||||
this.onLoad(imageName, imageEl);
|
|
||||||
} catch {
|
|
||||||
this.onError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,9 +352,6 @@ export class CanvasManager {
|
|||||||
if (lastProgressEvent !== prevLastProgressEvent) {
|
if (lastProgressEvent !== prevLastProgressEvent) {
|
||||||
log.debug('Rendering progress image');
|
log.debug('Rendering progress image');
|
||||||
await this.preview.progressPreview.render(lastProgressEvent);
|
await this.preview.progressPreview.render(lastProgressEvent);
|
||||||
if (this.stateApi.getSession().isActive) {
|
|
||||||
this.preview.stagingArea.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17,8 +17,9 @@ export class CanvasProgressPreview {
|
|||||||
|
|
||||||
async render(lastProgressEvent: InvocationDenoiseProgressEvent | null) {
|
async render(lastProgressEvent: InvocationDenoiseProgressEvent | null) {
|
||||||
const bboxRect = this.manager.stateApi.getBbox().rect;
|
const bboxRect = this.manager.stateApi.getBbox().rect;
|
||||||
|
const session = this.manager.stateApi.getSession();
|
||||||
|
|
||||||
if (lastProgressEvent) {
|
if (lastProgressEvent && session.isStaging) {
|
||||||
const { invocation, step, progress_image } = lastProgressEvent;
|
const { invocation, step, progress_image } = lastProgressEvent;
|
||||||
const { dataURL } = progress_image;
|
const { dataURL } = progress_image;
|
||||||
const { x, y, width, height } = bboxRect;
|
const { x, y, width, height } = bboxRect;
|
||||||
|
@ -25,16 +25,8 @@ export class CanvasStagingArea {
|
|||||||
|
|
||||||
if (this.selectedImage) {
|
if (this.selectedImage) {
|
||||||
const { imageDTO, offsetX, offsetY } = this.selectedImage;
|
const { imageDTO, offsetX, offsetY } = this.selectedImage;
|
||||||
if (this.image) {
|
|
||||||
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== imageDTO.image_name) {
|
if (!this.image) {
|
||||||
this.image.konvaImageGroup.visible(false);
|
|
||||||
this.image.konvaImage?.width(imageDTO.width);
|
|
||||||
this.image.konvaImage?.height(imageDTO.height);
|
|
||||||
this.image.konvaImageGroup.x(bboxRect.x + offsetX);
|
|
||||||
this.image.konvaImageGroup.y(bboxRect.y + offsetY);
|
|
||||||
await this.image.updateImageSource(imageDTO.image_name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const { image_name, width, height } = imageDTO;
|
const { image_name, width, height } = imageDTO;
|
||||||
this.image = new CanvasImage({
|
this.image = new CanvasImage({
|
||||||
id: 'staging-area-image',
|
id: 'staging-area-image',
|
||||||
@ -51,13 +43,16 @@ export class CanvasStagingArea {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.group.add(this.image.konvaImageGroup);
|
this.group.add(this.image.konvaImageGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== imageDTO.image_name) {
|
||||||
this.image.konvaImage?.width(imageDTO.width);
|
this.image.konvaImage?.width(imageDTO.width);
|
||||||
this.image.konvaImage?.height(imageDTO.height);
|
this.image.konvaImage?.height(imageDTO.height);
|
||||||
this.image.konvaImageGroup.x(bboxRect.x + offsetX);
|
this.image.konvaImageGroup.x(bboxRect.x + offsetX);
|
||||||
this.image.konvaImageGroup.y(bboxRect.y + offsetY);
|
this.image.konvaImageGroup.y(bboxRect.y + offsetY);
|
||||||
await this.image.updateImageSource(imageDTO.image_name);
|
await this.image.updateImageSource(imageDTO.image_name);
|
||||||
|
this.manager.stateApi.resetLastProgressEvent();
|
||||||
}
|
}
|
||||||
this.manager.stateApi.resetLastProgressEvent();
|
|
||||||
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
} else {
|
} else {
|
||||||
this.image?.konvaImageGroup.visible(false);
|
this.image?.konvaImageGroup.visible(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user