diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
index 29df0bf542..c9dd883c6d 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
@@ -9,7 +9,6 @@ import { addBatchEnqueuedListener } from 'app/store/middleware/listenerMiddlewar
import { addDeleteBoardAndImagesFulfilledListener } from 'app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted';
import { addBoardIdSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/boardIdSelected';
import { addBulkDownloadListeners } from 'app/store/middleware/listenerMiddleware/listeners/bulkDownload';
-import { addControlAdapterPreprocessor } from 'app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor';
import { addEnqueueRequestedLinear } from 'app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear';
import { addEnqueueRequestedNodes } from 'app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes';
import { addGalleryImageClickedListener } from 'app/store/middleware/listenerMiddleware/listeners/galleryImageClicked';
@@ -133,4 +132,4 @@ addAdHocPostProcessingRequestedListener(startAppListening);
addDynamicPromptsListener(startAppListening);
addSetDefaultSettingsListener(startAppListening);
-addControlAdapterPreprocessor(startAppListening);
+// addControlAdapterPreprocessor(startAppListening);
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts
index d3f20971b7..f5c3a95537 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts
@@ -1,5 +1,5 @@
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
-import { caAllDeleted, ipaAllDeleted, layerAllDeleted } from 'features/controlLayers/store/canvasV2Slice';
+import { ipaAllDeleted, layerAllDeleted } from 'features/controlLayers/store/canvasV2Slice';
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { imagesApi } from 'services/api/endpoints/images';
@@ -14,7 +14,7 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
let wereLayersReset = false;
let wasNodeEditorReset = false;
- let wereControlAdaptersReset = false;
+ const wereControlAdaptersReset = false;
let wereIPAdaptersReset = false;
const { nodes, canvasV2 } = getState();
@@ -31,10 +31,10 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
wasNodeEditorReset = true;
}
- if (imageUsage.isControlAdapterImage && !wereControlAdaptersReset) {
- dispatch(caAllDeleted());
- wereControlAdaptersReset = true;
- }
+ // if (imageUsage.isControlAdapterImage && !wereControlAdaptersReset) {
+ // dispatch(caAllDeleted());
+ // wereControlAdaptersReset = true;
+ // }
if (imageUsage.isIPAdapterImage && !wereIPAdaptersReset) {
dispatch(ipaAllDeleted());
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts
index 4d57cd6b02..17e6596701 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts
@@ -1,12 +1,7 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import type { AppDispatch, RootState } from 'app/store/store';
-import {
- caImageChanged,
- caProcessedImageChanged,
- entityDeleted,
- ipaImageChanged,
-} from 'features/controlLayers/store/canvasV2Slice';
+import { entityDeleted, ipaImageChanged } from 'features/controlLayers/store/canvasV2Slice';
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
@@ -39,14 +34,17 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
});
};
-const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
- state.canvasV2.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
- if (imageObject?.image.image_name === imageDTO.image_name || processedImageObject?.image.image_name === imageDTO.image_name) {
- dispatch(caImageChanged({ id, imageDTO: null }));
- dispatch(caProcessedImageChanged({ id, imageDTO: null }));
- }
- });
-};
+// const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
+// state.canvasV2.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
+// if (
+// imageObject?.image.image_name === imageDTO.image_name ||
+// processedImageObject?.image.image_name === imageDTO.image_name
+// ) {
+// dispatch(caImageChanged({ id, imageDTO: null }));
+// dispatch(caProcessedImageChanged({ id, imageDTO: null }));
+// }
+// });
+// };
const deleteIPAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
state.canvasV2.ipAdapters.entities.forEach(({ id, imageObject }) => {
@@ -120,7 +118,7 @@ export const addImageDeletionListeners = (startAppListening: AppStartListening)
}
deleteNodesImages(state, dispatch, imageDTO);
- deleteControlAdapterImages(state, dispatch, imageDTO);
+ // deleteControlAdapterImages(state, dispatch, imageDTO);
deleteIPAdapterImages(state, dispatch, imageDTO);
deleteLayerImages(state, dispatch, imageDTO);
} catch {
@@ -161,7 +159,7 @@ export const addImageDeletionListeners = (startAppListening: AppStartListening)
imageDTOs.forEach((imageDTO) => {
deleteNodesImages(state, dispatch, imageDTO);
- deleteControlAdapterImages(state, dispatch, imageDTO);
+ // deleteControlAdapterImages(state, dispatch, imageDTO);
deleteIPAdapterImages(state, dispatch, imageDTO);
deleteLayerImages(state, dispatch, imageDTO);
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
index 80c1f2bebd..39bb31ce63 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
@@ -3,7 +3,6 @@ import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { parseify } from 'common/util/serialize';
import {
- caImageChanged,
ipaImageChanged,
layerAdded,
rgIPAdapterImageChanged,
@@ -60,18 +59,18 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
return;
}
- /**
- * Image dropped on Control Adapter Layer
- */
- if (
- overData.actionType === 'SET_CA_IMAGE' &&
- activeData.payloadType === 'IMAGE_DTO' &&
- activeData.payload.imageDTO
- ) {
- const { id } = overData.context;
- dispatch(caImageChanged({ id, imageDTO: activeData.payload.imageDTO }));
- return;
- }
+ // /**
+ // * Image dropped on Control Adapter Layer
+ // */
+ // if (
+ // overData.actionType === 'SET_CA_IMAGE' &&
+ // activeData.payloadType === 'IMAGE_DTO' &&
+ // activeData.payload.imageDTO
+ // ) {
+ // const { id } = overData.context;
+ // dispatch(caImageChanged({ id, imageDTO: activeData.payload.imageDTO }));
+ // return;
+ // }
/**
* Image dropped on IP Adapter Layer
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
index fa23cdfc06..b0b26fbd1a 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
@@ -1,6 +1,6 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
-import { caImageChanged, ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
+import { ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
@@ -79,12 +79,12 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
return;
}
- if (postUploadAction?.type === 'SET_CA_IMAGE') {
- const { id } = postUploadAction;
- dispatch(caImageChanged({ id, imageDTO }));
- toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
- return;
- }
+ // if (postUploadAction?.type === 'SET_CA_IMAGE') {
+ // const { id } = postUploadAction;
+ // dispatch(caImageChanged({ id, imageDTO }));
+ // toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
+ // return;
+ // }
if (postUploadAction?.type === 'SET_IPA_IMAGE') {
const { id } = postUploadAction;
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
index e07dcdc844..bbb9cd1cef 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
@@ -1,7 +1,6 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import {
- entityIsEnabledToggled,
loraDeleted,
modelChanged,
vaeSelected,
@@ -50,14 +49,14 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}
// handle incompatible controlnets
- state.canvasV2.controlAdapters.entities.forEach((ca) => {
- if (ca.model?.base !== newBaseModel) {
- modelsCleared += 1;
- if (ca.isEnabled) {
- dispatch(entityIsEnabledToggled({ entityIdentifier: { id: ca.id, type: 'control_adapter' } }));
- }
- }
- });
+ // state.canvasV2.controlAdapters.entities.forEach((ca) => {
+ // if (ca.model?.base !== newBaseModel) {
+ // modelsCleared += 1;
+ // if (ca.isEnabled) {
+ // dispatch(entityIsEnabledToggled({ entityIdentifier: { id: ca.id, type: 'control_adapter' } }));
+ // }
+ // }
+ // });
if (modelsCleared > 0) {
toast({
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
index 87967544a8..86daeab55a 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
@@ -5,7 +5,6 @@ import type { JSONObject } from 'common/types';
import {
bboxHeightChanged,
bboxWidthChanged,
- caModelChanged,
ipaModelChanged,
loraDeleted,
modelChanged,
@@ -21,7 +20,6 @@ import type { Logger } from 'roarr';
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
import type { AnyModelConfig } from 'services/api/types';
import {
- isControlNetOrT2IAdapterModelConfig,
isIPAdapterModelConfig,
isLoRAModelConfig,
isNonRefinerMainModelConfig,
@@ -171,14 +169,14 @@ const handleLoRAModels: ModelHandler = (models, state, dispatch, _log) => {
};
const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
- const caModels = models.filter(isControlNetOrT2IAdapterModelConfig);
- state.canvasV2.controlAdapters.entities.forEach((ca) => {
- const isModelAvailable = caModels.some((m) => m.key === ca.model?.key);
- if (isModelAvailable) {
- return;
- }
- dispatch(caModelChanged({ id: ca.id, modelConfig: null }));
- });
+ // const caModels = models.filter(isControlNetOrT2IAdapterModelConfig);
+ // state.canvasV2.controlAdapters.entities.forEach((ca) => {
+ // const isModelAvailable = caModels.some((m) => m.key === ca.model?.key);
+ // if (isModelAvailable) {
+ // return;
+ // }
+ // dispatch(caModelChanged({ id: ca.id, modelConfig: null }));
+ // });
};
const handleIPAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
index bf8bf22d08..48df41fc48 100644
--- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
+++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
@@ -125,41 +125,41 @@ const createSelector = (templates: Templates) =>
reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') });
}
- canvasV2.controlAdapters.entities
- .filter((ca) => ca.isEnabled)
- .forEach((ca, i) => {
- const layerLiteral = i18n.t('controlLayers.layers_one');
- const layerNumber = i + 1;
- const layerType = i18n.t(LAYER_TYPE_TO_TKEY[ca.type]);
- const prefix = `${layerLiteral} #${layerNumber} (${layerType})`;
- const problems: string[] = [];
- // Must have model
- if (!ca.model) {
- problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoModelSelected'));
- }
- // Model base must match
- if (ca.model?.base !== model?.base) {
- problems.push(i18n.t('parameters.invoke.layer.controlAdapterIncompatibleBaseModel'));
- }
- // Must have a control image OR, if it has a processor, it must have a processed image
- if (!ca.imageObject) {
- problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoImageSelected'));
- } else if (ca.processorConfig && !ca.processedImageObject) {
- problems.push(i18n.t('parameters.invoke.layer.controlAdapterImageNotProcessed'));
- }
- // T2I Adapters require images have dimensions that are multiples of 64 (SD1.5) or 32 (SDXL)
- if (ca.adapterType === 't2i_adapter') {
- const multiple = model?.base === 'sdxl' ? 32 : 64;
- if (bbox.rect.width % multiple !== 0 || bbox.rect.height % multiple !== 0) {
- problems.push(i18n.t('parameters.invoke.layer.t2iAdapterIncompatibleDimensions', { multiple }));
- }
- }
+ // canvasV2.controlAdapters.entities
+ // .filter((ca) => ca.isEnabled)
+ // .forEach((ca, i) => {
+ // const layerLiteral = i18n.t('controlLayers.layers_one');
+ // const layerNumber = i + 1;
+ // const layerType = i18n.t(LAYER_TYPE_TO_TKEY[ca.type]);
+ // const prefix = `${layerLiteral} #${layerNumber} (${layerType})`;
+ // const problems: string[] = [];
+ // // Must have model
+ // if (!ca.model) {
+ // problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoModelSelected'));
+ // }
+ // // Model base must match
+ // if (ca.model?.base !== model?.base) {
+ // problems.push(i18n.t('parameters.invoke.layer.controlAdapterIncompatibleBaseModel'));
+ // }
+ // // Must have a control image OR, if it has a processor, it must have a processed image
+ // if (!ca.imageObject) {
+ // problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoImageSelected'));
+ // } else if (ca.processorConfig && !ca.processedImageObject) {
+ // problems.push(i18n.t('parameters.invoke.layer.controlAdapterImageNotProcessed'));
+ // }
+ // // T2I Adapters require images have dimensions that are multiples of 64 (SD1.5) or 32 (SDXL)
+ // if (ca.adapterType === 't2i_adapter') {
+ // const multiple = model?.base === 'sdxl' ? 32 : 64;
+ // if (bbox.rect.width % multiple !== 0 || bbox.rect.height % multiple !== 0) {
+ // problems.push(i18n.t('parameters.invoke.layer.t2iAdapterIncompatibleDimensions', { multiple }));
+ // }
+ // }
- if (problems.length) {
- const content = upperFirst(problems.join(', '));
- reasons.push({ prefix, content });
- }
- });
+ // if (problems.length) {
+ // const content = upperFirst(problems.join(', '));
+ // reasons.push({ prefix, content });
+ // }
+ // });
canvasV2.ipAdapters.entities
.filter((ipa) => ipa.isEnabled)
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx
index 9b3c9d3bc7..edb1708aae 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx
@@ -1,6 +1,5 @@
import { Flex } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
-import { ControlAdapterList } from 'features/controlLayers/components/ControlAdapter/ControlAdapterList';
import { InpaintMask } from 'features/controlLayers/components/InpaintMask/InpaintMask';
import { IPAdapterList } from 'features/controlLayers/components/IPAdapter/IPAdapterList';
import { LayerEntityList } from 'features/controlLayers/components/Layer/LayerEntityList';
@@ -13,7 +12,6 @@ export const CanvasEntityList = memo(() => {
-
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapter.tsx
deleted file mode 100644
index 280fd74288..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapter.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Spacer, useDisclosure } from '@invoke-ai/ui-library';
-import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
-import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
-import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
-import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
-import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
-import { ControlAdapterActionsMenu } from 'features/controlLayers/components/ControlAdapter/ControlAdapterActionsMenu';
-import { ControlAdapterOpacityAndFilter } from 'features/controlLayers/components/ControlAdapter/ControlAdapterOpacityAndFilter';
-import { ControlAdapterSettings } from 'features/controlLayers/components/ControlAdapter/ControlAdapterSettings';
-import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
-import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
-import { memo, useMemo } from 'react';
-
-type Props = {
- id: string;
-};
-
-export const ControlAdapter = memo(({ id }: Props) => {
- const entityIdentifier = useMemo(() => ({ id, type: 'control_adapter' }), [id]);
- const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
-
- return (
-
-
-
-
-
-
-
-
-
-
- {isOpen && }
-
-
- );
-});
-
-ControlAdapter.displayName = 'ControlAdapter';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterActionsMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterActionsMenu.tsx
deleted file mode 100644
index a9b5815a95..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterActionsMenu.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Menu, MenuList } from '@invoke-ai/ui-library';
-import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
-import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton';
-import { memo } from 'react';
-
-export const ControlAdapterActionsMenu = memo(() => {
- return (
-
- );
-});
-
-ControlAdapterActionsMenu.displayName = 'ControlAdapterActionsMenu';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterImagePreview.tsx
deleted file mode 100644
index 8faab37780..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterImagePreview.tsx
+++ /dev/null
@@ -1,229 +0,0 @@
-import { Box, Flex, Spinner, useShiftModifier } from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import IAIDndImage from 'common/components/IAIDndImage';
-import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
-import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
-import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
-import type { CanvasControlAdapterState } from 'features/controlLayers/store/types';
-import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
-import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
-import { memo, useCallback, useEffect, useMemo, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiArrowCounterClockwiseBold, PiFloppyDiskBold, PiRulerBold } from 'react-icons/pi';
-import {
- useAddImageToBoardMutation,
- useChangeImageIsIntermediateMutation,
- useGetImageDTOQuery,
- useRemoveImageFromBoardMutation,
-} from 'services/api/endpoints/images';
-import type { ImageDTO, PostUploadAction } from 'services/api/types';
-
-type Props = {
- controlAdapter: CanvasControlAdapterState;
- onChangeImage: (imageDTO: ImageDTO | null) => void;
- droppableData: TypesafeDroppableData;
- postUploadAction: PostUploadAction;
- onErrorLoadingImage: () => void;
- onErrorLoadingProcessedImage: () => void;
-};
-
-export const ControlAdapterImagePreview = memo(
- ({
- controlAdapter,
- onChangeImage,
- droppableData,
- postUploadAction,
- onErrorLoadingImage,
- onErrorLoadingProcessedImage,
- }: Props) => {
- const { t } = useTranslation();
- const dispatch = useAppDispatch();
- const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
- const isConnected = useAppSelector((s) => s.system.isConnected);
- const optimalDimension = useAppSelector(selectOptimalDimension);
- const shift = useShiftModifier();
-
- const [isMouseOverImage, setIsMouseOverImage] = useState(false);
-
- const { currentData: controlImage, isError: isErrorControlImage } = useGetImageDTOQuery(
- controlAdapter.imageObject?.image.image_name ?? skipToken
- );
- const { currentData: processedControlImage, isError: isErrorProcessedControlImage } = useGetImageDTOQuery(
- controlAdapter.processedImageObject?.image.image_name ?? skipToken
- );
-
- const [changeIsIntermediate] = useChangeImageIsIntermediateMutation();
- const [addToBoard] = useAddImageToBoardMutation();
- const [removeFromBoard] = useRemoveImageFromBoardMutation();
- const handleResetControlImage = useCallback(() => {
- onChangeImage(null);
- }, [onChangeImage]);
-
- const handleSaveControlImage = useCallback(async () => {
- if (!processedControlImage) {
- return;
- }
-
- await changeIsIntermediate({
- imageDTO: processedControlImage,
- is_intermediate: false,
- }).unwrap();
-
- if (autoAddBoardId !== 'none') {
- addToBoard({
- imageDTO: processedControlImage,
- board_id: autoAddBoardId,
- });
- } else {
- removeFromBoard({ imageDTO: processedControlImage });
- }
- }, [processedControlImage, changeIsIntermediate, autoAddBoardId, addToBoard, removeFromBoard]);
-
- const handleSetControlImageToDimensions = useCallback(() => {
- if (!controlImage) {
- return;
- }
-
- const options = { updateAspectRatio: true, clamp: true };
-
- if (shift) {
- const { width, height } = controlImage;
- dispatch(bboxWidthChanged({ width, ...options }));
- dispatch(bboxHeightChanged({ height, ...options }));
- } else {
- const { width, height } = calculateNewSize(
- controlImage.width / controlImage.height,
- optimalDimension * optimalDimension
- );
- dispatch(bboxWidthChanged({ width, ...options }));
- dispatch(bboxHeightChanged({ height, ...options }));
- }
- }, [controlImage, dispatch, optimalDimension, shift]);
-
- const handleMouseEnter = useCallback(() => {
- setIsMouseOverImage(true);
- }, []);
-
- const handleMouseLeave = useCallback(() => {
- setIsMouseOverImage(false);
- }, []);
-
- const draggableData = useMemo(() => {
- if (controlImage) {
- return {
- id: controlAdapter.id,
- payloadType: 'IMAGE_DTO',
- payload: { imageDTO: controlImage },
- };
- }
- }, [controlImage, controlAdapter.id]);
-
- const shouldShowProcessedImage =
- controlImage &&
- processedControlImage &&
- !isMouseOverImage &&
- !controlAdapter.processorPendingBatchId &&
- controlAdapter.processorConfig !== null;
-
- useEffect(() => {
- if (!isConnected) {
- return;
- }
- if (isErrorControlImage) {
- onErrorLoadingImage();
- }
- if (isErrorProcessedControlImage) {
- onErrorLoadingProcessedImage();
- }
- }, [
- handleResetControlImage,
- isConnected,
- isErrorControlImage,
- isErrorProcessedControlImage,
- onErrorLoadingImage,
- onErrorLoadingProcessedImage,
- ]);
-
- return (
-
-
-
-
-
-
-
- {controlImage && (
-
- }
- tooltip={t('controlnet.resetControlImage')}
- />
- }
- tooltip={t('controlnet.saveControlImage')}
- />
- }
- tooltip={
- shift ? t('controlnet.setControlImageDimensionsForce') : t('controlnet.setControlImageDimensions')
- }
- />
-
- )}
-
- {controlAdapter.processorPendingBatchId !== null && (
-
-
-
- )}
-
- );
- }
-);
-
-ControlAdapterImagePreview.displayName = 'ControlAdapterImagePreview';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterList.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterList.tsx
deleted file mode 100644
index 694dade9a8..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterList.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
-import { useAppSelector } from 'app/store/storeHooks';
-import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
-import { ControlAdapter } from 'features/controlLayers/components/ControlAdapter/ControlAdapter';
-import { mapId } from 'features/controlLayers/konva/util';
-import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
-import { memo } from 'react';
-import { useTranslation } from 'react-i18next';
-
-const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
- return canvasV2.controlAdapters.entities.map(mapId).reverse();
-});
-
-export const ControlAdapterList = memo(() => {
- const { t } = useTranslation();
- const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'control_adapter'));
- const caIds = useAppSelector(selectEntityIds);
-
- if (caIds.length === 0) {
- return null;
- }
-
- if (caIds.length > 0) {
- return (
- <>
-
- {caIds.map((id) => (
-
- ))}
- >
- );
- }
-});
-
-ControlAdapterList.displayName = 'ControlAdapterList';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterOpacityAndFilter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterOpacityAndFilter.tsx
deleted file mode 100644
index ba4f85a1e1..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterOpacityAndFilter.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import {
- CompositeNumberInput,
- CompositeSlider,
- Flex,
- FormControl,
- FormLabel,
- IconButton,
- Popover,
- PopoverArrow,
- PopoverBody,
- PopoverContent,
- PopoverTrigger,
- Switch,
-} from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { stopPropagation } from 'common/util/stopPropagation';
-import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
-import { caFilterChanged, caOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
-import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiDropHalfFill } from 'react-icons/pi';
-
-const marks = [0, 25, 50, 75, 100];
-const formatPct = (v: number | string) => `${v} %`;
-
-export const ControlAdapterOpacityAndFilter = memo(() => {
- const { id } = useEntityIdentifierContext();
- const { t } = useTranslation();
- const dispatch = useAppDispatch();
- const opacity = useAppSelector((s) => Math.round(selectCAOrThrow(s.canvasV2, id).opacity * 100));
- const isFilterEnabled = useAppSelector((s) =>
- selectCAOrThrow(s.canvasV2, id).filters.includes('LightnessToAlphaFilter')
- );
- const onChangeOpacity = useCallback(
- (v: number) => {
- dispatch(caOpacityChanged({ id, opacity: v / 100 }));
- },
- [dispatch, id]
- );
- const onChangeFilter = useCallback(
- (e: ChangeEvent) => {
- dispatch(caFilterChanged({ id, filters: e.target.checked ? ['LightnessToAlphaFilter'] : [] }));
- },
- [dispatch, id]
- );
- return (
-
-
- }
- variant="ghost"
- onDoubleClick={stopPropagation}
- />
-
-
-
-
-
-
-
- {t('controlLayers.opacityFilter')}
-
-
-
-
- {t('controlLayers.opacity')}
-
-
-
-
-
-
-
- );
-});
-
-ControlAdapterOpacityAndFilter.displayName = 'ControlAdapterOpacityAndFilter';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorConfig.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorConfig.tsx
deleted file mode 100644
index 6836584ba3..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorConfig.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { CannyProcessor } from 'features/controlLayers/components/ControlAdapter/processors/CannyProcessor';
-import { ColorMapProcessor } from 'features/controlLayers/components/ControlAdapter/processors/ColorMapProcessor';
-import { ContentShuffleProcessor } from 'features/controlLayers/components/ControlAdapter/processors/ContentShuffleProcessor';
-import { DepthAnythingProcessor } from 'features/controlLayers/components/ControlAdapter/processors/DepthAnythingProcessor';
-import { DWOpenposeProcessor } from 'features/controlLayers/components/ControlAdapter/processors/DWOpenposeProcessor';
-import { HedProcessor } from 'features/controlLayers/components/ControlAdapter/processors/HedProcessor';
-import { LineartProcessor } from 'features/controlLayers/components/ControlAdapter/processors/LineartProcessor';
-import { MediapipeFaceProcessor } from 'features/controlLayers/components/ControlAdapter/processors/MediapipeFaceProcessor';
-import { MidasDepthProcessor } from 'features/controlLayers/components/ControlAdapter/processors/MidasDepthProcessor';
-import { MlsdImageProcessor } from 'features/controlLayers/components/ControlAdapter/processors/MlsdImageProcessor';
-import { PidiProcessor } from 'features/controlLayers/components/ControlAdapter/processors/PidiProcessor';
-import type { FilterConfig } from 'features/controlLayers/store/types';
-import { memo } from 'react';
-
-type Props = {
- config: FilterConfig | null;
- onChange: (config: FilterConfig | null) => void;
-};
-
-export const ControlAdapterProcessorConfig = memo(({ config, onChange }: Props) => {
- if (!config) {
- return null;
- }
-
- if (config.type === 'canny_image_processor') {
- return ;
- }
-
- if (config.type === 'color_map_image_processor') {
- return ;
- }
-
- if (config.type === 'depth_anything_image_processor') {
- return ;
- }
-
- if (config.type === 'hed_image_processor') {
- return ;
- }
-
- if (config.type === 'lineart_image_processor') {
- return ;
- }
-
- if (config.type === 'content_shuffle_image_processor') {
- return ;
- }
-
- if (config.type === 'lineart_anime_image_processor') {
- // No configurable options for this processor
- return null;
- }
-
- if (config.type === 'mediapipe_face_processor') {
- return ;
- }
-
- if (config.type === 'midas_depth_image_processor') {
- return ;
- }
-
- if (config.type === 'mlsd_image_processor') {
- return ;
- }
-
- if (config.type === 'normalbae_image_processor') {
- // No configurable options for this processor
- return null;
- }
-
- if (config.type === 'dw_openpose_image_processor') {
- return ;
- }
-
- if (config.type === 'pidi_image_processor') {
- return ;
- }
-
- if (config.type === 'zoe_depth_image_processor') {
- return null;
- }
-});
-
-ControlAdapterProcessorConfig.displayName = 'ControlAdapterProcessorConfig';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorTypeSelect.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorTypeSelect.tsx
deleted file mode 100644
index e4d0d1878d..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterProcessorTypeSelect.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import type { ComboboxOnChange } from '@invoke-ai/ui-library';
-import { Combobox, Flex, FormControl, FormLabel, IconButton } from '@invoke-ai/ui-library';
-import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
-import { useAppSelector } from 'app/store/storeHooks';
-import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
-import type {FilterConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS, isFilterType } from 'features/controlLayers/store/types';
-import { configSelector } from 'features/system/store/configSelectors';
-import { includes, map } from 'lodash-es';
-import { memo, useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiXBold } from 'react-icons/pi';
-import { assert } from 'tsafe';
-
-type Props = {
- config: FilterConfig | null;
- onChange: (config: FilterConfig | null) => void;
-};
-
-const selectDisabledProcessors = createMemoizedSelector(
- configSelector,
- (config) => config.sd.disabledControlNetProcessors
-);
-
-export const ControlAdapterProcessorTypeSelect = memo(({ config, onChange }: Props) => {
- const { t } = useTranslation();
- const disabledProcessors = useAppSelector(selectDisabledProcessors);
- const options = useMemo(() => {
- return map(IMAGE_FILTERS, ({ labelTKey }, type) => ({ value: type, label: t(labelTKey) })).filter(
- (o) => !includes(disabledProcessors, o.value)
- );
- }, [disabledProcessors, t]);
-
- const _onChange = useCallback(
- (v) => {
- if (!v) {
- onChange(null);
- } else {
- assert(isFilterType(v.value));
- onChange(IMAGE_FILTERS[v.value].buildDefaults());
- }
- },
- [onChange]
- );
- const clearProcessor = useCallback(() => {
- onChange(null);
- }, [onChange]);
- const value = useMemo(() => options.find((o) => o.value === config?.type) ?? null, [options, config?.type]);
-
- return (
-
-
-
- {t('controlnet.processor')}
-
-
-
- }
- variant="ghost"
- size="sm"
- />
-
- );
-});
-
-ControlAdapterProcessorTypeSelect.displayName = 'ControlAdapterProcessorTypeSelect';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterSettings.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterSettings.tsx
deleted file mode 100644
index ef2379d117..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/ControlAdapterSettings.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-import { Box, Divider, Flex, Icon, IconButton } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
-import { CanvasEntitySettings } from 'features/controlLayers/components/common/CanvasEntitySettings';
-import { Weight } from 'features/controlLayers/components/common/Weight';
-import { ControlAdapterControlModeSelect } from 'features/controlLayers/components/ControlAdapter/ControlAdapterControlModeSelect';
-import { ControlAdapterImagePreview } from 'features/controlLayers/components/ControlAdapter/ControlAdapterImagePreview';
-import { ControlAdapterModel } from 'features/controlLayers/components/ControlAdapter/ControlAdapterModel';
-import { ControlAdapterProcessorConfig } from 'features/controlLayers/components/ControlAdapter/ControlAdapterProcessorConfig';
-import { ControlAdapterProcessorTypeSelect } from 'features/controlLayers/components/ControlAdapter/ControlAdapterProcessorTypeSelect';
-import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
-import {
- caBeginEndStepPctChanged,
- caControlModeChanged,
- caImageChanged,
- caModelChanged,
- caProcessedImageChanged,
- caProcessorConfigChanged,
- caWeightChanged,
-} from 'features/controlLayers/store/canvasV2Slice';
-import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
-import type { ControlModeV2, FilterConfig } from 'features/controlLayers/store/types';
-import type { CAImageDropData } from 'features/dnd/types';
-import { memo, useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiCaretUpBold } from 'react-icons/pi';
-import { useToggle } from 'react-use';
-import type {
- CAImagePostUploadAction,
- ControlNetModelConfig,
- ImageDTO,
- T2IAdapterModelConfig,
-} from 'services/api/types';
-
-export const ControlAdapterSettings = memo(() => {
- const { id } = useEntityIdentifierContext();
- const dispatch = useAppDispatch();
- const { t } = useTranslation();
- const [isExpanded, toggleIsExpanded] = useToggle(false);
-
- const controlAdapter = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id));
-
- const onChangeBeginEndStepPct = useCallback(
- (beginEndStepPct: [number, number]) => {
- dispatch(caBeginEndStepPctChanged({ id, beginEndStepPct }));
- },
- [dispatch, id]
- );
-
- const onChangeControlMode = useCallback(
- (controlMode: ControlModeV2) => {
- dispatch(caControlModeChanged({ id, controlMode }));
- },
- [dispatch, id]
- );
-
- const onChangeWeight = useCallback(
- (weight: number) => {
- dispatch(caWeightChanged({ id, weight }));
- },
- [dispatch, id]
- );
-
- const onChangeProcessorConfig = useCallback(
- (processorConfig: FilterConfig | null) => {
- dispatch(caProcessorConfigChanged({ id, processorConfig }));
- },
- [dispatch, id]
- );
-
- const onChangeModel = useCallback(
- (modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
- dispatch(caModelChanged({ id, modelConfig }));
- },
- [dispatch, id]
- );
-
- const onChangeImage = useCallback(
- (imageDTO: ImageDTO | null) => {
- dispatch(caImageChanged({ id, imageDTO }));
- },
- [dispatch, id]
- );
-
- const onErrorLoadingImage = useCallback(() => {
- dispatch(caImageChanged({ id, imageDTO: null }));
- }, [dispatch, id]);
-
- const onErrorLoadingProcessedImage = useCallback(() => {
- dispatch(caProcessedImageChanged({ id, imageDTO: null }));
- }, [dispatch, id]);
-
- const droppableData = useMemo(() => ({ actionType: 'SET_CA_IMAGE', context: { id }, id }), [id]);
- const postUploadAction = useMemo(() => ({ id, type: 'SET_CA_IMAGE' }), [id]);
-
- return (
-
-
-
-
-
-
-
-
- }
- />
-
-
-
- {controlAdapter.adapterType === 'controlnet' && (
-
- )}
-
-
-
-
-
-
-
- {isExpanded && (
- <>
-
-
-
-
-
- >
- )}
-
-
- );
-});
-
-ControlAdapterSettings.displayName = 'ControlAdapterSettings';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/CannyProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/CannyProcessor.tsx
deleted file mode 100644
index d1b15c6645..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/CannyProcessor.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { CannyProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['canny_image_processor'].buildDefaults();
-
-export const CannyProcessor = ({ onChange, config }: Props) => {
- const { t } = useTranslation();
- const handleLowThresholdChanged = useCallback(
- (v: number) => {
- onChange({ ...config, low_threshold: v });
- },
- [onChange, config]
- );
- const handleHighThresholdChanged = useCallback(
- (v: number) => {
- onChange({ ...config, high_threshold: v });
- },
- [onChange, config]
- );
-
- return (
-
-
- {t('controlnet.lowThreshold')}
-
-
-
-
- {t('controlnet.highThreshold')}
-
-
-
-
- );
-};
-
-CannyProcessor.displayName = 'CannyProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ColorMapProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ColorMapProcessor.tsx
deleted file mode 100644
index 3261703ded..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ColorMapProcessor.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { ColorMapProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['color_map_image_processor'].buildDefaults();
-
-export const ColorMapProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
- const handleColorMapTileSizeChanged = useCallback(
- (v: number) => {
- onChange({ ...config, color_map_tile_size: v });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.colorMapTileSize')}
-
-
-
-
- );
-});
-
-ColorMapProcessor.displayName = 'ColorMapProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ContentShuffleProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ContentShuffleProcessor.tsx
deleted file mode 100644
index b86387097a..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ContentShuffleProcessor.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { ContentShuffleProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['content_shuffle_image_processor'].buildDefaults();
-
-export const ContentShuffleProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleWChanged = useCallback(
- (v: number) => {
- onChange({ ...config, w: v });
- },
- [config, onChange]
- );
-
- const handleHChanged = useCallback(
- (v: number) => {
- onChange({ ...config, h: v });
- },
- [config, onChange]
- );
-
- const handleFChanged = useCallback(
- (v: number) => {
- onChange({ ...config, f: v });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.w')}
-
-
-
-
- {t('controlnet.h')}
-
-
-
-
- {t('controlnet.f')}
-
-
-
-
- );
-});
-
-ContentShuffleProcessor.displayName = 'ContentShuffleProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DWOpenposeProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DWOpenposeProcessor.tsx
deleted file mode 100644
index 4b197eb361..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DWOpenposeProcessor.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Flex, FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { DWOpenposeProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['dw_openpose_image_processor'].buildDefaults();
-
-export const DWOpenposeProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleDrawBodyChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, draw_body: e.target.checked });
- },
- [config, onChange]
- );
-
- const handleDrawFaceChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, draw_face: e.target.checked });
- },
- [config, onChange]
- );
-
- const handleDrawHandsChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, draw_hands: e.target.checked });
- },
- [config, onChange]
- );
-
- return (
-
-
-
- {t('controlnet.body')}
-
-
-
- {t('controlnet.face')}
-
-
-
- {t('controlnet.hands')}
-
-
-
-
- );
-});
-
-DWOpenposeProcessor.displayName = 'DWOpenposeProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DepthAnythingProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DepthAnythingProcessor.tsx
deleted file mode 100644
index b4aa76ed6e..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/DepthAnythingProcessor.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import type { ComboboxOnChange } from '@invoke-ai/ui-library';
-import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { DepthAnythingModelSize, DepthAnythingProcessorConfig } from 'features/controlLayers/store/types';
-import { isDepthAnythingModelSize } from 'features/controlLayers/store/types';
-import { memo, useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-
-export const DepthAnythingProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
- const handleModelSizeChange = useCallback(
- (v) => {
- if (!isDepthAnythingModelSize(v?.value)) {
- return;
- }
- onChange({ ...config, model_size: v.value });
- },
- [config, onChange]
- );
-
- const options: { label: string; value: DepthAnythingModelSize }[] = useMemo(
- () => [
- { label: t('controlnet.depthAnythingSmallV2'), value: 'small_v2' },
- { label: t('controlnet.small'), value: 'small' },
- { label: t('controlnet.base'), value: 'base' },
- { label: t('controlnet.large'), value: 'large' },
- ],
- [t]
- );
-
- const value = useMemo(() => options.filter((o) => o.value === config.model_size)[0], [options, config.model_size]);
-
- return (
-
-
- {t('controlnet.modelSize')}
-
-
-
- );
-});
-
-DepthAnythingProcessor.displayName = 'DepthAnythingProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/HedProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/HedProcessor.tsx
deleted file mode 100644
index 6c27e386c5..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/HedProcessor.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { HedProcessorConfig } from 'features/controlLayers/store/types';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-
-export const HedProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleScribbleChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, scribble: e.target.checked });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.scribble')}
-
-
-
- );
-});
-
-HedProcessor.displayName = 'HedProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/LineartProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/LineartProcessor.tsx
deleted file mode 100644
index 4abf7e920c..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/LineartProcessor.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { LineartProcessorConfig } from 'features/controlLayers/store/types';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-
-export const LineartProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleCoarseChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, coarse: e.target.checked });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.coarse')}
-
-
-
- );
-});
-
-LineartProcessor.displayName = 'LineartProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MediapipeFaceProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MediapipeFaceProcessor.tsx
deleted file mode 100644
index ad59e9f8d7..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MediapipeFaceProcessor.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { MediapipeFaceProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['mediapipe_face_processor'].buildDefaults();
-
-export const MediapipeFaceProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleMaxFacesChanged = useCallback(
- (v: number) => {
- onChange({ ...config, max_faces: v });
- },
- [config, onChange]
- );
-
- const handleMinConfidenceChanged = useCallback(
- (v: number) => {
- onChange({ ...config, min_confidence: v });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.maxFaces')}
-
-
-
-
- {t('controlnet.minConfidence')}
-
-
-
-
- );
-});
-
-MediapipeFaceProcessor.displayName = 'MediapipeFaceProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MidasDepthProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MidasDepthProcessor.tsx
deleted file mode 100644
index c9740c23d3..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MidasDepthProcessor.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { MidasDepthProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['midas_depth_image_processor'].buildDefaults();
-
-export const MidasDepthProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleAMultChanged = useCallback(
- (v: number) => {
- onChange({ ...config, a_mult: v });
- },
- [config, onChange]
- );
-
- const handleBgThChanged = useCallback(
- (v: number) => {
- onChange({ ...config, bg_th: v });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.amult')}
-
-
-
-
- {t('controlnet.bgth')}
-
-
-
-
- );
-});
-
-MidasDepthProcessor.displayName = 'MidasDepthProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MlsdImageProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MlsdImageProcessor.tsx
deleted file mode 100644
index d907cbe705..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/MlsdImageProcessor.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { MlsdProcessorConfig } from 'features/controlLayers/store/types';
-import { IMAGE_FILTERS } from 'features/controlLayers/store/types';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-const DEFAULTS = IMAGE_FILTERS['mlsd_image_processor'].buildDefaults();
-
-export const MlsdImageProcessor = memo(({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleThrDChanged = useCallback(
- (v: number) => {
- onChange({ ...config, thr_d: v });
- },
- [config, onChange]
- );
-
- const handleThrVChanged = useCallback(
- (v: number) => {
- onChange({ ...config, thr_v: v });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.w')}
-
-
-
-
- {t('controlnet.h')}
-
-
-
-
- );
-});
-
-MlsdImageProcessor.displayName = 'MlsdImageProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/PidiProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/PidiProcessor.tsx
deleted file mode 100644
index 6605baaadf..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/PidiProcessor.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
-import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAdapter/processors/types';
-import type { PidiProcessorConfig } from 'features/controlLayers/store/types';
-import type { ChangeEvent } from 'react';
-import { useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-
-import ProcessorWrapper from './ProcessorWrapper';
-
-type Props = ProcessorComponentProps;
-
-export const PidiProcessor = ({ onChange, config }: Props) => {
- const { t } = useTranslation();
-
- const handleScribbleChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, scribble: e.target.checked });
- },
- [config, onChange]
- );
-
- const handleSafeChanged = useCallback(
- (e: ChangeEvent) => {
- onChange({ ...config, safe: e.target.checked });
- },
- [config, onChange]
- );
-
- return (
-
-
- {t('controlnet.scribble')}
-
-
-
- {t('controlnet.safe')}
-
-
-
- );
-};
-
-PidiProcessor.displayName = 'PidiProcessor';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ProcessorWrapper.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ProcessorWrapper.tsx
deleted file mode 100644
index 2b2468703b..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/ProcessorWrapper.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Flex } from '@invoke-ai/ui-library';
-import type { PropsWithChildren } from 'react';
-import { memo } from 'react';
-
-type Props = PropsWithChildren;
-
-const ProcessorWrapper = (props: Props) => {
- return (
-
- {props.children}
-
- );
-};
-
-export default memo(ProcessorWrapper);
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/types.ts b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/types.ts
deleted file mode 100644
index a635e1f90f..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/processors/types.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import type { FilterConfig } from 'features/controlLayers/store/types';
-
-export type ProcessorComponentProps = {
- onChange: (config: T) => void;
- config: T;
-};
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx
index afaafe69f2..0527377d6f 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx
@@ -1,4 +1,5 @@
import {
+ Button,
Checkbox,
Flex,
FormControl,
@@ -11,7 +12,11 @@ import {
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { MaskOpacity } from 'features/controlLayers/components/MaskOpacity';
-import { clipToBboxChanged, invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice';
+import {
+ clipToBboxChanged,
+ invertScrollChanged,
+ rasterizationCachesInvalidated,
+} from 'features/controlLayers/store/canvasV2Slice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -30,6 +35,9 @@ const ControlLayersSettingsPopover = () => {
(e: ChangeEvent) => dispatch(clipToBboxChanged(e.target.checked)),
[dispatch]
);
+ const invalidateRasterizationCaches = useCallback(() => {
+ dispatch(rasterizationCachesInvalidated());
+ }, [dispatch]);
return (
@@ -47,6 +55,9 @@ const ControlLayersSettingsPopover = () => {
{t('unifiedCanvas.clipToBbox')}
+
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx
index 16a3c88d83..765608e6d7 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx
@@ -11,7 +11,7 @@ export const DeleteAllLayersButton = memo(() => {
const entityCount = useAppSelector((s) => {
return (
s.canvasV2.regions.entities.length +
- s.canvasV2.controlAdapters.entities.length +
+ // s.canvasV2.controlAdapters.entities.length +
s.canvasV2.ipAdapters.entities.length +
s.canvasV2.layers.entities.length
);
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Layer/Layer.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Layer/Layer.tsx
index 65c5501039..a91f104420 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Layer/Layer.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Layer/Layer.tsx
@@ -10,8 +10,6 @@ import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityI
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useMemo } from 'react';
-import { LayerOpacity } from './LayerOpacity';
-
type Props = {
id: string;
};
@@ -26,7 +24,6 @@ export const Layer = memo(({ id }: Props) => {
-
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerOpacity.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerOpacity.tsx
deleted file mode 100644
index 0d42659966..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/Layer/LayerOpacity.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import {
- CompositeNumberInput,
- CompositeSlider,
- Flex,
- FormControl,
- FormLabel,
- IconButton,
- Popover,
- PopoverArrow,
- PopoverBody,
- PopoverContent,
- PopoverTrigger,
-} from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { stopPropagation } from 'common/util/stopPropagation';
-import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
-import { layerOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
-import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiDropHalfFill } from 'react-icons/pi';
-
-const marks = [0, 25, 50, 75, 100];
-const formatPct = (v: number | string) => `${v} %`;
-
-export const LayerOpacity = memo(() => {
- const entityIdentifier = useEntityIdentifierContext();
- const { t } = useTranslation();
- const dispatch = useAppDispatch();
- const opacity = useAppSelector((s) => Math.round(selectLayerOrThrow(s.canvasV2, entityIdentifier.id).opacity * 100));
- const onChangeOpacity = useCallback(
- (v: number) => {
- dispatch(layerOpacityChanged({ id: entityIdentifier.id, opacity: v / 100 }));
- },
- [dispatch, entityIdentifier.id]
- );
- return (
-
-
- }
- variant="ghost"
- onDoubleClick={stopPropagation}
- />
-
-
-
-
-
-
- {t('controlLayers.opacity')}
-
-
-
-
-
-
-
- );
-});
-
-LayerOpacity.displayName = 'LayerOpacity';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityActionMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityActionMenuItems.tsx
index b75e87d828..0a812ce0c2 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityActionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityActionMenuItems.tsx
@@ -40,11 +40,6 @@ const getIndexAndCount = (
index: canvasV2.layers.entities.findIndex((entity) => entity.id === id),
count: canvasV2.layers.entities.length,
};
- } else if (type === 'control_adapter') {
- return {
- index: canvasV2.controlAdapters.entities.findIndex((entity) => entity.id === id),
- count: canvasV2.controlAdapters.entities.length,
- };
} else if (type === 'regional_guidance') {
return {
index: canvasV2.regions.entities.findIndex((entity) => entity.id === id),
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
index c41a95384c..c7fd177d75 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
@@ -1,6 +1,6 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { deepClone } from 'common/util/deepClone';
-import { caAdded, ipaAdded, rgIPAdapterAdded } from 'features/controlLayers/store/canvasV2Slice';
+import { ipaAdded, rgIPAdapterAdded } from 'features/controlLayers/store/canvasV2Slice';
import {
IMAGE_FILTERS,
initialControlNetV2,
@@ -37,7 +37,7 @@ export const useAddCALayer = () => {
const initialConfig = deepClone(model.type === 'controlnet' ? initialControlNetV2 : initialT2IAdapterV2);
const config = { ...initialConfig, model: zModelIdentifierField.parse(model), processorConfig };
- dispatch(caAdded({ config }));
+ // dispatch(caAdded({ config }));
}, [dispatch, model, baseModel]);
return [addCALayer, isDisabled] as const;
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasFilter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasFilter.ts
index dd1581612f..f718b78a61 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasFilter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasFilter.ts
@@ -100,7 +100,12 @@ export class CanvasFilter {
this.manager.stateApi.rasterizeEntity({
entityIdentifier: this.parent.getEntityIdentifier(),
imageObject: this.imageState,
- position: { x: Math.round(rect.x), y: Math.round(rect.y) },
+ rect: {
+ x: Math.round(rect.x),
+ y: Math.round(rect.y),
+ width: this.imageState.image.height,
+ height: this.imageState.image.width,
+ },
});
this.parent.renderer.showObjects();
this.manager.stateApi.$filteringEntity.set(null);
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts
index 6777bbd3ff..9d1f76ff4d 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts
@@ -9,20 +9,27 @@ import {
konvaNodeToBlob,
konvaNodeToImageData,
nanoid,
+ previewBlob,
} from 'features/controlLayers/konva/util';
import type { Extents, ExtentsResult, GetBboxTask, WorkerLogMessage } from 'features/controlLayers/konva/worker';
-import type { CanvasV2State, Coordinate, Dimensions, GenerationMode, Rect } from 'features/controlLayers/store/types';
+import type {
+ CanvasV2State,
+ Coordinate,
+ Dimensions,
+ GenerationMode,
+ ImageCache,
+ Rect,
+} from 'features/controlLayers/store/types';
import { isValidLayerWithoutControlAdapter } from 'features/nodes/util/graph/generation/addLayers';
import type Konva from 'konva';
-import { clamp } from 'lodash-es';
+import { clamp, isEqual } from 'lodash-es';
import { atom } from 'nanostores';
import type { Logger } from 'roarr';
import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
-import { assert } from 'tsafe';
import { CanvasBackground } from './CanvasBackground';
-import { CanvasControlAdapter } from './CanvasControlAdapter';
+import type { CanvasControlAdapter } from './CanvasControlAdapter';
import { CanvasLayerAdapter } from './CanvasLayerAdapter';
import { CanvasMaskAdapter } from './CanvasMaskAdapter';
import { CanvasPreview } from './CanvasPreview';
@@ -144,40 +151,15 @@ export class CanvasManager {
this._worker.postMessage(task, [data.buffer]);
}
- async renderControlAdapters() {
- const { entities } = this.stateApi.getControlAdaptersState();
-
- for (const canvasControlAdapter of this.controlAdapters.values()) {
- if (!entities.find((ca) => ca.id === canvasControlAdapter.id)) {
- canvasControlAdapter.destroy();
- this.controlAdapters.delete(canvasControlAdapter.id);
- }
- }
-
- for (const entity of entities) {
- let adapter = this.controlAdapters.get(entity.id);
- if (!adapter) {
- adapter = new CanvasControlAdapter(entity, this);
- this.controlAdapters.set(adapter.id, adapter);
- this.stage.add(adapter.konva.layer);
- }
- await adapter.render(entity);
- }
- }
-
arrangeEntities() {
- const { getLayersState, getControlAdaptersState, getRegionsState } = this.stateApi;
+ const { getLayersState, getRegionsState } = this.stateApi;
const layers = getLayersState().entities;
- const controlAdapters = getControlAdaptersState().entities;
const regions = getRegionsState().entities;
let zIndex = 0;
this.background.konva.layer.zIndex(++zIndex);
for (const layer of layers) {
this.layers.get(layer.id)?.konva.layer.zIndex(++zIndex);
}
- for (const ca of controlAdapters) {
- this.controlAdapters.get(ca.id)?.konva.layer.zIndex(++zIndex);
- }
for (const rg of regions) {
this.regions.get(rg.id)?.konva.layer.zIndex(++zIndex);
}
@@ -358,16 +340,6 @@ export class CanvasManager {
});
}
- if (
- this._isFirstRender ||
- state.controlAdapters.entities !== this._prevState.controlAdapters.entities ||
- state.tool.selected !== this._prevState.tool.selected ||
- state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
- ) {
- this.log.debug('Rendering control adapters');
- await this.renderControlAdapters();
- }
-
this.stateApi.$toolState.set(state.tool);
this.stateApi.$selectedEntityIdentifier.set(state.selectedEntityIdentifier);
this.stateApi.$selectedEntity.set(this.stateApi.getSelectedEntity());
@@ -382,12 +354,7 @@ export class CanvasManager {
await this.preview.bbox.render();
}
- if (
- this._isFirstRender ||
- state.layers !== this._prevState.layers ||
- state.controlAdapters !== this._prevState.controlAdapters ||
- state.regions !== this._prevState.regions
- ) {
+ if (this._isFirstRender || state.layers !== this._prevState.layers || state.regions !== this._prevState.regions) {
// this.log.debug('Updating entity bboxes');
// debouncedUpdateBboxes(stage, canvasV2.layers, canvasV2.controlAdapters, canvasV2.regions, onBboxChanged);
}
@@ -400,7 +367,6 @@ export class CanvasManager {
if (
this._isFirstRender ||
state.layers.entities !== this._prevState.layers.entities ||
- state.controlAdapters.entities !== this._prevState.controlAdapters.entities ||
state.regions.entities !== this._prevState.regions.entities ||
state.inpaintMask !== this._prevState.inpaintMask ||
state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
@@ -569,45 +535,43 @@ export class CanvasManager {
return konvaNodeToImageData(this.getCompositeLayerStageClone(), rect);
};
- getCompositeLayerImageDTO = async (rect?: Rect): Promise => {
+ getCompositeRasterizedImageCache = (rect: Rect): ImageCache | null => {
+ const layerState = this.stateApi.getLayersState();
+ const imageCache = layerState.compositeRasterizationCache.find((cache) => isEqual(cache.rect, rect));
+ return imageCache ?? null;
+ };
+
+ getCompositeLayerImageDTO = async (rect: Rect): Promise => {
+ let imageDTO: ImageDTO | null = null;
+ const compositeRasterizedImageCache = this.getCompositeRasterizedImageCache(rect);
+
+ if (compositeRasterizedImageCache) {
+ imageDTO = await getImageDTO(compositeRasterizedImageCache.imageName);
+ if (imageDTO) {
+ this.log.trace({ rect, compositeRasterizedImageCache, imageDTO }, 'Using cached composite rasterized image');
+ return imageDTO;
+ }
+ }
+
+ this.log.trace({ rect }, 'Rasterizing composite layer');
+
const blob = await this.getCompositeLayerBlob(rect);
- const imageDTO = await uploadImage(blob, 'composite-layer.png', 'general', true);
- this.stateApi.setLayerImageCache(imageDTO);
+
+ if (this._isDebugging) {
+ previewBlob(blob, 'Rasterized entity');
+ }
+
+ imageDTO = await uploadImage(blob, 'composite-layer.png', 'general', true);
+ this.stateApi.compositeLayerRasterized({ imageName: imageDTO.image_name, rect });
return imageDTO;
};
getInpaintMaskBlob = (rect?: Rect): Promise => {
- return this.inpaintMask.renderer.getBlob({ rect });
+ return this.inpaintMask.renderer.getBlob(rect);
};
getInpaintMaskImageData = (rect?: Rect): ImageData => {
- return this.inpaintMask.renderer.getImageData({ rect });
- };
-
- getInpaintMaskImageDTO = async (rect?: Rect): Promise => {
- const blob = await this.inpaintMask.renderer.getBlob({ rect });
- const imageDTO = await uploadImage(blob, 'inpaint-mask.png', 'mask', true);
- this.stateApi.setInpaintMaskImageCache(imageDTO);
- return imageDTO;
- };
-
- getRegionMaskImageDTO = async (id: string, rect?: Rect): Promise => {
- const region = this.stateApi.getEntity({ id, type: 'regional_guidance' });
- assert(region?.type === 'regional_guidance');
- if (region.state.imageCache) {
- const imageDTO = await getImageDTO(region.state.imageCache);
- if (imageDTO) {
- return imageDTO;
- }
- }
- return region.adapter.renderer.getImageDTO({
- rect,
- category: 'other',
- is_intermediate: true,
- onUploaded: (imageDTO) => {
- this.stateApi.setRegionMaskImageCache(region.state.id, imageDTO);
- },
- });
+ return this.inpaintMask.renderer.getImageData(rect);
};
getGenerationMode(): GenerationMode {
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts
index 4f6b16cda3..726ec21cf5 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts
@@ -14,14 +14,16 @@ import type {
CanvasEraserLineState,
CanvasImageState,
CanvasRectState,
+ ImageCache,
Rect,
RgbColor,
} from 'features/controlLayers/store/types';
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
import Konva from 'konva';
+import { isEqual } from 'lodash-es';
import type { Logger } from 'roarr';
import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
-import type { ImageCategory, ImageDTO } from 'services/api/types';
+import type { ImageDTO } from 'services/api/types';
import { assert } from 'tsafe';
/**
@@ -62,12 +64,6 @@ export class CanvasObjectRenderer {
*/
renderers: Map = new Map();
- /**
- * A cache of the rasterized image data URL. If the cache is null, the parent has not been rasterized since its last
- * change.
- */
- rasterizedImageCache: string | null = null;
-
/**
* A object containing singleton Konva nodes.
*/
@@ -168,19 +164,6 @@ export class CanvasObjectRenderer {
didRender = (await this.renderObject(this.buffer)) || didRender;
}
- if (didRender && this.rasterizedImageCache) {
- const hasOneObject = this.renderers.size === 1;
- const firstObject = Array.from(this.renderers.values())[0];
- if (
- hasOneObject &&
- firstObject &&
- firstObject.state.type === 'image' &&
- firstObject.state.image.image_name !== this.rasterizedImageCache
- ) {
- this.rasterizedImageCache = null;
- }
- }
-
return didRender;
};
@@ -376,29 +359,37 @@ export class CanvasObjectRenderer {
return this.renderers.size > 0 || this.buffer !== null;
};
+ getRasterizedImageCache = (rect: Rect): ImageCache | null => {
+ const imageCache = this.parent.state.rasterizationCache.find((cache) => isEqual(cache.rect, rect));
+ return imageCache ?? null;
+ };
+
/**
- * Rasterizes the parent entity. If the entity has a rasterization cache, the cached image is returned after
- * validating that it exists on the server.
+ * Rasterizes the parent entity. If the entity has a rasterization cache for the given rect, the cached image is
+ * returned. Otherwise, the entity is rasterized and the image is uploaded to the server.
*
- * The rasterization cache is reset when the entity's objects change. The buffer object is not considered part of the
- * entity's objects for this purpose.
+ * The rasterization cache is reset when the entity's state changes. The buffer object is not considered part of the
+ * entity state for this purpose as it is a temporary object.
*
+ * @param rect The rect to rasterize. If omitted, the entity's full rect will be used.
* @returns A promise that resolves to the rasterized image DTO.
*/
- rasterize = async (): Promise => {
- this.log.debug('Rasterizing entity');
-
+ rasterize = async (rect?: Rect): Promise => {
+ rect = rect ?? this.parent.transformer.getRelativeRect();
let imageDTO: ImageDTO | null = null;
- if (this.rasterizedImageCache) {
- imageDTO = await getImageDTO(this.rasterizedImageCache);
+ const rasterizedImageCache = this.getRasterizedImageCache(rect);
+
+ if (rasterizedImageCache) {
+ imageDTO = await getImageDTO(rasterizedImageCache.imageName);
+ if (imageDTO) {
+ this.log.trace({ rect, rasterizedImageCache, imageDTO }, 'Using cached rasterized image');
+ return imageDTO;
+ }
}
- if (imageDTO) {
- return imageDTO;
- }
+ this.log.trace({ rect }, 'Rasterizing entity');
- const rect = this.parent.transformer.getRelativeRect();
- const blob = await this.getBlob({ rect });
+ const blob = await this.getBlob(rect);
if (this.manager._isDebugging) {
previewBlob(blob, 'Rasterized entity');
}
@@ -408,41 +399,20 @@ export class CanvasObjectRenderer {
this.manager.stateApi.rasterizeEntity({
entityIdentifier: this.parent.getEntityIdentifier(),
imageObject,
- position: { x: Math.round(rect.x), y: Math.round(rect.y) },
+ rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: imageDTO.width, height: imageDTO.height },
});
- this.rasterizedImageCache = imageDTO.image_name;
-
return imageDTO;
};
- getBlob = ({ rect }: { rect?: Rect }): Promise => {
+ getBlob = (rect?: Rect): Promise => {
return konvaNodeToBlob(this.konva.objectGroup.clone(), rect);
};
- getImageData = ({ rect }: { rect?: Rect }): ImageData => {
+ getImageData = (rect?: Rect): ImageData => {
return konvaNodeToImageData(this.konva.objectGroup.clone(), rect);
};
- getImageDTO = async ({
- rect,
- category,
- is_intermediate,
- onUploaded,
- }: {
- rect?: Rect;
- category: ImageCategory;
- is_intermediate: boolean;
- onUploaded?: (imageDTO: ImageDTO) => void;
- }): Promise => {
- const blob = await this.getBlob({ rect });
- const imageDTO = await uploadImage(blob, `${this.id}.png`, category, is_intermediate);
- if (onUploaded) {
- onUploaded(imageDTO);
- }
- return imageDTO;
- };
-
/**
* Destroys this renderer and all of its object renderers.
*/
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts
index c880f60f59..4e20aebbe1 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts
@@ -26,9 +26,7 @@ import {
entityReset,
entitySelected,
eraserWidthChanged,
- imImageCacheChanged,
- layerImageCacheChanged,
- rgImageCacheChanged,
+ layerCompositeRasterized,
toolBufferChanged,
toolChanged,
} from 'features/controlLayers/store/canvasV2Slice';
@@ -51,7 +49,6 @@ import type {
import { RGBA_RED } from 'features/controlLayers/store/types';
import type { WritableAtom } from 'nanostores';
import { atom } from 'nanostores';
-import type { ImageDTO } from 'services/api/types';
type EntityStateAndAdapter =
| {
@@ -118,6 +115,10 @@ export class CanvasStateApi {
log.trace(arg, 'Rasterizing entity');
this._store.dispatch(entityRasterized(arg));
};
+ compositeLayerRasterized = (arg: { imageName: string; rect: Rect }) => {
+ log.trace(arg, 'Composite layer rasterized');
+ this._store.dispatch(layerCompositeRasterized(arg));
+ };
setSelectedEntity = (arg: EntityIdentifierPayload) => {
log.trace({ arg }, 'Setting selected entity');
this._store.dispatch(entitySelected(arg));
@@ -134,18 +135,6 @@ export class CanvasStateApi {
log.trace({ width }, 'Setting eraser width');
this._store.dispatch(eraserWidthChanged(width));
};
- setRegionMaskImageCache = (id: string, imageDTO: ImageDTO) => {
- log.trace({ id, imageDTO }, 'Setting region mask image cache');
- this._store.dispatch(rgImageCacheChanged({ id, imageDTO }));
- };
- setInpaintMaskImageCache = (imageDTO: ImageDTO) => {
- log.trace({ imageDTO }, 'Setting inpaint mask image cache');
- this._store.dispatch(imImageCacheChanged({ imageDTO }));
- };
- setLayerImageCache = (imageDTO: ImageDTO) => {
- log.trace({ imageDTO }, 'Setting layer image cache');
- this._store.dispatch(layerImageCacheChanged({ imageDTO }));
- };
setTool = (tool: Tool) => {
log.trace({ tool }, 'Setting tool');
this._store.dispatch(toolChanged(tool));
@@ -171,9 +160,6 @@ export class CanvasStateApi {
getLayersState = () => {
return this.getState().layers;
};
- getControlAdaptersState = () => {
- return this.getState().controlAdapters;
- };
getInpaintMaskState = () => {
return this.getState().inpaintMask;
};
@@ -202,9 +188,6 @@ export class CanvasStateApi {
if (identifier.type === 'layer') {
entityState = state.layers.entities.find((i) => i.id === identifier.id) ?? null;
entityAdapter = this.manager.layers.get(identifier.id) ?? null;
- } else if (identifier.type === 'control_adapter') {
- entityState = state.controlAdapters.entities.find((i) => i.id === identifier.id) ?? null;
- entityAdapter = this.manager.controlAdapters.get(identifier.id) ?? null;
} else if (identifier.type === 'regional_guidance') {
entityState = state.regions.entities.find((i) => i.id === identifier.id) ?? null;
entityAdapter = this.manager.regions.get(identifier.id) ?? null;
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts
index 0470b36281..1c69a67a97 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts
@@ -5,7 +5,6 @@ import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/uti
import { deepClone } from 'common/util/deepClone';
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
-import { controlAdaptersReducers } from 'features/controlLayers/store/controlAdaptersReducers';
import { inpaintMaskReducers } from 'features/controlLayers/store/inpaintMaskReducers';
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
import { layersReducers } from 'features/controlLayers/store/layersReducers';
@@ -18,13 +17,16 @@ import { toolReducers } from 'features/controlLayers/store/toolReducers';
import { getScaledBoundingBoxDimensions } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
import { initialAspectRatioState } from 'features/parameters/components/DocumentSize/constants';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
-import { pick } from 'lodash-es';
+import { isEqual, pick } from 'lodash-es';
import { atom } from 'nanostores';
import type { InvocationDenoiseProgressEvent } from 'services/events/types';
import { assert } from 'tsafe';
import type {
CanvasEntityIdentifier,
+ CanvasInpaintMaskState,
+ CanvasLayerState,
+ CanvasRegionalGuidanceState,
CanvasV2State,
Coordinate,
EntityBrushLineAddedPayload,
@@ -41,8 +43,7 @@ import { IMAGE_FILTERS, RGBA_RED } from './types';
const initialState: CanvasV2State = {
_version: 3,
selectedEntityIdentifier: null,
- layers: { entities: [], imageCache: null },
- controlAdapters: { entities: [] },
+ layers: { entities: [], compositeRasterizationCache: [] },
ipAdapters: { entities: [] },
regions: { entities: [] },
loras: [],
@@ -50,7 +51,7 @@ const initialState: CanvasV2State = {
id: 'inpaint_mask',
type: 'inpaint_mask',
fill: RGBA_RED,
- imageCache: null,
+ rasterizationCache: [],
isEnabled: true,
objects: [],
position: {
@@ -144,8 +145,6 @@ export function selectEntity(state: CanvasV2State, { id, type }: CanvasEntityIde
switch (type) {
case 'layer':
return state.layers.entities.find((layer) => layer.id === id);
- case 'control_adapter':
- return state.controlAdapters.entities.find((ca) => ca.id === id);
case 'inpaint_mask':
return state.inpaintMask;
case 'regional_guidance':
@@ -157,13 +156,37 @@ export function selectEntity(state: CanvasV2State, { id, type }: CanvasEntityIde
}
}
+const invalidateCompositeRasterizationCache = (entity: CanvasLayerState, state: CanvasV2State) => {
+ if (entity.controlAdapter === null) {
+ state.layers.compositeRasterizationCache = [];
+ }
+};
+
+const invalidateRasterizationCaches = (
+ entity: CanvasLayerState | CanvasInpaintMaskState | CanvasRegionalGuidanceState,
+ state: CanvasV2State
+) => {
+ // TODO(psyche): We can be more efficient and only invalidate caches when the entity's changes intersect with the
+ // cached rect.
+
+ // Reset the entity's rasterization cache
+ entity.rasterizationCache = [];
+
+ // When an individual layer has its cache reset, we must also reset the composite rasterization cache because the
+ // layer's image data will contribute to the composite layer's image data.
+ // If the layer is used as a control layer, it will not contribute to the composite layer, so we do not need to reset
+ // its cache.
+ if (entity.type === 'layer') {
+ invalidateCompositeRasterizationCache(entity, state);
+ }
+};
+
export const canvasV2Slice = createSlice({
name: 'canvasV2',
initialState,
reducers: {
...layersReducers,
...ipAdaptersReducers,
- ...controlAdaptersReducers,
...regionsReducers,
...lorasReducers,
...paramsReducers,
@@ -182,16 +205,11 @@ export const canvasV2Slice = createSlice({
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (entity.type === 'layer') {
+ } else if (entity.type === 'layer' || entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
entity.isEnabled = true;
entity.objects = [];
entity.position = { x: 0, y: 0 };
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.isEnabled = true;
- entity.objects = [];
- entity.position = { x: 0, y: 0 };
- entity.imageCache = null;
+ invalidateRasterizationCaches(entity, state);
} else {
assert(false, 'Not implemented');
}
@@ -209,32 +227,28 @@ export const canvasV2Slice = createSlice({
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (entity.type === 'layer') {
+ }
+
+ if (entity.type === 'layer' || entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
entity.position = position;
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.position = position;
- entity.imageCache = null;
- } else {
- assert(false, 'Not implemented');
+ // When an entity is moved, we need to invalidate the rasterization caches.
+ invalidateRasterizationCaches(entity, state);
}
},
entityRasterized: (state, action: PayloadAction) => {
- const { entityIdentifier, imageObject, position } = action.payload;
+ const { entityIdentifier, imageObject, rect } = action.payload;
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (entity.type === 'layer') {
+ }
+
+ if (entity.type === 'layer' || entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
entity.objects = [imageObject];
- entity.position = position;
- entity.imageCache = imageObject.image.image_name;
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.objects = [imageObject];
- entity.position = position;
- entity.imageCache = imageObject.image.image_name;
- } else {
- assert(false, 'Not implemented');
+ entity.position = { x: rect.x, y: rect.y };
+ // Remove the cache for the given rect. This should never happen, because we should never rasterize the same
+ // rect twice. Just in case, we remove the old cache.
+ entity.rasterizationCache = entity.rasterizationCache.filter((cache) => !isEqual(cache.rect, rect));
+ entity.rasterizationCache.push({ imageName: imageObject.image.image_name, rect });
}
},
entityBrushLineAdded: (state, action: PayloadAction) => {
@@ -242,14 +256,12 @@ export const canvasV2Slice = createSlice({
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (entity.type === 'layer') {
+ }
+
+ if (entity.type === 'layer' || entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
entity.objects.push(brushLine);
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.objects.push(brushLine);
- entity.imageCache = null;
- } else {
- assert(false, 'Not implemented');
+ // When adding a brush line, we need to invalidate the rasterization caches.
+ invalidateRasterizationCaches(entity, state);
}
},
entityEraserLineAdded: (state, action: PayloadAction) => {
@@ -257,12 +269,10 @@ export const canvasV2Slice = createSlice({
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (entity.type === 'layer') {
+ } else if (entity.type === 'layer' || entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
entity.objects.push(eraserLine);
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.objects.push(eraserLine);
- entity.imageCache = null;
+ // When adding an eraser line, we need to invalidate the rasterization caches.
+ invalidateRasterizationCaches(entity, state);
} else {
assert(false, 'Not implemented');
}
@@ -274,19 +284,21 @@ export const canvasV2Slice = createSlice({
return;
} else if (entity.type === 'layer') {
entity.objects.push(rect);
- state.layers.imageCache = null;
- } else if (entity.type === 'inpaint_mask' || entity.type === 'regional_guidance') {
- entity.objects.push(rect);
- entity.imageCache = null;
+ // When adding an eraser line, we need to invalidate the rasterization caches.
+ invalidateRasterizationCaches(entity, state);
} else {
assert(false, 'Not implemented');
}
},
entityDeleted: (state, action: PayloadAction) => {
const { entityIdentifier } = action.payload;
+ const entity = selectEntity(state, entityIdentifier);
+ if (entity?.type === 'layer') {
+ // When a layer is deleted, we may need to invalidate the composite rasterization cache.
+ invalidateCompositeRasterizationCache(entity, state);
+ }
if (entityIdentifier.type === 'layer') {
state.layers.entities = state.layers.entities.filter((layer) => layer.id !== entityIdentifier.id);
- state.layers.imageCache = null;
} else if (entityIdentifier.type === 'regional_guidance') {
state.regions.entities = state.regions.entities.filter((rg) => rg.id !== entityIdentifier.id);
} else {
@@ -301,11 +313,10 @@ export const canvasV2Slice = createSlice({
}
if (entity.type === 'layer') {
moveOneToEnd(state.layers.entities, entity);
- state.layers.imageCache = null;
+ // When arranging an entity, we may need to invalidate the composite rasterization cache.
+ invalidateCompositeRasterizationCache(entity, state);
} else if (entity.type === 'regional_guidance') {
moveOneToEnd(state.regions.entities, entity);
- } else if (entity.type === 'control_adapter') {
- moveOneToEnd(state.controlAdapters.entities, entity);
}
},
entityArrangedToFront: (state, action: PayloadAction) => {
@@ -316,11 +327,10 @@ export const canvasV2Slice = createSlice({
}
if (entity.type === 'layer') {
moveToEnd(state.layers.entities, entity);
- state.layers.imageCache = null;
+ // When arranging an entity, we may need to invalidate the composite rasterization cache.
+ invalidateCompositeRasterizationCache(entity, state);
} else if (entity.type === 'regional_guidance') {
moveToEnd(state.regions.entities, entity);
- } else if (entity.type === 'control_adapter') {
- moveToEnd(state.controlAdapters.entities, entity);
}
},
entityArrangedBackwardOne: (state, action: PayloadAction) => {
@@ -331,11 +341,10 @@ export const canvasV2Slice = createSlice({
}
if (entity.type === 'layer') {
moveOneToStart(state.layers.entities, entity);
- state.layers.imageCache = null;
+ // When arranging an entity, we may need to invalidate the composite rasterization cache.
+ invalidateCompositeRasterizationCache(entity, state);
} else if (entity.type === 'regional_guidance') {
moveOneToStart(state.regions.entities, entity);
- } else if (entity.type === 'control_adapter') {
- moveOneToStart(state.controlAdapters.entities, entity);
}
},
entityArrangedToBack: (state, action: PayloadAction) => {
@@ -346,19 +355,17 @@ export const canvasV2Slice = createSlice({
}
if (entity.type === 'layer') {
moveToStart(state.layers.entities, entity);
- state.layers.imageCache = null;
+ // When arranging an entity, we may need to invalidate the composite rasterization cache.
+ invalidateCompositeRasterizationCache(entity, state);
} else if (entity.type === 'regional_guidance') {
moveToStart(state.regions.entities, entity);
- } else if (entity.type === 'control_adapter') {
- moveToStart(state.controlAdapters.entities, entity);
}
},
allEntitiesDeleted: (state) => {
state.regions.entities = [];
state.layers.entities = [];
- state.layers.imageCache = null;
+ state.layers.compositeRasterizationCache = [];
state.ipAdapters.entities = [];
- state.controlAdapters.entities = [];
},
filterSelected: (state, action: PayloadAction<{ type: FilterConfig['type'] }>) => {
state.filter.config = IMAGE_FILTERS[action.payload.type].buildDefaults();
@@ -366,6 +373,23 @@ export const canvasV2Slice = createSlice({
filterConfigChanged: (state, action: PayloadAction<{ config: FilterConfig }>) => {
state.filter.config = action.payload.config;
},
+ rasterizationCachesInvalidated: (state) => {
+ // Invalidate the rasterization caches for all entities.
+
+ // Layers & composite layer
+ state.layers.compositeRasterizationCache = [];
+ for (const layer of state.layers.entities) {
+ layer.rasterizationCache = [];
+ }
+
+ // Regions
+ for (const region of state.regions.entities) {
+ region.rasterizationCache = [];
+ }
+
+ // Inpaint mask
+ state.inpaintMask.rasterizationCache = [];
+ },
canvasReset: (state) => {
state.bbox = deepClone(initialState.bbox);
const optimalDimension = getOptimalDimension(state.params.model);
@@ -374,7 +398,6 @@ export const canvasV2Slice = createSlice({
const size = pick(state.bbox.rect, 'width', 'height');
state.bbox.scaledSize = getScaledBoundingBoxDimensions(size, optimalDimension);
- state.controlAdapters = deepClone(initialState.controlAdapters);
state.ipAdapters = deepClone(initialState.ipAdapters);
state.layers = deepClone(initialState.layers);
state.regions = deepClone(initialState.regions);
@@ -397,6 +420,7 @@ export const {
allEntitiesDeleted,
clipToBboxChanged,
canvasReset,
+ rasterizationCachesInvalidated,
// All entities
entitySelected,
entityReset,
@@ -424,14 +448,13 @@ export const {
// layers
layerAdded,
layerRecalled,
- layerOpacityChanged,
layerAllDeleted,
- layerImageCacheChanged,
layerUsedAsControlChanged,
layerControlAdapterModelChanged,
layerControlAdapterControlModeChanged,
layerControlAdapterWeightChanged,
layerControlAdapterBeginEndStepPctChanged,
+ layerCompositeRasterized,
// IP Adapters
ipaAdded,
ipaRecalled,
@@ -444,20 +467,6 @@ export const {
ipaCLIPVisionModelChanged,
ipaWeightChanged,
ipaBeginEndStepPctChanged,
- // Control Adapters
- caAdded,
- caAllDeleted,
- caOpacityChanged,
- caRecalled,
- caImageChanged,
- caProcessedImageChanged,
- caModelChanged,
- caControlModeChanged,
- caProcessorConfigChanged,
- caFilterChanged,
- caProcessorPendingBatchIdChanged,
- caWeightChanged,
- caBeginEndStepPctChanged,
// Regions
rgAdded,
rgRecalled,
@@ -465,7 +474,6 @@ export const {
rgPositivePromptChanged,
rgNegativePromptChanged,
rgFillChanged,
- rgImageCacheChanged,
rgAutoNegativeChanged,
rgIPAdapterAdded,
rgIPAdapterDeleted,
@@ -522,7 +530,6 @@ export const {
// Inpaint mask
imRecalled,
imFillChanged,
- imImageCacheChanged,
// Staging
sessionStartedStaging,
sessionImageStaged,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts
deleted file mode 100644
index a71b2fcedb..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
-import { zModelIdentifierField } from 'features/nodes/types/common';
-import { isEqual } from 'lodash-es';
-import type { ControlNetModelConfig, ImageDTO, T2IAdapterModelConfig } from 'services/api/types';
-import { assert } from 'tsafe';
-import { v4 as uuidv4 } from 'uuid';
-
-import type {
- CanvasControlAdapterState,
- CanvasControlNetState,
- CanvasT2IAdapterState,
- CanvasV2State,
- ControlModeV2,
- ControlNetConfig,
- Filter,
- FilterConfig,
- T2IAdapterConfig,
-} from './types';
-import { buildControlAdapterProcessorV2, imageDTOToImageObject } from './types';
-
-export const selectCA = (state: CanvasV2State, id: string) => state.controlAdapters.entities.find((ca) => ca.id === id);
-export const selectCAOrThrow = (state: CanvasV2State, id: string) => {
- const ca = selectCA(state, id);
- assert(ca, `Control Adapter with id ${id} not found`);
- return ca;
-};
-
-export const controlAdaptersReducers = {
- caAdded: {
- reducer: (state, action: PayloadAction<{ id: string; config: ControlNetConfig | T2IAdapterConfig }>) => {
- const { id, config } = action.payload;
- state.controlAdapters.entities.push({
- id,
- type: 'control_adapter',
- position: { x: 0, y: 0 },
- bbox: null,
- bboxNeedsUpdate: false,
- isEnabled: true,
- opacity: 1,
- filters: ['LightnessToAlphaFilter'],
- processorPendingBatchId: null,
- ...config,
- });
- state.selectedEntityIdentifier = { type: 'control_adapter', id };
- },
- prepare: (payload: { config: ControlNetConfig | T2IAdapterConfig }) => ({
- payload: { id: uuidv4(), ...payload },
- }),
- },
- caRecalled: (state, action: PayloadAction<{ data: CanvasControlAdapterState }>) => {
- const { data } = action.payload;
- state.controlAdapters.entities.push(data);
- state.selectedEntityIdentifier = { type: 'control_adapter', id: data.id };
- },
- caAllDeleted: (state) => {
- state.controlAdapters.entities = [];
- },
- caOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
- const { id, opacity } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.opacity = opacity;
- },
- caImageChanged: {
- reducer: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null; objectId: string }>) => {
- const { id, imageDTO, objectId } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.bbox = null;
- ca.bboxNeedsUpdate = true;
- ca.isEnabled = true;
- if (imageDTO) {
- const newImageObject = imageDTOToImageObject(imageDTO, { filters: ca.filters });
- if (isEqual(newImageObject, ca.imageObject)) {
- return;
- }
- ca.imageObject = newImageObject;
- ca.processedImageObject = null;
- } else {
- ca.imageObject = null;
- ca.processedImageObject = null;
- }
- },
- prepare: (payload: { id: string; imageDTO: ImageDTO | null }) => ({ payload: { ...payload, objectId: uuidv4() } }),
- },
- caProcessedImageChanged: {
- reducer: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null; objectId: string }>) => {
- const { id, imageDTO, objectId } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.bbox = null;
- ca.bboxNeedsUpdate = true;
- ca.isEnabled = true;
- ca.processedImageObject = imageDTO ? imageDTOToImageObject(imageDTO, { filters: ca.filters }) : null;
- },
- prepare: (payload: { id: string; imageDTO: ImageDTO | null }) => ({ payload: { ...payload, objectId: uuidv4() } }),
- },
- caModelChanged: (
- state,
- action: PayloadAction<{
- id: string;
- modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null;
- }>
- ) => {
- const { id, modelConfig } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- if (!modelConfig) {
- ca.model = null;
- return;
- }
- ca.model = zModelIdentifierField.parse(modelConfig);
-
- const candidateProcessorConfig = buildControlAdapterProcessorV2(modelConfig);
- if (candidateProcessorConfig?.type !== ca.processorConfig?.type) {
- // The processor has changed. For example, the previous model was a Canny model and the new model is a Depth
- // model. We need to use the new processor.
- ca.processedImageObject = null;
- ca.processorConfig = candidateProcessorConfig;
- }
-
- // We may need to convert the CA to match the model
- if (ca.adapterType === 't2i_adapter' && ca.model.type === 'controlnet') {
- const convertedCA: CanvasControlNetState = { ...ca, adapterType: 'controlnet', controlMode: 'balanced' };
- state.controlAdapters.entities.splice(state.controlAdapters.entities.indexOf(ca), 1, convertedCA);
- } else if (ca.adapterType === 'controlnet' && ca.model.type === 't2i_adapter') {
- const { controlMode: _, ...rest } = ca;
- const convertedCA: CanvasT2IAdapterState = { ...rest, adapterType: 't2i_adapter' };
- state.controlAdapters.entities.splice(state.controlAdapters.entities.indexOf(ca), 1, convertedCA);
- }
- },
- caControlModeChanged: (state, action: PayloadAction<{ id: string; controlMode: ControlModeV2 }>) => {
- const { id, controlMode } = action.payload;
- const ca = selectCA(state, id);
- if (!ca || ca.adapterType !== 'controlnet') {
- return;
- }
- ca.controlMode = controlMode;
- },
- caProcessorConfigChanged: (state, action: PayloadAction<{ id: string; processorConfig: FilterConfig | null }>) => {
- const { id, processorConfig } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.processorConfig = processorConfig;
- if (!processorConfig) {
- ca.processedImageObject = null;
- }
- },
- caFilterChanged: (state, action: PayloadAction<{ id: string; filters: Filter[] }>) => {
- const { id, filters } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.filters = filters;
- if (ca.imageObject) {
- ca.imageObject.filters = filters;
- }
- if (ca.processedImageObject) {
- ca.processedImageObject.filters = filters;
- }
- },
- caProcessorPendingBatchIdChanged: (state, action: PayloadAction<{ id: string; batchId: string | null }>) => {
- const { id, batchId } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.processorPendingBatchId = batchId;
- },
- caWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
- const { id, weight } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.weight = weight;
- },
- caBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
- const { id, beginEndStepPct } = action.payload;
- const ca = selectCA(state, id);
- if (!ca) {
- return;
- }
- ca.beginEndStepPct = beginEndStepPct;
- },
-} satisfies SliceCaseReducers;
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts
index ae7e52805d..2950dc60b7 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts
@@ -1,7 +1,5 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import type { CanvasInpaintMaskState, CanvasV2State } from 'features/controlLayers/store/types';
-import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
-import type { ImageDTO } from 'services/api/types';
import type { RgbColor } from './types';
@@ -15,8 +13,4 @@ export const inpaintMaskReducers = {
const { fill } = action.payload;
state.inpaintMask.fill = fill;
},
- imImageCacheChanged: (state, action: PayloadAction<{ imageDTO: ImageDTO | null }>) => {
- const { imageDTO } = action.payload;
- state.inpaintMask.imageCache = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
- },
} satisfies SliceCaseReducers;
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts
index 1d0407ec94..8348848168 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts
@@ -1,12 +1,11 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { zModelIdentifierField } from 'features/nodes/types/common';
-import { merge } from 'lodash-es';
-import type { ControlNetModelConfig, ImageDTO, T2IAdapterModelConfig } from 'services/api/types';
+import { isEqual, merge } from 'lodash-es';
+import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
import { assert } from 'tsafe';
-import type { CanvasLayerState, CanvasV2State, ControlModeV2, ControlNetConfig, T2IAdapterConfig } from './types';
-import { imageDTOToImageWithDims } from './types';
+import type { CanvasLayerState, CanvasV2State, ControlModeV2, ControlNetConfig, Rect, T2IAdapterConfig } from './types';
export const selectLayer = (state: CanvasV2State, id: string) => state.layers.entities.find((layer) => layer.id === id);
export const selectLayerOrThrow = (state: CanvasV2State, id: string) => {
@@ -29,7 +28,7 @@ export const layersReducers = {
objects: [],
opacity: 1,
position: { x: 0, y: 0 },
- imageCache: null,
+ rasterizationCache: [],
controlAdapter: null,
};
merge(layer, overrides);
@@ -37,7 +36,11 @@ export const layersReducers = {
if (isSelected) {
state.selectedEntityIdentifier = { type: 'layer', id };
}
- state.layers.imageCache = null;
+
+ if (layer.objects.length > 0) {
+ // This new layer will change the composite layer's image data. Invalidate the cache.
+ state.layers.compositeRasterizationCache = [];
+ }
},
prepare: (payload: { overrides?: Partial; isSelected?: boolean }) => ({
payload: { ...payload, id: getPrefixedId('layer') },
@@ -47,24 +50,20 @@ export const layersReducers = {
const { data } = action.payload;
state.layers.entities.push(data);
state.selectedEntityIdentifier = { type: 'layer', id: data.id };
- state.layers.imageCache = null;
+ if (data.objects.length > 0) {
+ // This new layer will change the composite layer's image data. Invalidate the cache.
+ state.layers.compositeRasterizationCache = [];
+ }
},
layerAllDeleted: (state) => {
state.layers.entities = [];
- state.layers.imageCache = null;
+ state.layers.compositeRasterizationCache = [];
},
- layerOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
- const { id, opacity } = action.payload;
- const layer = selectLayer(state, id);
- if (!layer) {
- return;
- }
- layer.opacity = opacity;
- state.layers.imageCache = null;
- },
- layerImageCacheChanged: (state, action: PayloadAction<{ imageDTO: ImageDTO | null }>) => {
- const { imageDTO } = action.payload;
- state.layers.imageCache = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
+ layerCompositeRasterized: (state, action: PayloadAction<{ imageName: string; rect: Rect }>) => {
+ state.layers.compositeRasterizationCache = state.layers.compositeRasterizationCache.filter(
+ (cache) => !isEqual(cache.rect, action.payload.rect)
+ );
+ state.layers.compositeRasterizationCache.push(action.payload);
},
layerUsedAsControlChanged: (
state,
@@ -76,6 +75,8 @@ export const layersReducers = {
return;
}
layer.controlAdapter = controlAdapter;
+ // The composite layer's image data will change when the layer is used as control (or not). Invalidate the cache.
+ state.layers.compositeRasterizationCache = [];
},
layerControlAdapterModelChanged: (
state,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts
index 372d570e53..fa7a84735e 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts
@@ -54,7 +54,7 @@ export const regionsReducers = {
positivePrompt: '',
negativePrompt: null,
ipAdapters: [],
- imageCache: null,
+ rasterizationCache: [],
};
state.regions.entities.push(rg);
state.selectedEntityIdentifier = { type: 'regional_guidance', id };
@@ -93,14 +93,6 @@ export const regionsReducers = {
}
rg.fill = fill;
},
- rgImageCacheChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO }>) => {
- const { id, imageDTO } = action.payload;
- const rg = selectRG(state, id);
- if (!rg) {
- return;
- }
- rg.imageCache = imageDTO.image_name;
- },
rgAutoNegativeChanged: (state, action: PayloadAction<{ id: string; autoNegative: ParameterAutoNegative }>) => {
const { id, autoNegative } = action.payload;
const rg = selectRG(state, id);
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts b/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts
index 4a3fd7812d..b52aca28ae 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts
@@ -5,7 +5,7 @@ import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2) => {
return (
canvasV2.regions.entities.length +
- canvasV2.controlAdapters.entities.length +
+ // canvasV2.controlAdapters.entities.length +
canvasV2.ipAdapters.entities.length +
canvasV2.layers.entities.length
);
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
index 1705d5a3c6..a19b694670 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
@@ -639,6 +639,12 @@ const zMaskObject = z
})
.pipe(z.discriminatedUnion('type', [zCanvasBrushLineState, zCanvasEraserLineState, zCanvasRectState]));
+const zImageCache = z.object({
+ imageName: z.string(),
+ rect: zRect,
+});
+export type ImageCache = z.infer;
+
export const zCanvasRegionalGuidanceState = z.object({
id: zId,
type: z.literal('regional_guidance'),
@@ -650,7 +656,7 @@ export const zCanvasRegionalGuidanceState = z.object({
negativePrompt: zParameterNegativePrompt.nullable(),
ipAdapters: z.array(zCanvasIPAdapterState),
autoNegative: zAutoNegative,
- imageCache: z.string().min(1).nullable(),
+ rasterizationCache: z.array(zImageCache),
});
export type CanvasRegionalGuidanceState = z.infer;
@@ -670,7 +676,7 @@ const zCanvasInpaintMaskState = z.object({
position: zCoordinate,
fill: zRgbColor,
objects: z.array(zCanvasObjectState),
- imageCache: z.string().min(1).nullable(),
+ rasterizationCache: z.array(zImageCache),
});
export type CanvasInpaintMaskState = z.infer;
@@ -729,7 +735,7 @@ export const zCanvasLayerState = z.object({
position: zCoordinate,
opacity: zOpacity,
objects: z.array(zCanvasObjectState),
- imageCache: z.string().min(1).nullable(),
+ rasterizationCache: z.array(zImageCache),
controlAdapter: z.discriminatedUnion('type', [zControlNetConfig, zT2IAdapterConfig]).nullable(),
});
export type CanvasLayerState = z.infer;
@@ -826,11 +832,7 @@ export type CanvasV2State = {
_version: 3;
selectedEntityIdentifier: CanvasEntityIdentifier | null;
inpaintMask: CanvasInpaintMaskState;
- layers: {
- imageCache: ImageWithDims | null;
- entities: CanvasLayerState[];
- };
- controlAdapters: { entities: CanvasControlAdapterState[] };
+ layers: { entities: CanvasLayerState[]; compositeRasterizationCache: ImageCache[] };
ipAdapters: { entities: CanvasIPAdapterState[] };
regions: { entities: CanvasRegionalGuidanceState[] };
loras: LoRA[];
@@ -938,7 +940,7 @@ export type EntityRectAddedPayload = { entityIdentifier: CanvasEntityIdentifier;
export type EntityRasterizedPayload = {
entityIdentifier: CanvasEntityIdentifier;
imageObject: CanvasImageState;
- position: Coordinate;
+ rect: Rect;
};
export type ImageObjectAddedArg = { id: string; imageDTO: ImageDTO; position?: Coordinate };
diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts
index 5ea7fb0ad0..54196820d4 100644
--- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts
@@ -13,7 +13,7 @@ import {
import {
bboxHeightChanged,
bboxWidthChanged,
- caRecalled,
+ // caRecalled,
ipaRecalled,
layerAllDeleted,
layerRecalled,
@@ -43,8 +43,8 @@ import type {
CanvasControlAdapterState,
CanvasIPAdapterState,
CanvasLayerState,
- LoRA,
CanvasRegionalGuidanceState,
+ LoRA,
} from 'features/controlLayers/store/types';
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
import type {
@@ -271,7 +271,7 @@ const recallCA: MetadataRecallFunc = async (ca) => {
}
// No clobber
clone.id = getCAId(uuidv4());
- dispatch(caRecalled({ data: clone }));
+ // dispatch(caRecalled({ data: clone }));
return;
};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts
index b4a8b0b8d0..8b8023cca3 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts
@@ -26,10 +26,11 @@ export const addControlAdapters = async (
const layersWithValidControlAdapters = layers
.filter((layer) => layer.isEnabled)
.filter((layer) => doesLayerHaveValidControlAdapter(layer, base));
+
for (const layer of layersWithValidControlAdapters) {
const adapter = manager.layers.get(layer.id);
assert(adapter, 'Adapter not found');
- const imageDTO = await adapter.renderer.getImageDTO({ rect: bbox, is_intermediate: true, category: 'control' });
+ const imageDTO = await adapter.renderer.rasterize(bbox);
if (layer.controlAdapter.type === 'controlnet') {
await addControlNetToGraph(g, layer, imageDTO, denoise);
} else {
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
index 75cf71dcdf..5f85274ac5 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
@@ -22,7 +22,7 @@ export const addInpaint = async (
denoise.denoising_start = denoising_start;
const initialImage = await manager.getCompositeLayerImageDTO(bbox.rect);
- const maskImage = await manager.getInpaintMaskImageDTO(bbox.rect);
+ const maskImage = await manager.inpaintMask.renderer.rasterize(bbox.rect);
if (!isEqual(scaledSize, originalSize)) {
// Scale before processing requires some resizing
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
index fcf5b77393..7e4296cb77 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
@@ -23,7 +23,7 @@ export const addOutpaint = async (
denoise.denoising_start = denoising_start;
const initialImage = await manager.getCompositeLayerImageDTO(bbox.rect);
- const maskImage = await manager.getInpaintMaskImageDTO(bbox.rect);
+ const maskImage = await manager.inpaintMask.renderer.rasterize(bbox.rect);
const infill = getInfill(g, compositing);
if (!isEqual(scaledSize, originalSize)) {
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts
index 5e65a3a669..847348cfb9 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts
@@ -43,15 +43,16 @@ export const addRegions = async (
const validRegions = regions.filter((rg) => isValidRegion(rg, base));
for (const region of validRegions) {
- // Upload the mask image, or get the cached image if it exists
- const { image_name } = await manager.getRegionMaskImageDTO(region.id, bbox);
+ const adapter = manager.regions.get(region.id);
+ assert(adapter, 'Adapter not found');
+ const imageDTO = await adapter.renderer.rasterize(bbox);
// The main mask-to-tensor node
const maskToTensor = g.addNode({
id: `${PROMPT_REGION_MASK_TO_TENSOR_PREFIX}_${region.id}`,
type: 'alpha_mask_to_tensor',
image: {
- image_name,
+ image_name: imageDTO.image_name,
},
});