mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): use new canvas output node
This commit is contained in:
parent
fd269e91e0
commit
389bfc9e31
@ -1,11 +1,10 @@
|
|||||||
import { isAnyOf } from '@reduxjs/toolkit';
|
|
||||||
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 {
|
||||||
layerAdded,
|
layerAdded,
|
||||||
layerImageAdded,
|
layerImageAdded,
|
||||||
sessionStagedImageAccepted,
|
sessionStagingAreaImageAccepted,
|
||||||
sessionStagingCanceled,
|
sessionStagingAreaReset,
|
||||||
} from 'features/controlLayers/store/canvasV2Slice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { toast } from 'features/toast/toast';
|
import { toast } from 'features/toast/toast';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
@ -14,7 +13,7 @@ import { assert } from 'tsafe';
|
|||||||
|
|
||||||
export const addStagingListeners = (startAppListening: AppStartListening) => {
|
export const addStagingListeners = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
matcher: isAnyOf(sessionStagingCanceled, sessionStagedImageAccepted),
|
actionCreator: sessionStagingAreaReset,
|
||||||
effect: async (_, { dispatch }) => {
|
effect: async (_, { dispatch }) => {
|
||||||
const log = logger('canvas');
|
const log = logger('canvas');
|
||||||
|
|
||||||
@ -47,10 +46,10 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
startAppListening({
|
startAppListening({
|
||||||
actionCreator: sessionStagedImageAccepted,
|
actionCreator: sessionStagingAreaImageAccepted,
|
||||||
effect: async (action, api) => {
|
effect: async (action, api) => {
|
||||||
const { imageDTO } = action.payload;
|
const { index } = action.payload;
|
||||||
const { layers, selectedEntityIdentifier, bbox } = api.getState().canvasV2;
|
const { layers, selectedEntityIdentifier } = api.getState().canvasV2;
|
||||||
let layer = layers.entities.find((layer) => layer.id === selectedEntityIdentifier?.id);
|
let layer = layers.entities.find((layer) => layer.id === selectedEntityIdentifier?.id);
|
||||||
|
|
||||||
if (!layer) {
|
if (!layer) {
|
||||||
@ -63,11 +62,21 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
|
|||||||
layer = api.getState().canvasV2.layers.entities[0];
|
layer = api.getState().canvasV2.layers.entities[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stagedImage = api.getState().canvasV2.session.stagedImages[index];
|
||||||
|
|
||||||
|
assert(stagedImage, 'No staged image found to accept');
|
||||||
assert(layer, 'No layer found to stage image');
|
assert(layer, 'No layer found to stage image');
|
||||||
|
|
||||||
const { id } = layer;
|
const { id } = layer;
|
||||||
|
|
||||||
api.dispatch(layerImageAdded({ id, imageDTO, pos: { x: bbox.rect.x - layer.x, y: bbox.rect.y - layer.y } }));
|
api.dispatch(
|
||||||
|
layerImageAdded({
|
||||||
|
id,
|
||||||
|
imageDTO: stagedImage.imageDTO,
|
||||||
|
pos: { x: stagedImage.rect.x - layer.x, y: stagedImage.rect.y - layer.y },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
api.dispatch(sessionStagingAreaReset());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { enqueueRequested } from 'app/store/actions';
|
import { enqueueRequested } from 'app/store/actions';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { getCanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import { getCanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import { sessionStagingCanceled, sessionStartedStaging } from 'features/controlLayers/store/canvasV2Slice';
|
import { sessionStagingAreaReset, sessionStartedStaging } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||||
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
|
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
|
||||||
import { buildSDXLGraph } from 'features/nodes/util/graph/generation/buildSDXLGraph';
|
import { buildSDXLGraph } from 'features/nodes/util/graph/generation/buildSDXLGraph';
|
||||||
@ -49,7 +49,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
|||||||
await req.unwrap();
|
await req.unwrap();
|
||||||
} catch {
|
} catch {
|
||||||
if (didStartStaging && getState().canvasV2.session.isStaging) {
|
if (didStartStaging && getState().canvasV2.session.isStaging) {
|
||||||
dispatch(sessionStagingCanceled());
|
dispatch(sessionStagingAreaReset());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,6 @@ import { $lastProgressEvent, sessionImageStaged } from 'features/controlLayers/s
|
|||||||
import { boardIdSelected, galleryViewChanged, imageSelected, offsetChanged } from 'features/gallery/store/gallerySlice';
|
import { boardIdSelected, galleryViewChanged, imageSelected, offsetChanged } from 'features/gallery/store/gallerySlice';
|
||||||
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
|
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
|
||||||
import { zNodeStatus } from 'features/nodes/types/invocation';
|
import { zNodeStatus } from 'features/nodes/types/invocation';
|
||||||
import { CANVAS_OUTPUT } from 'features/nodes/util/graph/constants';
|
|
||||||
import { boardsApi } from 'services/api/endpoints/boards';
|
import { boardsApi } from 'services/api/endpoints/boards';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { getCategories, getListImagesUrl } from 'services/api/util';
|
import { getCategories, getListImagesUrl } from 'services/api/util';
|
||||||
@ -42,7 +41,10 @@ export const addInvocationCompleteEventListener = (startAppListening: AppStartLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This complete event has an associated image output
|
// This complete event has an associated image output
|
||||||
if (data.result.type === 'image_output' && !nodeTypeDenylist.includes(data.invocation.type)) {
|
if (
|
||||||
|
(data.result.type === 'image_output' || data.result.type === 'canvas_v2_mask_and_crop_output') &&
|
||||||
|
!nodeTypeDenylist.includes(data.invocation.type)
|
||||||
|
) {
|
||||||
const { image_name } = data.result.image;
|
const { image_name } = data.result.image;
|
||||||
const { gallery, canvasV2 } = getState();
|
const { gallery, canvasV2 } = getState();
|
||||||
|
|
||||||
@ -57,9 +59,10 @@ export const addInvocationCompleteEventListener = (startAppListening: AppStartLi
|
|||||||
imageDTORequest.unsubscribe();
|
imageDTORequest.unsubscribe();
|
||||||
|
|
||||||
// handle tab-specific logic
|
// handle tab-specific logic
|
||||||
if (data.origin === 'canvas' && data.invocation_source_id === CANVAS_OUTPUT) {
|
if (data.origin === 'canvas' && data.result.type === 'canvas_v2_mask_and_crop_output') {
|
||||||
|
const { x, y, width, height } = data.result;
|
||||||
if (canvasV2.session.isStaging) {
|
if (canvasV2.session.isStaging) {
|
||||||
dispatch(sessionImageStaged({ imageDTO }));
|
dispatch(sessionImageStaged({ imageDTO, rect: { x, y, width, height } }));
|
||||||
} else if (!canvasV2.session.isActive) {
|
} else if (!canvasV2.session.isActive) {
|
||||||
$lastProgressEvent.set(null);
|
$lastProgressEvent.set(null);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ import { useStore } from '@nanostores/react';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
$shouldShowStagedImage,
|
$shouldShowStagedImage,
|
||||||
sessionStagingCanceled,
|
|
||||||
sessionStagedImageAccepted,
|
|
||||||
sessionStagedImageDiscarded,
|
|
||||||
sessionNextStagedImageSelected,
|
sessionNextStagedImageSelected,
|
||||||
sessionPrevStagedImageSelected,
|
sessionPrevStagedImageSelected,
|
||||||
|
sessionStagedImageDiscarded,
|
||||||
|
sessionStagingAreaImageAccepted,
|
||||||
|
sessionStagingAreaReset,
|
||||||
} from 'features/controlLayers/store/canvasV2Slice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
@ -40,7 +40,7 @@ export const StagingAreaToolbarContent = memo(() => {
|
|||||||
const stagingArea = useAppSelector((s) => s.canvasV2.session);
|
const stagingArea = useAppSelector((s) => s.canvasV2.session);
|
||||||
const shouldShowStagedImage = useStore($shouldShowStagedImage);
|
const shouldShowStagedImage = useStore($shouldShowStagedImage);
|
||||||
const images = useMemo(() => stagingArea.stagedImages, [stagingArea]);
|
const images = useMemo(() => stagingArea.stagedImages, [stagingArea]);
|
||||||
const selectedImageDTO = useMemo(() => {
|
const selectedImage = useMemo(() => {
|
||||||
return images[stagingArea.selectedStagedImageIndex] ?? null;
|
return images[stagingArea.selectedStagedImageIndex] ?? null;
|
||||||
}, [images, stagingArea.selectedStagedImageIndex]);
|
}, [images, stagingArea.selectedStagedImageIndex]);
|
||||||
|
|
||||||
@ -57,25 +57,25 @@ export const StagingAreaToolbarContent = memo(() => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const onAccept = useCallback(() => {
|
const onAccept = useCallback(() => {
|
||||||
if (!selectedImageDTO) {
|
if (!selectedImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(sessionStagedImageAccepted({ imageDTO: selectedImageDTO }));
|
dispatch(sessionStagingAreaImageAccepted({ index: stagingArea.selectedStagedImageIndex }));
|
||||||
}, [dispatch, selectedImageDTO]);
|
}, [dispatch, selectedImage, stagingArea.selectedStagedImageIndex]);
|
||||||
|
|
||||||
const onDiscardOne = useCallback(() => {
|
const onDiscardOne = useCallback(() => {
|
||||||
if (!selectedImageDTO) {
|
if (!selectedImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (images.length === 1) {
|
if (images.length === 1) {
|
||||||
dispatch(sessionStagingCanceled());
|
dispatch(sessionStagingAreaReset());
|
||||||
} else {
|
} else {
|
||||||
dispatch(sessionStagedImageDiscarded({ imageDTO: selectedImageDTO }));
|
dispatch(sessionStagedImageDiscarded({ index: stagingArea.selectedStagedImageIndex }));
|
||||||
}
|
}
|
||||||
}, [dispatch, selectedImageDTO, images.length]);
|
}, [selectedImage, images.length, dispatch, stagingArea.selectedStagedImageIndex]);
|
||||||
|
|
||||||
const onDiscardAll = useCallback(() => {
|
const onDiscardAll = useCallback(() => {
|
||||||
dispatch(sessionStagingCanceled());
|
dispatch(sessionStagingAreaReset());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const onToggleShouldShowStagedImage = useCallback(() => {
|
const onToggleShouldShowStagedImage = useCallback(() => {
|
||||||
@ -145,7 +145,7 @@ export const StagingAreaToolbarContent = memo(() => {
|
|||||||
icon={<PiCheckBold />}
|
icon={<PiCheckBold />}
|
||||||
onClick={onAccept}
|
onClick={onAccept}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
isDisabled={!selectedImageDTO}
|
isDisabled={!selectedImage}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
tooltip={shouldShowStagedImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
|
tooltip={shouldShowStagedImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
|
||||||
@ -161,7 +161,7 @@ export const StagingAreaToolbarContent = memo(() => {
|
|||||||
icon={<PiFloppyDiskBold />}
|
icon={<PiFloppyDiskBold />}
|
||||||
onClick={onSaveStagingImage}
|
onClick={onSaveStagingImage}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
isDisabled={!selectedImageDTO || !selectedImageDTO.is_intermediate}
|
isDisabled={!selectedImage || !selectedImage.imageDTO.is_intermediate}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
tooltip={`${t('unifiedCanvas.discardCurrent')}`}
|
tooltip={`${t('unifiedCanvas.discardCurrent')}`}
|
||||||
@ -170,7 +170,7 @@ export const StagingAreaToolbarContent = memo(() => {
|
|||||||
onClick={onDiscardOne}
|
onClick={onDiscardOne}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
fontSize={16}
|
fontSize={16}
|
||||||
isDisabled={!selectedImageDTO}
|
isDisabled={!selectedImage}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
tooltip={`${t('unifiedCanvas.discardAll')} (Esc)`}
|
tooltip={`${t('unifiedCanvas.discardAll')} (Esc)`}
|
||||||
|
@ -1,44 +1,48 @@
|
|||||||
import { CanvasImage } from 'features/controlLayers/konva/CanvasImage';
|
import { CanvasImage } from 'features/controlLayers/konva/CanvasImage';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { StagingAreaImage } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
export class CanvasStagingArea {
|
export class CanvasStagingArea {
|
||||||
group: Konva.Group;
|
group: Konva.Group;
|
||||||
image: CanvasImage | null;
|
image: CanvasImage | null;
|
||||||
imageDTO: ImageDTO | null;
|
selectedImage: StagingAreaImage | null;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.group = new Konva.Group({ listening: false });
|
this.group = new Konva.Group({ listening: false });
|
||||||
this.image = null;
|
this.image = null;
|
||||||
this.imageDTO = null;
|
this.selectedImage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async render() {
|
async render() {
|
||||||
const session = this.manager.stateApi.getSession();
|
const session = this.manager.stateApi.getSession();
|
||||||
const bboxRect = this.manager.stateApi.getBbox().rect;
|
|
||||||
const shouldShowStagedImage = this.manager.stateApi.getShouldShowStagedImage();
|
const shouldShowStagedImage = this.manager.stateApi.getShouldShowStagedImage();
|
||||||
|
|
||||||
this.imageDTO = session.stagedImages[session.selectedStagedImageIndex] ?? null;
|
this.selectedImage = session.stagedImages[session.selectedStagedImageIndex] ?? null;
|
||||||
|
|
||||||
if (this.imageDTO) {
|
if (this.selectedImage) {
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== this.imageDTO.image_name) {
|
if (
|
||||||
await this.image.updateImageSource(this.imageDTO.image_name);
|
!this.image.isLoading &&
|
||||||
|
!this.image.isError &&
|
||||||
|
this.image.imageName !== this.selectedImage.imageDTO.image_name
|
||||||
|
) {
|
||||||
|
await this.image.updateImageSource(this.selectedImage.imageDTO.image_name);
|
||||||
}
|
}
|
||||||
this.image.konvaImageGroup.x(bboxRect.x);
|
this.image.konvaImageGroup.x(this.selectedImage.rect.x);
|
||||||
this.image.konvaImageGroup.y(bboxRect.y);
|
this.image.konvaImageGroup.y(this.selectedImage.rect.y);
|
||||||
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
} else {
|
} else {
|
||||||
const { image_name, width, height } = this.imageDTO;
|
const { image_name } = this.selectedImage.imageDTO;
|
||||||
|
const { x, y, width, height } = this.selectedImage.rect;
|
||||||
this.image = new CanvasImage(
|
this.image = new CanvasImage(
|
||||||
{
|
{
|
||||||
id: 'staging-area-image',
|
id: 'staging-area-image',
|
||||||
type: 'image',
|
type: 'image',
|
||||||
x: bboxRect.x,
|
x,
|
||||||
y: bboxRect.y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
filters: [],
|
filters: [],
|
||||||
@ -50,9 +54,9 @@ export class CanvasStagingArea {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onLoad: (konvaImage) => {
|
onLoad: (konvaImage) => {
|
||||||
if (this.imageDTO) {
|
if (this.selectedImage) {
|
||||||
konvaImage.width(this.imageDTO.width);
|
konvaImage.width(this.selectedImage.rect.width);
|
||||||
konvaImage.height(this.imageDTO.height);
|
konvaImage.height(this.selectedImage.rect.height);
|
||||||
}
|
}
|
||||||
this.manager.stateApi.resetLastProgressEvent();
|
this.manager.stateApi.resetLastProgressEvent();
|
||||||
this.image?.konvaImageGroup.visible(shouldShowStagedImage);
|
this.image?.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
@ -60,7 +64,7 @@ export class CanvasStagingArea {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.group.add(this.image.konvaImageGroup);
|
this.group.add(this.image.konvaImageGroup);
|
||||||
await this.image.updateImageSource(this.imageDTO.image_name);
|
await this.image.updateImageSource(this.selectedImage.imageDTO.image_name);
|
||||||
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,8 +339,7 @@ export const {
|
|||||||
sessionStartedStaging,
|
sessionStartedStaging,
|
||||||
sessionImageStaged,
|
sessionImageStaged,
|
||||||
sessionStagedImageDiscarded,
|
sessionStagedImageDiscarded,
|
||||||
sessionStagedImageAccepted,
|
sessionStagingAreaReset,
|
||||||
sessionStagingCanceled,
|
|
||||||
sessionNextStagedImageSelected,
|
sessionNextStagedImageSelected,
|
||||||
sessionPrevStagedImageSelected,
|
sessionPrevStagedImageSelected,
|
||||||
// Initial image
|
// Initial image
|
||||||
@ -383,3 +382,6 @@ export const canvasV2PersistConfig: PersistConfig<CanvasV2State> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const sessionRequested = createAction(`${canvasV2Slice.name}/sessionRequested`);
|
export const sessionRequested = createAction(`${canvasV2Slice.name}/sessionRequested`);
|
||||||
|
export const sessionStagingAreaImageAccepted = createAction<{ index: number }>(
|
||||||
|
`${canvasV2Slice.name}/sessionStagingAreaImageAccepted`
|
||||||
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
import type { CanvasV2State, StagingAreaImage } from 'features/controlLayers/store/types';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
export const sessionReducers = {
|
export const sessionReducers = {
|
||||||
sessionStarted: (state) => {
|
sessionStarted: (state) => {
|
||||||
@ -15,9 +14,9 @@ export const sessionReducers = {
|
|||||||
state.tool.selectedBuffer = state.tool.selected;
|
state.tool.selectedBuffer = state.tool.selected;
|
||||||
state.tool.selected = 'view';
|
state.tool.selected = 'view';
|
||||||
},
|
},
|
||||||
sessionImageStaged: (state, action: PayloadAction<{ imageDTO: ImageDTO }>) => {
|
sessionImageStaged: (state, action: PayloadAction<StagingAreaImage>) => {
|
||||||
const { imageDTO } = action.payload;
|
const { imageDTO, rect } = action.payload;
|
||||||
state.session.stagedImages.push(imageDTO);
|
state.session.stagedImages.push({ imageDTO, rect });
|
||||||
state.session.selectedStagedImageIndex = state.session.stagedImages.length - 1;
|
state.session.selectedStagedImageIndex = state.session.stagedImages.length - 1;
|
||||||
},
|
},
|
||||||
sessionNextStagedImageSelected: (state) => {
|
sessionNextStagedImageSelected: (state) => {
|
||||||
@ -29,9 +28,9 @@ export const sessionReducers = {
|
|||||||
(state.session.selectedStagedImageIndex - 1 + state.session.stagedImages.length) %
|
(state.session.selectedStagedImageIndex - 1 + state.session.stagedImages.length) %
|
||||||
state.session.stagedImages.length;
|
state.session.stagedImages.length;
|
||||||
},
|
},
|
||||||
sessionStagedImageDiscarded: (state, action: PayloadAction<{ imageDTO: ImageDTO }>) => {
|
sessionStagedImageDiscarded: (state, action: PayloadAction<{ index: number }>) => {
|
||||||
const { imageDTO } = action.payload;
|
const { index } = action.payload;
|
||||||
state.session.stagedImages = state.session.stagedImages.filter((image) => image.image_name !== imageDTO.image_name);
|
state.session.stagedImages = state.session.stagedImages.splice(index, 1);
|
||||||
state.session.selectedStagedImageIndex = Math.min(
|
state.session.selectedStagedImageIndex = Math.min(
|
||||||
state.session.selectedStagedImageIndex,
|
state.session.selectedStagedImageIndex,
|
||||||
state.session.stagedImages.length - 1
|
state.session.stagedImages.length - 1
|
||||||
@ -40,17 +39,7 @@ export const sessionReducers = {
|
|||||||
state.session.isStaging = false;
|
state.session.isStaging = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sessionStagedImageAccepted: (state, _: PayloadAction<{ imageDTO: ImageDTO }>) => {
|
sessionStagingAreaReset: (state) => {
|
||||||
// When we finish staging, reset the tool back to the previous selection.
|
|
||||||
state.session.isStaging = false;
|
|
||||||
state.session.stagedImages = [];
|
|
||||||
state.session.selectedStagedImageIndex = 0;
|
|
||||||
if (state.tool.selectedBuffer) {
|
|
||||||
state.tool.selected = state.tool.selectedBuffer;
|
|
||||||
state.tool.selectedBuffer = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sessionStagingCanceled: (state) => {
|
|
||||||
state.session.isStaging = false;
|
state.session.isStaging = false;
|
||||||
state.session.stagedImages = [];
|
state.session.stagedImages = [];
|
||||||
state.session.selectedStagedImageIndex = 0;
|
state.session.selectedStagedImageIndex = 0;
|
||||||
|
@ -826,6 +826,11 @@ export type LoRA = {
|
|||||||
weight: number;
|
weight: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type StagingAreaImage = {
|
||||||
|
imageDTO: ImageDTO;
|
||||||
|
rect: Rect;
|
||||||
|
};
|
||||||
|
|
||||||
export type CanvasV2State = {
|
export type CanvasV2State = {
|
||||||
_version: 3;
|
_version: 3;
|
||||||
selectedEntityIdentifier: CanvasEntityIdentifier | null;
|
selectedEntityIdentifier: CanvasEntityIdentifier | null;
|
||||||
@ -913,7 +918,7 @@ export type CanvasV2State = {
|
|||||||
session: {
|
session: {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isStaging: boolean;
|
isStaging: boolean;
|
||||||
stagedImages: ImageDTO[];
|
stagedImages: StagingAreaImage[];
|
||||||
selectedStagedImageIndex: number;
|
selectedStagedImageIndex: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ export const addInpaint = async (
|
|||||||
compositing: CanvasV2State['compositing'],
|
compositing: CanvasV2State['compositing'],
|
||||||
denoising_start: number,
|
denoising_start: number,
|
||||||
vaePrecision: ParameterPrecision
|
vaePrecision: ParameterPrecision
|
||||||
): Promise<Invocation<'canvas_paste_back'>> => {
|
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
|
||||||
denoise.denoising_start = denoising_start;
|
denoise.denoising_start = denoising_start;
|
||||||
|
|
||||||
const cropBbox = pick(bbox.rect, ['x', 'y', 'width', 'height']);
|
const cropBbox = pick(bbox.rect, ['x', 'y', 'width', 'height']);
|
||||||
@ -64,10 +64,10 @@ export const addInpaint = async (
|
|||||||
fp32: vaePrecision === 'fp32',
|
fp32: vaePrecision === 'fp32',
|
||||||
});
|
});
|
||||||
const canvasPasteBack = g.addNode({
|
const canvasPasteBack = g.addNode({
|
||||||
id: 'canvas_paste_back',
|
id: 'canvas_v2_mask_and_crop',
|
||||||
type: 'canvas_paste_back',
|
type: 'canvas_v2_mask_and_crop',
|
||||||
mask_blur: compositing.maskBlur,
|
invert: true,
|
||||||
source_image: { image_name: initialImage.image_name },
|
crop_visible: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resize initial image and mask to scaled size, feed into to gradient mask
|
// Resize initial image and mask to scaled size, feed into to gradient mask
|
||||||
@ -88,7 +88,7 @@ export const addInpaint = async (
|
|||||||
g.addEdge(createGradientMask, 'expanded_mask_area', resizeMaskToOriginalSize, 'image');
|
g.addEdge(createGradientMask, 'expanded_mask_area', resizeMaskToOriginalSize, 'image');
|
||||||
|
|
||||||
// Finally, paste the generated masked image back onto the original image
|
// Finally, paste the generated masked image back onto the original image
|
||||||
g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'target_image');
|
g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'image');
|
||||||
g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
|
g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
|
||||||
|
|
||||||
return canvasPasteBack;
|
return canvasPasteBack;
|
||||||
@ -111,10 +111,10 @@ export const addInpaint = async (
|
|||||||
image: { image_name: initialImage.image_name },
|
image: { image_name: initialImage.image_name },
|
||||||
});
|
});
|
||||||
const canvasPasteBack = g.addNode({
|
const canvasPasteBack = g.addNode({
|
||||||
id: 'canvas_paste_back',
|
id: 'canvas_v2_mask_and_crop',
|
||||||
type: 'canvas_paste_back',
|
type: 'canvas_v2_mask_and_crop',
|
||||||
mask_blur: compositing.maskBlur,
|
invert: true,
|
||||||
source_image: { image_name: initialImage.image_name },
|
crop_visible: true,
|
||||||
});
|
});
|
||||||
g.addEdge(alphaToMask, 'image', createGradientMask, 'mask');
|
g.addEdge(alphaToMask, 'image', createGradientMask, 'mask');
|
||||||
g.addEdge(i2l, 'latents', denoise, 'latents');
|
g.addEdge(i2l, 'latents', denoise, 'latents');
|
||||||
@ -124,7 +124,7 @@ export const addInpaint = async (
|
|||||||
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
|
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
|
||||||
g.addEdge(createGradientMask, 'expanded_mask_area', canvasPasteBack, 'mask');
|
g.addEdge(createGradientMask, 'expanded_mask_area', canvasPasteBack, 'mask');
|
||||||
|
|
||||||
g.addEdge(l2i, 'image', canvasPasteBack, 'target_image');
|
g.addEdge(l2i, 'image', canvasPasteBack, 'image');
|
||||||
|
|
||||||
return canvasPasteBack;
|
return canvasPasteBack;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export const addOutpaint = async (
|
|||||||
compositing: CanvasV2State['compositing'],
|
compositing: CanvasV2State['compositing'],
|
||||||
denoising_start: number,
|
denoising_start: number,
|
||||||
vaePrecision: ParameterPrecision
|
vaePrecision: ParameterPrecision
|
||||||
): Promise<Invocation<'canvas_paste_back'>> => {
|
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
|
||||||
denoise.denoising_start = denoising_start;
|
denoise.denoising_start = denoising_start;
|
||||||
|
|
||||||
const cropBbox = pick(bbox.rect, ['x', 'y', 'width', 'height']);
|
const cropBbox = pick(bbox.rect, ['x', 'y', 'width', 'height']);
|
||||||
@ -99,10 +99,10 @@ export const addOutpaint = async (
|
|||||||
...originalSize,
|
...originalSize,
|
||||||
});
|
});
|
||||||
const canvasPasteBack = g.addNode({
|
const canvasPasteBack = g.addNode({
|
||||||
id: 'canvas_paste_back',
|
id: 'canvas_v2_mask_and_crop',
|
||||||
type: 'canvas_paste_back',
|
type: 'canvas_v2_mask_and_crop',
|
||||||
mask_blur: compositing.maskBlur,
|
invert: true,
|
||||||
source_image: { image_name: initialImage.image_name },
|
crop_visible: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resize initial image and mask to scaled size, feed into to gradient mask
|
// Resize initial image and mask to scaled size, feed into to gradient mask
|
||||||
@ -112,7 +112,7 @@ export const addOutpaint = async (
|
|||||||
g.addEdge(createGradientMask, 'expanded_mask_area', resizeOutputMaskToOriginalSize, 'image');
|
g.addEdge(createGradientMask, 'expanded_mask_area', resizeOutputMaskToOriginalSize, 'image');
|
||||||
|
|
||||||
// Finally, paste the generated masked image back onto the original image
|
// Finally, paste the generated masked image back onto the original image
|
||||||
g.addEdge(resizeOutputImageToOriginalSize, 'image', canvasPasteBack, 'target_image');
|
g.addEdge(resizeOutputImageToOriginalSize, 'image', canvasPasteBack, 'image');
|
||||||
g.addEdge(resizeOutputMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
|
g.addEdge(resizeOutputMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
|
||||||
|
|
||||||
return canvasPasteBack;
|
return canvasPasteBack;
|
||||||
@ -145,9 +145,10 @@ export const addOutpaint = async (
|
|||||||
image: { image_name: initialImage.image_name },
|
image: { image_name: initialImage.image_name },
|
||||||
});
|
});
|
||||||
const canvasPasteBack = g.addNode({
|
const canvasPasteBack = g.addNode({
|
||||||
id: 'canvas_paste_back',
|
id: 'canvas_v2_mask_and_crop',
|
||||||
type: 'canvas_paste_back',
|
type: 'canvas_v2_mask_and_crop',
|
||||||
mask_blur: compositing.maskBlur,
|
invert: true,
|
||||||
|
crop_visible: true,
|
||||||
});
|
});
|
||||||
g.addEdge(maskAlphaToMask, 'image', maskCombine, 'mask1');
|
g.addEdge(maskAlphaToMask, 'image', maskCombine, 'mask1');
|
||||||
g.addEdge(initialImageAlphaToMask, 'image', maskCombine, 'mask2');
|
g.addEdge(initialImageAlphaToMask, 'image', maskCombine, 'mask2');
|
||||||
@ -159,8 +160,7 @@ export const addOutpaint = async (
|
|||||||
g.addEdge(modelLoader, 'unet', createGradientMask, 'unet');
|
g.addEdge(modelLoader, 'unet', createGradientMask, 'unet');
|
||||||
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
|
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
|
||||||
g.addEdge(createGradientMask, 'expanded_mask_area', canvasPasteBack, 'mask');
|
g.addEdge(createGradientMask, 'expanded_mask_area', canvasPasteBack, 'mask');
|
||||||
g.addEdge(infill, 'image', canvasPasteBack, 'source_image');
|
g.addEdge(l2i, 'image', canvasPasteBack, 'image');
|
||||||
g.addEdge(l2i, 'image', canvasPasteBack, 'target_image');
|
|
||||||
|
|
||||||
return canvasPasteBack;
|
return canvasPasteBack;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
|
|||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let canvasOutput: Invocation<'l2i' | 'img_nsfw' | 'img_watermark' | 'img_resize' | 'canvas_paste_back'> = l2i;
|
let canvasOutput: Invocation<'l2i' | 'img_nsfw' | 'img_watermark' | 'img_resize' | 'canvas_v2_mask_and_crop'> = l2i;
|
||||||
|
|
||||||
g.addEdge(modelLoader, 'unet', denoise, 'unet');
|
g.addEdge(modelLoader, 'unet', denoise, 'unet');
|
||||||
g.addEdge(modelLoader, 'clip', clipSkip, 'clip');
|
g.addEdge(modelLoader, 'clip', clipSkip, 'clip');
|
||||||
|
@ -121,7 +121,7 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
|
|||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let canvasOutput: Invocation<'l2i' | 'img_nsfw' | 'img_watermark' | 'img_resize' | 'canvas_paste_back'> = l2i;
|
let canvasOutput: Invocation<'l2i' | 'img_nsfw' | 'img_watermark' | 'img_resize' | 'canvas_v2_mask_and_crop'> = l2i;
|
||||||
|
|
||||||
g.addEdge(modelLoader, 'unet', denoise, 'unet');
|
g.addEdge(modelLoader, 'unet', denoise, 'unet');
|
||||||
g.addEdge(modelLoader, 'clip', posCond, 'clip');
|
g.addEdge(modelLoader, 'clip', posCond, 'clip');
|
||||||
|
Loading…
Reference in New Issue
Block a user