diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAOpacityAndFilter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAOpacityAndFilter.tsx index f6bfbdf6f8..25117459f6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAOpacityAndFilter.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAdapter/CAOpacityAndFilter.tsx @@ -32,7 +32,9 @@ export const CAOpacityAndFilter = memo(({ id }: Props) => { 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).filter === 'LightnessToAlphaFilter'); + const isFilterEnabled = useAppSelector((s) => + selectCAOrThrow(s.canvasV2, id).filters.includes('LightnessToAlphaFilter') + ); const onChangeOpacity = useCallback( (v: number) => { dispatch(caOpacityChanged({ id, opacity: v / 100 })); @@ -41,7 +43,7 @@ export const CAOpacityAndFilter = memo(({ id }: Props) => { ); const onChangeFilter = useCallback( (e: ChangeEvent) => { - dispatch(caFilterChanged({ id, filter: e.target.checked ? 'LightnessToAlphaFilter' : 'none' })); + dispatch(caFilterChanged({ id, filters: e.target.checked ? ['LightnessToAlphaFilter'] : [] })); }, [dispatch, id] ); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasImage.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasImage.ts index 62e55f83f6..d102869c3a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasImage.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasImage.ts @@ -86,10 +86,16 @@ export class CanvasImage { image: imageEl, width, height, - filters: filters.map((f) => FILTER_MAP[f]), }); this.konvaImageGroup.add(this.konvaImage); } + if (filters.length > 0) { + this.konvaImage.cache(); + this.konvaImage.filters(filters.map((f) => FILTER_MAP[f])); + } else { + this.konvaImage.clearCache(); + this.konvaImage.filters([]); + } this.imageName = imageName; this.isLoading = false; this.isError = false; @@ -144,7 +150,14 @@ export class CanvasImage { if (this.lastImageObject.image.name !== image.name || force) { await this.updateImageSource(image.name); } - this.konvaImage?.setAttrs({ x, y, width, height, filters: filters.map((f) => FILTER_MAP[f]) }); + this.konvaImage?.setAttrs({ x, y, width, height }); + if (filters.length > 0) { + this.konvaImage?.cache(); + this.konvaImage?.filters(filters.map((f) => FILTER_MAP[f])); + } else { + this.konvaImage?.clearCache(); + this.konvaImage?.filters([]); + } this.konvaPlaceholderRect.setAttrs({ width, height }); this.konvaPlaceholderText.setAttrs({ width, height, fontSize: width / 16 }); this.lastImageObject = imageObject; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts index 28402f4861..e82b7aa2ac 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts @@ -41,7 +41,7 @@ export const controlAdaptersReducers = { bboxNeedsUpdate: false, isEnabled: true, opacity: 1, - filter: 'LightnessToAlphaFilter', + filters: ['LightnessToAlphaFilter'], processorPendingBatchId: null, ...config, }); @@ -164,7 +164,7 @@ export const controlAdaptersReducers = { ca.bboxNeedsUpdate = true; ca.isEnabled = true; if (imageDTO) { - const newImageObject = imageDTOToImageObject(id, objectId, imageDTO, { filters: ca.filter ? [ca.filter] : [] }); + const newImageObject = imageDTOToImageObject(id, objectId, imageDTO, { filters: ca.filters }); if (isEqual(newImageObject, ca.imageObject)) { return; } @@ -188,7 +188,7 @@ export const controlAdaptersReducers = { ca.bboxNeedsUpdate = true; ca.isEnabled = true; ca.processedImageObject = imageDTO - ? imageDTOToImageObject(id, objectId, imageDTO, { filters: ca.filter ? [ca.filter] : [] }) + ? imageDTOToImageObject(id, objectId, imageDTO, { filters: ca.filters }) : null; }, prepare: (payload: { id: string; imageDTO: ImageDTO | null }) => ({ payload: { ...payload, objectId: uuidv4() } }), @@ -248,13 +248,19 @@ export const controlAdaptersReducers = { ca.processedImageObject = null; } }, - caFilterChanged: (state, action: PayloadAction<{ id: string; filter: Filter }>) => { - const { id, filter } = action.payload; + caFilterChanged: (state, action: PayloadAction<{ id: string; filters: Filter[] }>) => { + const { id, filters } = action.payload; const ca = selectCA(state, id); if (!ca) { return; } - ca.filter = filter; + 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; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index af00a59b61..92697568ea 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -677,7 +677,7 @@ const zControlAdapterEntityBase = z.object({ bbox: zRect.nullable(), bboxNeedsUpdate: z.boolean(), opacity: zOpacity, - filter: zFilter, + filters: z.array(zFilter), weight: z.number().gte(-1).lte(2), imageObject: zImageObject.nullable(), processedImageObject: zImageObject.nullable(),