mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): mask layers choose own opacity
This commit is contained in:
parent
43b3fab6be
commit
cb293fd7ac
@ -12,7 +12,6 @@ import {
|
|||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { MaskOpacity } from 'features/controlLayers/components/MaskOpacity';
|
|
||||||
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import {
|
import {
|
||||||
clipToBboxChanged,
|
clipToBboxChanged,
|
||||||
@ -64,7 +63,6 @@ const ControlLayersSettingsPopover = () => {
|
|||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<PopoverBody>
|
<PopoverBody>
|
||||||
<Flex direction="column" gap={2}>
|
<Flex direction="column" gap={2}>
|
||||||
<MaskOpacity />
|
|
||||||
<FormControl w="full">
|
<FormControl w="full">
|
||||||
<FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
|
<FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
|
||||||
<Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} />
|
<Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} />
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||||
import { imFillColorChanged, imFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
import { imFillColorChanged, imFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { FillStyle } from 'features/controlLayers/store/types';
|
import type { FillStyle, RgbaColor } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { RgbColor } from 'react-colorful';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const InpaintMaskMaskFillColorPicker = memo(() => {
|
export const InpaintMaskMaskFillColorPicker = memo(() => {
|
||||||
@ -15,7 +14,7 @@ export const InpaintMaskMaskFillColorPicker = memo(() => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const fill = useAppSelector((s) => s.canvasV2.inpaintMask.fill);
|
const fill = useAppSelector((s) => s.canvasV2.inpaintMask.fill);
|
||||||
const onChangeFillColor = useCallback(
|
const onChangeFillColor = useCallback(
|
||||||
(color: RgbColor) => {
|
(color: RgbaColor) => {
|
||||||
dispatch(imFillColorChanged({ color }));
|
dispatch(imFillColorChanged({ color }));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
@ -44,7 +43,7 @@ export const InpaintMaskMaskFillColorPicker = memo(() => {
|
|||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<PopoverBody minH={64}>
|
<PopoverBody minH={64}>
|
||||||
<Flex flexDir="column" gap={4}>
|
<Flex flexDir="column" gap={4}>
|
||||||
<RgbColorPicker color={fill.color} onChange={onChangeFillColor} withNumberInput />
|
<IAIColorPicker color={fill.color} onChange={onChangeFillColor} withNumberInput />
|
||||||
<MaskFillStyle style={fill.style} onChange={onChangeFillStyle} />
|
<MaskFillStyle style={fill.style} onChange={onChangeFillStyle} />
|
||||||
</Flex>
|
</Flex>
|
||||||
</PopoverBody>
|
</PopoverBody>
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { maskOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const marks = [0, 25, 50, 75, 100];
|
|
||||||
const formatPct = (v: number | string) => `${v} %`;
|
|
||||||
|
|
||||||
export const MaskOpacity = memo(() => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const opacity = useAppSelector((s) => Math.round(s.canvasV2.settings.maskOpacity * 100));
|
|
||||||
const onChange = useCallback(
|
|
||||||
(v: number) => {
|
|
||||||
dispatch(maskOpacityChanged(Math.max(v / 100, 0.25)));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<FormControl orientation="vertical">
|
|
||||||
<FormLabel m={0}>{t('controlLayers.globalMaskOpacity')}</FormLabel>
|
|
||||||
<Flex gap={4}>
|
|
||||||
<CompositeSlider
|
|
||||||
min={25}
|
|
||||||
max={100}
|
|
||||||
step={1}
|
|
||||||
value={opacity}
|
|
||||||
defaultValue={0.3}
|
|
||||||
onChange={onChange}
|
|
||||||
marks={marks}
|
|
||||||
minW={48}
|
|
||||||
/>
|
|
||||||
<CompositeNumberInput
|
|
||||||
min={25}
|
|
||||||
max={100}
|
|
||||||
step={1}
|
|
||||||
value={opacity}
|
|
||||||
defaultValue={0.3}
|
|
||||||
onChange={onChange}
|
|
||||||
w={28}
|
|
||||||
format={formatPct}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
MaskOpacity.displayName = 'MaskOpacity';
|
|
@ -1,15 +1,14 @@
|
|||||||
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import type { FillStyle } from 'features/controlLayers/store/types';
|
import type { FillStyle, RgbaColor } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { RgbColor } from 'react-colorful';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
||||||
@ -18,7 +17,7 @@ export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const fill = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
const fill = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
||||||
const onChangeFillColor = useCallback(
|
const onChangeFillColor = useCallback(
|
||||||
(color: RgbColor) => {
|
(color: RgbaColor) => {
|
||||||
dispatch(rgFillColorChanged({ id: entityIdentifier.id, color }));
|
dispatch(rgFillColorChanged({ id: entityIdentifier.id, color }));
|
||||||
},
|
},
|
||||||
[dispatch, entityIdentifier.id]
|
[dispatch, entityIdentifier.id]
|
||||||
@ -47,7 +46,7 @@ export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
|||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<PopoverBody minH={64}>
|
<PopoverBody minH={64}>
|
||||||
<Flex flexDir="column" gap={4}>
|
<Flex flexDir="column" gap={4}>
|
||||||
<RgbColorPicker color={fill.color} onChange={onChangeFillColor} withNumberInput />
|
<IAIColorPicker color={fill.color} onChange={onChangeFillColor} withNumberInput />
|
||||||
<MaskFillStyle style={fill.style} onChange={onChangeFillStyle} />
|
<MaskFillStyle style={fill.style} onChange={onChangeFillStyle} />
|
||||||
</Flex>
|
</Flex>
|
||||||
</PopoverBody>
|
</PopoverBody>
|
||||||
|
@ -323,7 +323,6 @@ export class CanvasManager {
|
|||||||
if (
|
if (
|
||||||
this._isFirstRender ||
|
this._isFirstRender ||
|
||||||
state.regions.entities !== this._prevState.regions.entities ||
|
state.regions.entities !== this._prevState.regions.entities ||
|
||||||
state.settings.maskOpacity !== this._prevState.settings.maskOpacity ||
|
|
||||||
state.tool.selected !== this._prevState.tool.selected ||
|
state.tool.selected !== this._prevState.tool.selected ||
|
||||||
state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
|
state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
|
||||||
) {
|
) {
|
||||||
@ -355,7 +354,6 @@ export class CanvasManager {
|
|||||||
if (
|
if (
|
||||||
this._isFirstRender ||
|
this._isFirstRender ||
|
||||||
state.inpaintMask !== this._prevState.inpaintMask ||
|
state.inpaintMask !== this._prevState.inpaintMask ||
|
||||||
state.settings.maskOpacity !== this._prevState.settings.maskOpacity ||
|
|
||||||
state.tool.selected !== this._prevState.tool.selected ||
|
state.tool.selected !== this._prevState.tool.selected ||
|
||||||
state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
|
state.selectedEntityIdentifier?.id !== this._prevState.selectedEntityIdentifier?.id
|
||||||
) {
|
) {
|
||||||
|
@ -22,7 +22,6 @@ export class CanvasMaskAdapter {
|
|||||||
log: Logger;
|
log: Logger;
|
||||||
|
|
||||||
state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
|
state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
|
||||||
maskOpacity: number;
|
|
||||||
|
|
||||||
transformer: CanvasTransformer;
|
transformer: CanvasTransformer;
|
||||||
renderer: CanvasObjectRenderer;
|
renderer: CanvasObjectRenderer;
|
||||||
@ -54,8 +53,6 @@ export class CanvasMaskAdapter {
|
|||||||
|
|
||||||
this.renderer = new CanvasObjectRenderer(this);
|
this.renderer = new CanvasObjectRenderer(this);
|
||||||
this.transformer = new CanvasTransformer(this);
|
this.transformer = new CanvasTransformer(this);
|
||||||
|
|
||||||
this.maskOpacity = this.manager.stateApi.getMaskOpacity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,14 +76,8 @@ export class CanvasMaskAdapter {
|
|||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const state = get(arg, 'state', this.state);
|
const state = get(arg, 'state', this.state);
|
||||||
const maskOpacity = this.manager.stateApi.getMaskOpacity();
|
|
||||||
|
|
||||||
if (
|
if (!this.isFirstRender && state === this.state && state.fill === this.state.fill) {
|
||||||
!this.isFirstRender &&
|
|
||||||
state === this.state &&
|
|
||||||
state.fill === this.state.fill &&
|
|
||||||
maskOpacity === this.maskOpacity
|
|
||||||
) {
|
|
||||||
this.log.trace('State unchanged, skipping update');
|
this.log.trace('State unchanged, skipping update');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -107,10 +98,6 @@ export class CanvasMaskAdapter {
|
|||||||
this.updateVisibility({ isEnabled });
|
this.updateVisibility({ isEnabled });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isFirstRender || state.fill !== this.state.fill || maskOpacity !== this.maskOpacity) {
|
|
||||||
this.renderer.updateCompositingRect(state.fill, maskOpacity);
|
|
||||||
this.maskOpacity = maskOpacity;
|
|
||||||
}
|
|
||||||
// this.transformer.syncInteractionState();
|
// this.transformer.syncInteractionState();
|
||||||
|
|
||||||
if (this.isFirstRender) {
|
if (this.isFirstRender) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { JSONObject } from 'common/types';
|
import type { JSONObject } from 'common/types';
|
||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
||||||
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||||
@ -149,7 +149,7 @@ export class CanvasObjectRenderer {
|
|||||||
this.subscriptions.add(
|
this.subscriptions.add(
|
||||||
this.manager.stateApi.$stageAttrs.listen(() => {
|
this.manager.stateApi.$stageAttrs.listen(() => {
|
||||||
if (this.konva.compositing && this.parent.type === 'mask_adapter') {
|
if (this.konva.compositing && this.parent.type === 'mask_adapter') {
|
||||||
this.updateCompositingRect(this.parent.state.fill, this.manager.stateApi.getMaskOpacity());
|
this.updateCompositingRect(this.parent.state.fill);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -183,14 +183,13 @@ export class CanvasObjectRenderer {
|
|||||||
return didRender;
|
return didRender;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateCompositingRect = (fill: Fill, opacity: number) => {
|
updateCompositingRect = (fill: Fill) => {
|
||||||
this.log.trace('Updating compositing rect');
|
this.log.trace('Updating compositing rect');
|
||||||
assert(this.konva.compositing, 'Missing compositing rect');
|
assert(this.konva.compositing, 'Missing compositing rect');
|
||||||
|
|
||||||
const { x, y, width, height, scale } = this.manager.stateApi.$stageAttrs.get();
|
const { x, y, width, height, scale } = this.manager.stateApi.$stageAttrs.get();
|
||||||
console.log('stageAttrs', this.manager.stateApi.$stageAttrs.get());
|
|
||||||
const attrs: RectConfig = {
|
const attrs: RectConfig = {
|
||||||
opacity,
|
|
||||||
x: -x / scale,
|
x: -x / scale,
|
||||||
y: -y / scale,
|
y: -y / scale,
|
||||||
width: width / scale,
|
width: width / scale,
|
||||||
@ -198,7 +197,7 @@ export class CanvasObjectRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (fill.style === 'solid') {
|
if (fill.style === 'solid') {
|
||||||
attrs.fill = rgbColorToString(fill.color);
|
attrs.fill = rgbaColorToString(fill.color);
|
||||||
attrs.fillPriority = 'color';
|
attrs.fillPriority = 'color';
|
||||||
this.konva.compositing.rect.setAttrs(attrs);
|
this.konva.compositing.rect.setAttrs(attrs);
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,9 +171,6 @@ export class CanvasStateApi {
|
|||||||
getInpaintMaskState = () => {
|
getInpaintMaskState = () => {
|
||||||
return this.getState().inpaintMask;
|
return this.getState().inpaintMask;
|
||||||
};
|
};
|
||||||
getMaskOpacity = () => {
|
|
||||||
return this.getState().settings.maskOpacity;
|
|
||||||
};
|
|
||||||
getSession = () => {
|
getSession = () => {
|
||||||
return this.getState().session;
|
return this.getState().session;
|
||||||
};
|
};
|
||||||
@ -232,26 +229,23 @@ export class CanvasStateApi {
|
|||||||
let currentFill: RgbaColor = state.tool.fill;
|
let currentFill: RgbaColor = state.tool.fill;
|
||||||
const selectedEntity = this.getSelectedEntity();
|
const selectedEntity = this.getSelectedEntity();
|
||||||
if (selectedEntity) {
|
if (selectedEntity) {
|
||||||
// These two entity types use a compositing rect for opacity. Their fill is always white.
|
// These two entity types use a compositing rect for opacity. Their fill is always a solid color.
|
||||||
if (selectedEntity.state.type === 'regional_guidance' || selectedEntity.state.type === 'inpaint_mask') {
|
if (selectedEntity.state.type === 'regional_guidance' || selectedEntity.state.type === 'inpaint_mask') {
|
||||||
currentFill = RGBA_RED;
|
currentFill = RGBA_RED;
|
||||||
// currentFill = RGBA_WHITE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return currentFill;
|
return currentFill;
|
||||||
};
|
};
|
||||||
|
|
||||||
getBrushPreviewFill = () => {
|
getBrushPreviewFill = (): RgbaColor => {
|
||||||
const state = this.getState();
|
|
||||||
let currentFill: RgbaColor = state.tool.fill;
|
|
||||||
const selectedEntity = this.getSelectedEntity();
|
const selectedEntity = this.getSelectedEntity();
|
||||||
if (selectedEntity) {
|
if (selectedEntity?.state.type === 'regional_guidance' || selectedEntity?.state.type === 'inpaint_mask') {
|
||||||
// The brush should use the mask opacity for these entity types
|
// The brush should use the mask opacity for these entity types
|
||||||
if (selectedEntity.state.type === 'regional_guidance' || selectedEntity.state.type === 'inpaint_mask') {
|
return selectedEntity.state.fill.color;
|
||||||
currentFill = { ...selectedEntity.state.fill.color, a: this.getSettings().maskOpacity };
|
} else {
|
||||||
}
|
const state = this.getState();
|
||||||
|
return state.tool.fill;
|
||||||
}
|
}
|
||||||
return currentFill;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$transformingEntity = $transformingEntity;
|
$transformingEntity = $transformingEntity;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
import type { FillStyle, RgbaColor } from 'features/controlLayers/store/types';
|
||||||
|
|
||||||
import crosshatch from './pattern-crosshatch.svg?raw';
|
import crosshatch from './pattern-crosshatch.svg?raw';
|
||||||
import diagonal from './pattern-diagonal.svg?raw';
|
import diagonal from './pattern-diagonal.svg?raw';
|
||||||
@ -7,7 +7,7 @@ import grid from './pattern-grid.svg?raw';
|
|||||||
import horizontal from './pattern-horizontal.svg?raw';
|
import horizontal from './pattern-horizontal.svg?raw';
|
||||||
import vertical from './pattern-vertical.svg?raw';
|
import vertical from './pattern-vertical.svg?raw';
|
||||||
|
|
||||||
export function getPatternSVG(pattern: Exclude<FillStyle, 'solid'>, color: RgbColor) {
|
export function getPatternSVG(pattern: Exclude<FillStyle, 'solid'>, color: RgbaColor) {
|
||||||
let content: string = 'data:image/svg+xml;utf8,';
|
let content: string = 'data:image/svg+xml;utf8,';
|
||||||
if (pattern === 'crosshatch') {
|
if (pattern === 'crosshatch') {
|
||||||
content += crosshatch;
|
content += crosshatch;
|
||||||
@ -21,7 +21,7 @@ export function getPatternSVG(pattern: Exclude<FillStyle, 'solid'>, color: RgbCo
|
|||||||
content += grid;
|
content += grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = content.replaceAll('stroke:black', `stroke:${rgbColorToString(color)}`);
|
content = content.replaceAll('stroke:black', `stroke:${rgbaColorToString(color)}`);
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ import type {
|
|||||||
FilterConfig,
|
FilterConfig,
|
||||||
StageAttrs,
|
StageAttrs,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { IMAGE_FILTERS, isDrawableEntity, RGBA_RED } from './types';
|
import { IMAGE_FILTERS, isDrawableEntity } from './types';
|
||||||
|
|
||||||
const initialState: CanvasV2State = {
|
const initialState: CanvasV2State = {
|
||||||
_version: 3,
|
_version: 3,
|
||||||
@ -55,7 +55,7 @@ const initialState: CanvasV2State = {
|
|||||||
type: 'inpaint_mask',
|
type: 'inpaint_mask',
|
||||||
fill: {
|
fill: {
|
||||||
style: 'diagonal',
|
style: 'diagonal',
|
||||||
color: RGBA_RED,
|
color: { r: 255, g: 122, b: 0, a: 1 }, // some orange color
|
||||||
},
|
},
|
||||||
rasterizationCache: [],
|
rasterizationCache: [],
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
@ -69,7 +69,7 @@ const initialState: CanvasV2State = {
|
|||||||
selected: 'view',
|
selected: 'view',
|
||||||
selectedBuffer: null,
|
selectedBuffer: null,
|
||||||
invertScroll: false,
|
invertScroll: false,
|
||||||
fill: RGBA_RED,
|
fill: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500
|
||||||
brush: {
|
brush: {
|
||||||
width: 50,
|
width: 50,
|
||||||
},
|
},
|
||||||
@ -87,7 +87,6 @@ const initialState: CanvasV2State = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
maskOpacity: 0.3,
|
|
||||||
// TODO(psyche): These are copied from old canvas state, need to be implemented
|
// TODO(psyche): These are copied from old canvas state, need to be implemented
|
||||||
autoSave: false,
|
autoSave: false,
|
||||||
imageSmoothing: true,
|
imageSmoothing: true,
|
||||||
@ -471,7 +470,6 @@ export const {
|
|||||||
invertScrollChanged,
|
invertScrollChanged,
|
||||||
toolChanged,
|
toolChanged,
|
||||||
toolBufferChanged,
|
toolBufferChanged,
|
||||||
maskOpacityChanged,
|
|
||||||
allEntitiesDeleted,
|
allEntitiesDeleted,
|
||||||
clipToBboxChanged,
|
clipToBboxChanged,
|
||||||
canvasReset,
|
canvasReset,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
import type { CanvasInpaintMaskState, CanvasV2State, FillStyle } from 'features/controlLayers/store/types';
|
import type { CanvasInpaintMaskState, CanvasV2State, FillStyle, RgbaColor } from 'features/controlLayers/store/types';
|
||||||
import type { RgbColor } from 'react-colorful';
|
|
||||||
|
|
||||||
export const inpaintMaskReducers = {
|
export const inpaintMaskReducers = {
|
||||||
imRecalled: (state, action: PayloadAction<{ data: CanvasInpaintMaskState }>) => {
|
imRecalled: (state, action: PayloadAction<{ data: CanvasInpaintMaskState }>) => {
|
||||||
@ -8,7 +7,7 @@ export const inpaintMaskReducers = {
|
|||||||
state.inpaintMask = data;
|
state.inpaintMask = data;
|
||||||
state.selectedEntityIdentifier = { type: 'inpaint_mask', id: data.id };
|
state.selectedEntityIdentifier = { type: 'inpaint_mask', id: data.id };
|
||||||
},
|
},
|
||||||
imFillColorChanged: (state, action: PayloadAction<{ color: RgbColor }>) => {
|
imFillColorChanged: (state, action: PayloadAction<{ color: RgbaColor }>) => {
|
||||||
const { color } = action.payload;
|
const { color } = action.payload;
|
||||||
state.inpaintMask.fill.color = color;
|
state.inpaintMask.fill.color = color;
|
||||||
},
|
},
|
||||||
|
@ -6,6 +6,7 @@ import type {
|
|||||||
FillStyle,
|
FillStyle,
|
||||||
IPMethodV2,
|
IPMethodV2,
|
||||||
RegionalGuidanceIPAdapterConfig,
|
RegionalGuidanceIPAdapterConfig,
|
||||||
|
RgbaColor,
|
||||||
} from 'features/controlLayers/store/types';
|
} from 'features/controlLayers/store/types';
|
||||||
import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
|
import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
@ -14,7 +15,7 @@ import { isEqual } from 'lodash-es';
|
|||||||
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
import type { CanvasRegionalGuidanceState, RgbColor } from './types';
|
import type { CanvasRegionalGuidanceState } from './types';
|
||||||
|
|
||||||
export const selectRegionalGuidanceEntity = (state: CanvasV2State, id: string) => {
|
export const selectRegionalGuidanceEntity = (state: CanvasV2State, id: string) => {
|
||||||
return state.regions.entities.find((rg) => rg.id === id);
|
return state.regions.entities.find((rg) => rg.id === id);
|
||||||
@ -32,17 +33,17 @@ export const selectRegionalGuidanceEntityOrThrow = (state: CanvasV2State, id: st
|
|||||||
return rg;
|
return rg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_MASK_COLORS: RgbColor[] = [
|
const DEFAULT_MASK_COLORS: RgbaColor[] = [
|
||||||
{ r: 121, g: 157, b: 219 }, // rgb(121, 157, 219)
|
{ r: 121, g: 157, b: 219, a: 0.5 }, // rgb(121, 157, 219)
|
||||||
{ r: 131, g: 214, b: 131 }, // rgb(131, 214, 131)
|
{ r: 131, g: 214, b: 131, a: 0.5 }, // rgb(131, 214, 131)
|
||||||
{ r: 250, g: 225, b: 80 }, // rgb(250, 225, 80)
|
{ r: 250, g: 225, b: 80, a: 0.5 }, // rgb(250, 225, 80)
|
||||||
{ r: 220, g: 144, b: 101 }, // rgb(220, 144, 101)
|
{ r: 220, g: 144, b: 101, a: 0.5 }, // rgb(220, 144, 101)
|
||||||
{ r: 224, g: 117, b: 117 }, // rgb(224, 117, 117)
|
{ r: 224, g: 117, b: 117, a: 0.5 }, // rgb(224, 117, 117)
|
||||||
{ r: 213, g: 139, b: 202 }, // rgb(213, 139, 202)
|
{ r: 213, g: 139, b: 202, a: 0.5 }, // rgb(213, 139, 202)
|
||||||
{ r: 161, g: 120, b: 214 }, // rgb(161, 120, 214)
|
{ r: 161, g: 120, b: 214, a: 0.5 }, // rgb(161, 120, 214)
|
||||||
];
|
];
|
||||||
|
|
||||||
const getRGMaskFill = (state: CanvasV2State): RgbColor => {
|
const getRGMaskFill = (state: CanvasV2State): RgbaColor => {
|
||||||
const lastFill = state.regions.entities.slice(-1)[0]?.fill.color;
|
const lastFill = state.regions.entities.slice(-1)[0]?.fill.color;
|
||||||
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
||||||
if (i === -1) {
|
if (i === -1) {
|
||||||
@ -104,7 +105,7 @@ export const regionsReducers = {
|
|||||||
}
|
}
|
||||||
entity.negativePrompt = prompt;
|
entity.negativePrompt = prompt;
|
||||||
},
|
},
|
||||||
rgFillColorChanged: (state, action: PayloadAction<{ id: string; color: RgbColor }>) => {
|
rgFillColorChanged: (state, action: PayloadAction<{ id: string; color: RgbaColor }>) => {
|
||||||
const { id, color } = action.payload;
|
const { id, color } = action.payload;
|
||||||
const entity = selectRegionalGuidanceEntity(state, id);
|
const entity = selectRegionalGuidanceEntity(state, id);
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
|
@ -2,9 +2,6 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
|||||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
|
|
||||||
export const settingsReducers = {
|
export const settingsReducers = {
|
||||||
maskOpacityChanged: (state, action: PayloadAction<number>) => {
|
|
||||||
state.settings.maskOpacity = action.payload;
|
|
||||||
},
|
|
||||||
clipToBboxChanged: (state, action: PayloadAction<boolean>) => {
|
clipToBboxChanged: (state, action: PayloadAction<boolean>) => {
|
||||||
state.settings.clipToBbox = action.payload;
|
state.settings.clipToBbox = action.payload;
|
||||||
},
|
},
|
||||||
|
@ -644,7 +644,7 @@ const zMaskObject = z
|
|||||||
const zFillStyle = z.enum(['solid', 'grid', 'crosshatch', 'diagonal', 'horizontal', 'vertical']);
|
const zFillStyle = z.enum(['solid', 'grid', 'crosshatch', 'diagonal', 'horizontal', 'vertical']);
|
||||||
export type FillStyle = z.infer<typeof zFillStyle>;
|
export type FillStyle = z.infer<typeof zFillStyle>;
|
||||||
export const isFillStyle = (v: unknown): v is FillStyle => zFillStyle.safeParse(v).success;
|
export const isFillStyle = (v: unknown): v is FillStyle => zFillStyle.safeParse(v).success;
|
||||||
const zFill = z.object({ style: zFillStyle, color: zRgbColor });
|
const zFill = z.object({ style: zFillStyle, color: zRgbaColor });
|
||||||
export type Fill = z.infer<typeof zFill>;
|
export type Fill = z.infer<typeof zFill>;
|
||||||
|
|
||||||
const zImageCache = z.object({
|
const zImageCache = z.object({
|
||||||
@ -858,7 +858,6 @@ export type CanvasV2State = {
|
|||||||
};
|
};
|
||||||
settings: {
|
settings: {
|
||||||
imageSmoothing: boolean;
|
imageSmoothing: boolean;
|
||||||
maskOpacity: number;
|
|
||||||
showHUD: boolean;
|
showHUD: boolean;
|
||||||
autoSave: boolean;
|
autoSave: boolean;
|
||||||
preserveMaskedArea: boolean;
|
preserveMaskedArea: boolean;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user