mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add all save as options to filter
This commit is contained in:
committed by
Kent Keirsey
parent
8e7cabdc04
commit
008be9b846
@@ -1,4 +1,14 @@
|
||||
import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Flex,
|
||||
Heading,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Spacer,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
|
||||
@@ -15,7 +25,7 @@ import { IMAGE_FILTERS } from 'features/controlLayers/store/filters';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowsCounterClockwiseBold, PiCheckBold, PiPlayFill, PiXBold } from 'react-icons/pi';
|
||||
import { PiArrowsCounterClockwiseBold, PiFloppyDiskBold, PiPlayFill, PiXBold } from 'react-icons/pi';
|
||||
|
||||
const FilterContent = memo(
|
||||
({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => {
|
||||
@@ -46,6 +56,22 @@ const FilterContent = memo(
|
||||
return IMAGE_FILTERS[config.type].validateConfig?.(config as never) ?? true;
|
||||
}, [config]);
|
||||
|
||||
const saveAsInpaintMask = useCallback(() => {
|
||||
adapter.filterer.saveAs('inpaint_mask');
|
||||
}, [adapter.filterer]);
|
||||
|
||||
const saveAsRegionalGuidance = useCallback(() => {
|
||||
adapter.filterer.saveAs('regional_guidance');
|
||||
}, [adapter.filterer]);
|
||||
|
||||
const saveAsRasterLayer = useCallback(() => {
|
||||
adapter.filterer.saveAs('raster_layer');
|
||||
}, [adapter.filterer]);
|
||||
|
||||
const saveAsControlLayer = useCallback(() => {
|
||||
adapter.filterer.saveAs('control_layer');
|
||||
}, [adapter.filterer]);
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'applyFilter',
|
||||
category: 'canvas',
|
||||
@@ -107,16 +133,35 @@ const FilterContent = memo(
|
||||
>
|
||||
{t('controlLayers.filter.reset')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
leftIcon={<PiCheckBold />}
|
||||
onClick={adapter.filterer.apply}
|
||||
isLoading={isProcessing}
|
||||
loadingText={t('controlLayers.filter.apply')}
|
||||
isDisabled={!isValid || !hasProcessed}
|
||||
>
|
||||
{t('controlLayers.filter.apply')}
|
||||
</Button>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
leftIcon={<PiFloppyDiskBold />}
|
||||
isLoading={isProcessing}
|
||||
loadingText={t('controlLayers.selectObject.saveAs')}
|
||||
variant="ghost"
|
||||
isDisabled={!isValid || !hasProcessed}
|
||||
>
|
||||
{t('controlLayers.selectObject.saveAs')}
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={adapter.filterer.apply}>
|
||||
{t('controlLayers.replaceCurrent')}
|
||||
</MenuItem>
|
||||
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={saveAsInpaintMask}>
|
||||
{t('controlLayers.newInpaintMask')}
|
||||
</MenuItem>
|
||||
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={saveAsRegionalGuidance}>
|
||||
{t('controlLayers.newRegionalGuidance')}
|
||||
</MenuItem>
|
||||
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={saveAsControlLayer}>
|
||||
{t('controlLayers.newControlLayer')}
|
||||
</MenuItem>
|
||||
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={saveAsRasterLayer}>
|
||||
{t('controlLayers.newRasterLayer')}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
<Button
|
||||
variant="ghost"
|
||||
leftIcon={<PiXBold />}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectAutoProcess } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import type { FilterConfig } from 'features/controlLayers/store/filters';
|
||||
import { getFilterForModel, IMAGE_FILTERS } from 'features/controlLayers/store/filters';
|
||||
import type { CanvasImageState } from 'features/controlLayers/store/types';
|
||||
import type { CanvasEntityType, CanvasImageState } from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/util';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { atom } from 'nanostores';
|
||||
@@ -15,6 +15,8 @@ import type { Logger } from 'roarr';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import { buildSelectModelConfig } from 'services/api/hooks/modelsByType';
|
||||
import { isControlNetOrT2IAdapterModelConfig } from 'services/api/types';
|
||||
import type { Equals } from 'tsafe';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
type CanvasEntityFiltererConfig = {
|
||||
processDebounceMs: number;
|
||||
@@ -220,6 +222,50 @@ export class CanvasEntityFilterer extends CanvasModuleBase {
|
||||
this.manager.stateApi.$filteringAdapter.set(null);
|
||||
};
|
||||
|
||||
saveAs = (type: Exclude<CanvasEntityType, 'reference_image'>) => {
|
||||
const imageState = this.imageState;
|
||||
if (!imageState) {
|
||||
this.log.warn('No image state to apply filter to');
|
||||
return;
|
||||
}
|
||||
this.log.trace('Applying filter');
|
||||
this.parent.bufferRenderer.commitBuffer();
|
||||
const rect = this.parent.transformer.getRelativeRect();
|
||||
const arg = {
|
||||
overrides: {
|
||||
objects: [imageState],
|
||||
position: {
|
||||
x: Math.round(rect.x),
|
||||
y: Math.round(rect.y),
|
||||
},
|
||||
},
|
||||
isSelected: true,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'raster_layer':
|
||||
this.manager.stateApi.addRasterLayer(arg);
|
||||
break;
|
||||
case 'control_layer':
|
||||
this.manager.stateApi.addControlLayer(arg);
|
||||
break;
|
||||
case 'inpaint_mask':
|
||||
this.manager.stateApi.addInpaintMask(arg);
|
||||
break;
|
||||
case 'regional_guidance':
|
||||
this.manager.stateApi.addRegionalGuidance(arg);
|
||||
break;
|
||||
default:
|
||||
assert<Equals<typeof type, never>>(false);
|
||||
}
|
||||
|
||||
this.imageState = null;
|
||||
this.unsubscribe();
|
||||
this.$isFiltering.set(false);
|
||||
this.$hasProcessed.set(false);
|
||||
this.manager.stateApi.$filteringAdapter.set(null);
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
this.log.trace('Resetting filter');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user