mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): make control image opacity filter toggleable
This commit is contained in:
parent
1212698059
commit
8a791d4f16
@ -1541,6 +1541,7 @@
|
||||
"globalControlAdapter": "Global $t(controlnet.controlAdapter_one)",
|
||||
"globalControlAdapterLayer": "Global $t(controlnet.controlAdapter_one) $t(unifiedCanvas.layer)",
|
||||
"globalIPAdapter": "Global $t(common.ipAdapter)",
|
||||
"globalIPAdapterLayer": "Global $t(common.ipAdapter) $t(unifiedCanvas.layer)"
|
||||
"globalIPAdapterLayer": "Global $t(common.ipAdapter) $t(unifiedCanvas.layer)",
|
||||
"opacityFilter": "Opacity Filter"
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,12 @@ import {
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Switch,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useLayerOpacity } from 'features/controlLayers/hooks/layerStateHooks';
|
||||
import { layerOpacityChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { isFilterEnabledChanged, layerOpacityChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDropHalfFill } from 'react-icons/pi';
|
||||
@ -28,13 +30,19 @@ const formatPct = (v: number | string) => `${v} %`;
|
||||
const CALayerOpacity = ({ layerId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const opacity = useLayerOpacity(layerId);
|
||||
const onChange = useCallback(
|
||||
const { opacity, isFilterEnabled } = useLayerOpacity(layerId);
|
||||
const onChangeOpacity = useCallback(
|
||||
(v: number) => {
|
||||
dispatch(layerOpacityChanged({ layerId, opacity: v / 100 }));
|
||||
},
|
||||
[dispatch, layerId]
|
||||
);
|
||||
const onChangeFilter = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(isFilterEnabledChanged({ layerId, isFilterEnabled: e.target.checked }));
|
||||
},
|
||||
[dispatch, layerId]
|
||||
);
|
||||
return (
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
@ -49,7 +57,13 @@ const CALayerOpacity = ({ layerId }: Props) => {
|
||||
<PopoverArrow />
|
||||
<PopoverBody>
|
||||
<Flex direction="column" gap={2}>
|
||||
<FormControl orientation="horizontal" minW={96}>
|
||||
<FormControl orientation="horizontal" w="full">
|
||||
<FormLabel m={0} flexGrow={1} cursor="pointer">
|
||||
{t('controlLayers.opacityFilter')}
|
||||
</FormLabel>
|
||||
<Switch isChecked={isFilterEnabled} onChange={onChangeFilter} />
|
||||
</FormControl>
|
||||
<FormControl orientation="horizontal">
|
||||
<FormLabel m={0}>{t('controlLayers.opacity')}</FormLabel>
|
||||
<CompositeSlider
|
||||
min={0}
|
||||
@ -57,8 +71,9 @@ const CALayerOpacity = ({ layerId }: Props) => {
|
||||
step={1}
|
||||
value={opacity}
|
||||
defaultValue={100}
|
||||
onChange={onChange}
|
||||
onChange={onChangeOpacity}
|
||||
marks={marks}
|
||||
w={48}
|
||||
/>
|
||||
<CompositeNumberInput
|
||||
min={0}
|
||||
@ -66,8 +81,8 @@ const CALayerOpacity = ({ layerId }: Props) => {
|
||||
step={1}
|
||||
value={opacity}
|
||||
defaultValue={100}
|
||||
onChange={onChange}
|
||||
minW={24}
|
||||
onChange={onChangeOpacity}
|
||||
w={24}
|
||||
format={formatPct}
|
||||
/>
|
||||
</FormControl>
|
||||
|
@ -72,7 +72,7 @@ export const useLayerOpacity = (layerId: string) => {
|
||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
||||
const layer = controlLayers.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
|
||||
assert(layer, `Layer ${layerId} not found`);
|
||||
return Math.round(layer.opacity * 100);
|
||||
return { opacity: Math.round(layer.opacity * 100), isFilterEnabled: layer.isFilterEnabled };
|
||||
}),
|
||||
[layerId]
|
||||
);
|
||||
|
@ -141,6 +141,7 @@ export const controlLayersSlice = createSlice({
|
||||
imageName: null,
|
||||
opacity: 1,
|
||||
isSelected: true,
|
||||
isFilterEnabled: true,
|
||||
};
|
||||
state.layers.push(layer);
|
||||
state.selectedLayerId = layer.id;
|
||||
@ -243,6 +244,19 @@ export const controlLayersSlice = createSlice({
|
||||
},
|
||||
//#endregion
|
||||
|
||||
//#region CA Layers
|
||||
isFilterEnabledChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ layerId: string; isFilterEnabled: boolean }>
|
||||
) => {
|
||||
const { layerId, isFilterEnabled } = action.payload;
|
||||
const layer = state.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
|
||||
if (layer) {
|
||||
layer.isFilterEnabled = isFilterEnabled;
|
||||
}
|
||||
},
|
||||
//#endregion
|
||||
|
||||
//#region Mask Layers
|
||||
maskLayerPositivePromptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string | null }>) => {
|
||||
const { layerId, prompt } = action.payload;
|
||||
@ -522,6 +536,8 @@ export const {
|
||||
ipAdapterLayerAdded,
|
||||
controlAdapterLayerAdded,
|
||||
layerOpacityChanged,
|
||||
// CA layer actions
|
||||
isFilterEnabledChanged,
|
||||
// Mask layer actions
|
||||
maskLayerLineAdded,
|
||||
maskLayerPointsAdded,
|
||||
|
@ -50,6 +50,7 @@ export type ControlAdapterLayer = RenderableLayerBase & {
|
||||
controlNetId: string;
|
||||
imageName: string | null;
|
||||
opacity: number;
|
||||
isFilterEnabled: boolean;
|
||||
};
|
||||
|
||||
export type IPAdapterLayer = LayerBase & {
|
||||
|
@ -421,7 +421,6 @@ const createControlNetLayerImage = (konvaLayer: Konva.Layer, image: HTMLImageEle
|
||||
const konvaImage = new Konva.Image({
|
||||
name: CONTROLNET_LAYER_IMAGE_NAME,
|
||||
image,
|
||||
filters: [LightnessToAlphaFilter],
|
||||
});
|
||||
konvaLayer.add(konvaImage);
|
||||
return konvaImage;
|
||||
@ -469,10 +468,12 @@ const updateControlNetLayerImageAttrs = (
|
||||
let needsCache = false;
|
||||
const newWidth = stage.width() / stage.scaleX();
|
||||
const newHeight = stage.height() / stage.scaleY();
|
||||
const hasFilter = konvaImage.filters() !== null && konvaImage.filters().length > 0;
|
||||
if (
|
||||
konvaImage.width() !== newWidth ||
|
||||
konvaImage.height() !== newHeight ||
|
||||
konvaImage.visible() !== reduxLayer.isEnabled
|
||||
konvaImage.visible() !== reduxLayer.isEnabled ||
|
||||
hasFilter !== reduxLayer.isFilterEnabled
|
||||
) {
|
||||
konvaImage.setAttrs({
|
||||
opacity: reduxLayer.opacity,
|
||||
@ -481,6 +482,7 @@ const updateControlNetLayerImageAttrs = (
|
||||
width: stage.width() / stage.scaleX(),
|
||||
height: stage.height() / stage.scaleY(),
|
||||
visible: reduxLayer.isEnabled,
|
||||
filters: reduxLayer.isFilterEnabled ? [LightnessToAlphaFilter] : [],
|
||||
});
|
||||
needsCache = true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user