mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Fixes app after removing in/out-painting refs
This commit is contained in:
parent
98e3bbb3bd
commit
223e0529ba
@ -616,10 +616,7 @@ class InvokeAIWebServer:
|
||||
"""
|
||||
Prepare for generation based on generation_mode
|
||||
"""
|
||||
if generation_parameters["generation_mode"] in [
|
||||
"outpainting",
|
||||
"inpainting",
|
||||
]:
|
||||
if generation_parameters["generation_mode"] == "unifiedCanvas":
|
||||
"""
|
||||
generation_parameters["init_img"] is a base64 image
|
||||
generation_parameters["init_mask"] is a base64 image
|
||||
@ -634,35 +631,23 @@ class InvokeAIWebServer:
|
||||
|
||||
original_bounding_box = generation_parameters["bounding_box"].copy()
|
||||
|
||||
if generation_parameters["generation_mode"] == "outpainting":
|
||||
initial_image = dataURL_to_image(
|
||||
generation_parameters["init_img"]
|
||||
).convert("RGBA")
|
||||
initial_image = dataURL_to_image(
|
||||
generation_parameters["init_img"]
|
||||
).convert("RGBA")
|
||||
|
||||
"""
|
||||
The outpaint image and mask are pre-cropped by the UI, so the bounding box we pass
|
||||
to the generator should be:
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": original_bounding_box["width"],
|
||||
"height": original_bounding_box["height"]
|
||||
}
|
||||
"""
|
||||
"""
|
||||
The outpaint image and mask are pre-cropped by the UI, so the bounding box we pass
|
||||
to the generator should be:
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": original_bounding_box["width"],
|
||||
"height": original_bounding_box["height"]
|
||||
}
|
||||
"""
|
||||
|
||||
generation_parameters["bounding_box"]["x"] = 0
|
||||
generation_parameters["bounding_box"]["y"] = 0
|
||||
elif generation_parameters["generation_mode"] == "inpainting":
|
||||
init_img_path = self.get_image_path_from_url(init_img_url)
|
||||
initial_image = Image.open(init_img_path)
|
||||
|
||||
"""
|
||||
For inpainting, only the mask is pre-cropped by the UI, so we need to crop out a copy
|
||||
of the region of the image to be inpainted to match the size of the mask image.
|
||||
"""
|
||||
initial_image = copy_image_from_bounding_box(
|
||||
initial_image, **generation_parameters["bounding_box"]
|
||||
)
|
||||
generation_parameters["bounding_box"]["x"] = 0
|
||||
generation_parameters["bounding_box"]["y"] = 0
|
||||
|
||||
# Convert mask dataURL to an image and convert to greyscale
|
||||
mask_image = dataURL_to_image(
|
||||
@ -930,7 +915,7 @@ class InvokeAIWebServer:
|
||||
if "init_mask" in all_parameters:
|
||||
all_parameters["init_mask"] = "" # TODO: store the mask in metadata
|
||||
|
||||
if generation_parameters["generation_mode"] == "outpainting":
|
||||
if generation_parameters["generation_mode"] == "unifiedCanvas":
|
||||
all_parameters["bounding_box"] = original_bounding_box
|
||||
|
||||
metadata = self.parameters_to_generated_image_metadata(all_parameters)
|
||||
|
@ -50,7 +50,7 @@ const appSelector = createSelector(
|
||||
|
||||
const shouldShowGalleryButton =
|
||||
!(shouldShowGallery || (shouldHoldGalleryOpen && !shouldPinGallery)) &&
|
||||
['txt2img', 'img2img', 'inpainting', 'outpainting'].includes(
|
||||
['txt2img', 'img2img', 'unifiedCanvas'].includes(
|
||||
activeTabName
|
||||
);
|
||||
|
||||
@ -59,7 +59,7 @@ const appSelector = createSelector(
|
||||
shouldShowOptionsPanel ||
|
||||
(shouldHoldOptionsPanelOpen && !shouldPinOptionsPanel)
|
||||
) &&
|
||||
['txt2img', 'img2img', 'inpainting', 'outpainting'].includes(
|
||||
['txt2img', 'img2img', 'unifiedCanvas'].includes(
|
||||
activeTabName
|
||||
);
|
||||
|
||||
|
@ -44,11 +44,6 @@ export const readinessSelector = createSelector(
|
||||
reasonsWhyNotReady.push('No initial image selected');
|
||||
}
|
||||
|
||||
if (activeTabName === 'inpainting' && !initialCanvasImage) {
|
||||
isReady = false;
|
||||
reasonsWhyNotReady.push('No inpainting image selected');
|
||||
}
|
||||
|
||||
// TODO: job queue
|
||||
// Cannot generate if already processing an image
|
||||
if (isProcessing) {
|
||||
|
@ -54,24 +54,26 @@ const makeSocketIOEmitters = (
|
||||
systemState,
|
||||
};
|
||||
|
||||
if (generationMode === 'inpainting') {
|
||||
const initialCanvasImage = initialCanvasImageSelector(getState());
|
||||
const imageUrl = initialCanvasImage?.image.url;
|
||||
// if (generationMode === 'inpainting') {
|
||||
// const initialCanvasImage = initialCanvasImageSelector(getState());
|
||||
// const imageUrl = initialCanvasImage?.image.url;
|
||||
|
||||
if (!imageUrl) {
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: 'Inpainting image not loaded, cannot generate image.',
|
||||
level: 'error',
|
||||
})
|
||||
);
|
||||
dispatch(errorOccurred());
|
||||
return;
|
||||
}
|
||||
// if (!imageUrl) {
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: 'Inpainting image not loaded, cannot generate image.',
|
||||
// level: 'error',
|
||||
// })
|
||||
// );
|
||||
// dispatch(errorOccurred());
|
||||
// return;
|
||||
// }
|
||||
|
||||
frontendToBackendParametersConfig.imageToProcessUrl = imageUrl;
|
||||
} else if (!['txt2img', 'img2img'].includes(generationMode)) {
|
||||
// frontendToBackendParametersConfig.imageToProcessUrl = imageUrl;
|
||||
// } else
|
||||
|
||||
if (!['txt2img', 'img2img'].includes(generationMode)) {
|
||||
if (!galleryState.currentImage?.url) return;
|
||||
|
||||
frontendToBackendParametersConfig.imageToProcessUrl =
|
||||
|
@ -37,10 +37,7 @@ import {
|
||||
requestNewImages,
|
||||
requestSystemConfig,
|
||||
} from './actions';
|
||||
import {
|
||||
addImageToStagingArea,
|
||||
setImageToInpaint,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { addImageToStagingArea } from 'features/canvas/canvasSlice';
|
||||
import { tabMap } from 'features/tabs/InvokeTabs';
|
||||
|
||||
/**
|
||||
@ -120,23 +117,15 @@ const makeSocketIOListeners = (
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
['inpainting', 'outpainting'].includes(generationMode) &&
|
||||
data.boundingBox
|
||||
) {
|
||||
if (generationMode === 'unifiedCanvas' && data.boundingBox) {
|
||||
newImage.category = 'temp';
|
||||
const { boundingBox } = data;
|
||||
|
||||
if (generationMode === 'inpainting') {
|
||||
dispatch(setImageToInpaint(newImage));
|
||||
} else {
|
||||
dispatch(
|
||||
addImageToStagingArea({
|
||||
image: newImage,
|
||||
boundingBox,
|
||||
})
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
addImageToStagingArea({
|
||||
image: newImage,
|
||||
boundingBox,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldLoopback) {
|
||||
@ -146,10 +135,6 @@ const makeSocketIOListeners = (
|
||||
dispatch(setInitialImage(newImage));
|
||||
break;
|
||||
}
|
||||
case 'inpainting': {
|
||||
dispatch(setImageToInpaint(newImage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,14 +28,8 @@ import { socketioMiddleware } from './socketio/middleware';
|
||||
* The necesssary nested persistors with blacklists are configured below.
|
||||
*/
|
||||
|
||||
const genericCanvasBlacklist = ['cursorPosition'];
|
||||
|
||||
const inpaintingCanvasBlacklist = genericCanvasBlacklist.map(
|
||||
(blacklistItem) => `canvas.inpainting.${blacklistItem}`
|
||||
);
|
||||
|
||||
const outpaintingCanvasBlacklist = genericCanvasBlacklist.map(
|
||||
(blacklistItem) => `canvas.outpainting.${blacklistItem}`
|
||||
const canvasBlacklist = ['cursorPosition'].map(
|
||||
(blacklistItem) => `canvas.${blacklistItem}`
|
||||
);
|
||||
|
||||
const systemBlacklist = [
|
||||
@ -73,12 +67,7 @@ const rootPersistConfig = getPersistConfig({
|
||||
key: 'root',
|
||||
storage,
|
||||
rootReducer,
|
||||
blacklist: [
|
||||
...inpaintingCanvasBlacklist,
|
||||
...outpaintingCanvasBlacklist,
|
||||
...systemBlacklist,
|
||||
...galleryBlacklist,
|
||||
],
|
||||
blacklist: [...canvasBlacklist, ...systemBlacklist, ...galleryBlacklist],
|
||||
debounce: 300,
|
||||
});
|
||||
|
||||
|
@ -135,7 +135,7 @@ const ImageUploader = (props: ImageUploaderProps) => {
|
||||
};
|
||||
}, [dispatch, toast, activeTabName]);
|
||||
|
||||
const overlaySecondaryText = ['img2img', 'inpainting'].includes(activeTabName)
|
||||
const overlaySecondaryText = ['img2img', 'unifiedCanvas'].includes(activeTabName)
|
||||
? ` to ${tabDict[activeTabName as keyof typeof tabDict].tooltip}`
|
||||
: ``;
|
||||
|
||||
|
BIN
frontend/src/common/icons/UnifiedCanvas.afdesign
Normal file
BIN
frontend/src/common/icons/UnifiedCanvas.afdesign
Normal file
Binary file not shown.
16
frontend/src/common/icons/UnifiedCanvasIcon.tsx
Normal file
16
frontend/src/common/icons/UnifiedCanvasIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
BIN
frontend/src/common/icons/design_files/UnifiedCanvas.afdesign
Normal file
BIN
frontend/src/common/icons/design_files/UnifiedCanvas.afdesign
Normal file
Binary file not shown.
7
frontend/src/common/icons/design_files/UnifiedCanvas.svg
Normal file
7
frontend/src/common/icons/design_files/UnifiedCanvas.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.8 KiB |
@ -106,10 +106,7 @@ export const frontendToBackendParameters = (
|
||||
}
|
||||
|
||||
// inpainting exclusive parameters
|
||||
if (
|
||||
['inpainting', 'outpainting'].includes(generationMode) &&
|
||||
canvasImageLayerRef.current
|
||||
) {
|
||||
if (generationMode === 'unifiedCanvas' && canvasImageLayerRef.current) {
|
||||
const {
|
||||
layerState: { objects },
|
||||
boundingBoxCoordinates,
|
||||
@ -146,44 +143,42 @@ export const frontendToBackendParameters = (
|
||||
|
||||
generationParameters.bounding_box = boundingBox;
|
||||
|
||||
if (generationMode === 'outpainting') {
|
||||
const tempScale = canvasImageLayerRef.current.scale();
|
||||
const tempScale = canvasImageLayerRef.current.scale();
|
||||
|
||||
canvasImageLayerRef.current.scale({
|
||||
x: 1 / stageScale,
|
||||
y: 1 / stageScale,
|
||||
});
|
||||
canvasImageLayerRef.current.scale({
|
||||
x: 1 / stageScale,
|
||||
y: 1 / stageScale,
|
||||
});
|
||||
|
||||
const absPos = canvasImageLayerRef.current.getAbsolutePosition();
|
||||
const absPos = canvasImageLayerRef.current.getAbsolutePosition();
|
||||
|
||||
const imageDataURL = canvasImageLayerRef.current.toDataURL({
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
});
|
||||
const imageDataURL = canvasImageLayerRef.current.toDataURL({
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
});
|
||||
|
||||
if (enableImageDebugging) {
|
||||
openBase64ImageInTab([
|
||||
{ base64: maskDataURL, caption: 'mask sent as init_mask' },
|
||||
{ base64: imageDataURL, caption: 'image sent as init_img' },
|
||||
]);
|
||||
}
|
||||
|
||||
canvasImageLayerRef.current.scale(tempScale);
|
||||
|
||||
generationParameters.init_img = imageDataURL;
|
||||
|
||||
// TODO: The server metadata generation needs to be changed to fix this.
|
||||
generationParameters.progress_images = false;
|
||||
|
||||
generationParameters.seam_size = 96;
|
||||
generationParameters.seam_blur = 16;
|
||||
generationParameters.seam_strength = 0.7;
|
||||
generationParameters.seam_steps = 10;
|
||||
generationParameters.tile_size = 32;
|
||||
generationParameters.force_outpaint = false;
|
||||
if (enableImageDebugging) {
|
||||
openBase64ImageInTab([
|
||||
{ base64: maskDataURL, caption: 'mask sent as init_mask' },
|
||||
{ base64: imageDataURL, caption: 'image sent as init_img' },
|
||||
]);
|
||||
}
|
||||
|
||||
canvasImageLayerRef.current.scale(tempScale);
|
||||
|
||||
generationParameters.init_img = imageDataURL;
|
||||
|
||||
// TODO: The server metadata generation needs to be changed to fix this.
|
||||
generationParameters.progress_images = false;
|
||||
|
||||
generationParameters.seam_size = 96;
|
||||
generationParameters.seam_blur = 16;
|
||||
generationParameters.seam_strength = 0.7;
|
||||
generationParameters.seam_steps = 10;
|
||||
generationParameters.tile_size = 32;
|
||||
generationParameters.force_outpaint = false;
|
||||
}
|
||||
|
||||
if (shouldGenerateVariations) {
|
||||
|
@ -93,7 +93,6 @@ const selector = createSelector(
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
tool,
|
||||
isOnOutpaintingTab: activeTabName === 'outpainting',
|
||||
isStaging,
|
||||
shouldShowIntermediates,
|
||||
shouldLockToInitialImage,
|
||||
@ -122,7 +121,6 @@ const IAICanvas = () => {
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
tool,
|
||||
isOnOutpaintingTab,
|
||||
isStaging,
|
||||
shouldShowIntermediates,
|
||||
shouldLockToInitialImage,
|
||||
@ -207,11 +205,7 @@ const IAICanvas = () => {
|
||||
onDragEnd={handleDragEnd}
|
||||
onWheel={handleWheel}
|
||||
listening={(tool === 'move' || isStaging) && !isModifyingBoundingBox}
|
||||
draggable={
|
||||
(tool === 'move' || isStaging) &&
|
||||
!isModifyingBoundingBox &&
|
||||
isOnOutpaintingTab
|
||||
}
|
||||
draggable={(tool === 'move' || isStaging) && !isModifyingBoundingBox}
|
||||
>
|
||||
<Layer id={'grid'} visible={shouldShowGrid}>
|
||||
<IAICanvasGrid />
|
||||
@ -243,7 +237,7 @@ const IAICanvas = () => {
|
||||
/>
|
||||
</Layer>
|
||||
</Stage>
|
||||
{isOnOutpaintingTab && <IAICanvasStatusText />}
|
||||
<IAICanvasStatusText />
|
||||
<IAICanvasStagingAreaToolbar />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
import _ from 'lodash';
|
||||
import IAICanvasMaskColorPicker from './IAICanvasMaskControls/IAICanvasMaskColorPicker';
|
||||
|
||||
const inpaintingBrushSelector = createSelector(
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { tool, brushSize } = canvas;
|
||||
@ -38,9 +38,7 @@ const inpaintingBrushSelector = createSelector(
|
||||
|
||||
export default function IAICanvasBrushControl() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, brushSize, activeTabName } = useAppSelector(
|
||||
inpaintingBrushSelector
|
||||
);
|
||||
const { tool, brushSize, activeTabName } = useAppSelector(selector);
|
||||
|
||||
const handleSelectBrushTool = () => dispatch(setTool('brush'));
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { clearImageToInpaint } from 'features/canvas/canvasSlice';
|
||||
|
||||
export default function IAICanvasClearImageControl() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleClearImage = () => {
|
||||
dispatch(clearImageToInpaint());
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Clear Image"
|
||||
tooltip="Clear Image"
|
||||
icon={<FaTrash size={16} />}
|
||||
onClick={handleClearImage}
|
||||
/>
|
||||
);
|
||||
}
|
@ -40,7 +40,7 @@ export default function IAICanvasMaskVisibilityControl() {
|
||||
handleToggleShouldShowMask();
|
||||
},
|
||||
{
|
||||
enabled: activeTabName === 'inpainting' || activeTabName == 'outpainting',
|
||||
enabled: activeTabName === 'unifiedCanvas',
|
||||
},
|
||||
[activeTabName, isMaskEnabled]
|
||||
);
|
||||
|
@ -1,49 +0,0 @@
|
||||
// import { GroupConfig } from 'konva/lib/Group';
|
||||
// import { Group, Line } from 'react-konva';
|
||||
// import { RootState, useAppSelector } from 'app/store';
|
||||
// import { createSelector } from '@reduxjs/toolkit';
|
||||
// import { OutpaintingCanvasState } from './canvasSlice';
|
||||
|
||||
// export const canvasEraserLinesSelector = createSelector(
|
||||
// (state: RootState) => state.canvas.outpainting,
|
||||
// (outpainting: OutpaintingCanvasState) => {
|
||||
// const { eraserLines } = outpainting;
|
||||
// return {
|
||||
// eraserLines,
|
||||
// };
|
||||
// }
|
||||
// );
|
||||
|
||||
// type IAICanvasEraserLinesProps = GroupConfig;
|
||||
|
||||
// /**
|
||||
// * Draws the lines which comprise the mask.
|
||||
// *
|
||||
// * Uses globalCompositeOperation to handle the brush and eraser tools.
|
||||
// */
|
||||
// const IAICanvasEraserLines = (props: IAICanvasEraserLinesProps) => {
|
||||
// const { ...rest } = props;
|
||||
// const { eraserLines } = useAppSelector(canvasEraserLinesSelector);
|
||||
|
||||
// return (
|
||||
// <Group {...rest} globalCompositeOperation={'destination-out'}>
|
||||
// {eraserLines.map((line, i) => (
|
||||
// <Line
|
||||
// key={i}
|
||||
// points={line.points}
|
||||
// stroke={'rgb(0,0,0)'} // The lines can be any color, just need alpha > 0
|
||||
// strokeWidth={line.strokeWidth * 2}
|
||||
// tension={0}
|
||||
// lineCap="round"
|
||||
// lineJoin="round"
|
||||
// shadowForStrokeEnabled={false}
|
||||
// listening={false}
|
||||
// globalCompositeOperation={'source-over'}
|
||||
// />
|
||||
// ))}
|
||||
// </Group>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default IAICanvasEraserLines;
|
||||
export default {}
|
@ -52,9 +52,6 @@ const IAICanvasResizer = () => {
|
||||
|
||||
if (!initialCanvasImage?.image) return;
|
||||
|
||||
const { width: imageWidth, height: imageHeight } =
|
||||
initialCanvasImage.image;
|
||||
|
||||
dispatch(
|
||||
setCanvasContainerDimensions({
|
||||
width: clientWidth,
|
||||
@ -69,34 +66,6 @@ const IAICanvasResizer = () => {
|
||||
}
|
||||
|
||||
dispatch(setDoesCanvasNeedScaling(false));
|
||||
// }
|
||||
// if ((activeTabName === 'inpainting') && initialCanvasImage?.image) {
|
||||
// const { width: imageWidth, height: imageHeight } =
|
||||
// initialCanvasImage.image;
|
||||
|
||||
// const scale = Math.min(
|
||||
// 1,
|
||||
// Math.min(clientWidth / imageWidth, clientHeight / imageHeight)
|
||||
// );
|
||||
|
||||
// dispatch(setStageScale(scale));
|
||||
|
||||
// dispatch(
|
||||
// setStageDimensions({
|
||||
// width: Math.floor(imageWidth * scale),
|
||||
// height: Math.floor(imageHeight * scale),
|
||||
// })
|
||||
// );
|
||||
// dispatch(setDoesCanvasNeedScaling(false));
|
||||
// } else if (activeTabName === 'outpainting') {
|
||||
// dispatch(
|
||||
// setStageDimensions({
|
||||
// width: Math.floor(clientWidth),
|
||||
// height: Math.floor(clientHeight),
|
||||
// })
|
||||
// );
|
||||
// dispatch(setDoesCanvasNeedScaling(false));
|
||||
// }
|
||||
}, 0);
|
||||
}, [
|
||||
dispatch,
|
||||
@ -104,6 +73,7 @@ const IAICanvasResizer = () => {
|
||||
doesCanvasNeedScaling,
|
||||
activeTabName,
|
||||
isCanvasInitialized,
|
||||
shouldLockToInitialImage,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
} from 'common/util/roundDownToMultiple';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const setInitialCanvasImage = (
|
||||
export const setInitialCanvasImage_reducer = (
|
||||
state: CanvasState,
|
||||
image: InvokeAI.Image
|
||||
) => {
|
||||
|
@ -8,13 +8,11 @@ import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { RootState } from 'app/store';
|
||||
import { mergeAndUploadCanvas } from './util/mergeAndUploadCanvas';
|
||||
import { uploadImage } from 'features/gallery/util/uploadImage';
|
||||
import { setInitialCanvasImage } from './canvasReducers';
|
||||
import { setInitialCanvasImage_reducer } from './canvasReducers';
|
||||
import calculateScale from './util/calculateScale';
|
||||
import calculateCoordinates from './util/calculateCoordinates';
|
||||
import floorCoordinates from './util/floorCoordinates';
|
||||
|
||||
export type CanvasMode = 'inpainting' | 'outpainting';
|
||||
|
||||
export type CanvasLayer = 'base' | 'mask';
|
||||
|
||||
export type CanvasDrawingTool = 'brush' | 'eraser';
|
||||
@ -256,15 +254,8 @@ export const canvasSlice = createSlice({
|
||||
setCursorPosition: (state, action: PayloadAction<Vector2d | null>) => {
|
||||
state.cursorPosition = action.payload;
|
||||
},
|
||||
clearImageToInpaint: (state) => {
|
||||
// TODO
|
||||
// state.inpainting.imageToInpaint = undefined;
|
||||
},
|
||||
setImageToOutpaint: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
setInitialCanvasImage(state, action.payload);
|
||||
},
|
||||
setImageToInpaint: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
setInitialCanvasImage(state, action.payload);
|
||||
setInitialCanvasImage: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
setInitialCanvasImage_reducer(state, action.payload);
|
||||
},
|
||||
setStageDimensions: (state, action: PayloadAction<Dimensions>) => {
|
||||
state.stageDimensions = action.payload;
|
||||
@ -642,10 +633,8 @@ export const canvasSlice = createSlice({
|
||||
|
||||
if (kind !== 'init') return;
|
||||
|
||||
if (activeTabName === 'inpainting') {
|
||||
setInitialCanvasImage(state, image);
|
||||
} else if (activeTabName === 'outpainting') {
|
||||
setInitialCanvasImage(state, image);
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
setInitialCanvasImage_reducer(state, image);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -665,13 +654,11 @@ export const {
|
||||
setShouldShowBrushPreview,
|
||||
setMaskColor,
|
||||
clearMask,
|
||||
clearImageToInpaint,
|
||||
undo,
|
||||
redo,
|
||||
setCursorPosition,
|
||||
setStageDimensions,
|
||||
setImageToInpaint,
|
||||
setImageToOutpaint,
|
||||
setInitialCanvasImage,
|
||||
setBoundingBoxDimensions,
|
||||
setBoundingBoxCoordinates,
|
||||
setBoundingBoxPreviewFill,
|
||||
|
@ -13,7 +13,7 @@ import { canvasSelector } from '../canvasSlice';
|
||||
import { useRef } from 'react';
|
||||
import { stageRef } from '../IAICanvas';
|
||||
|
||||
const inpaintingCanvasHotkeysSelector = createSelector(
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const {
|
||||
@ -40,9 +40,8 @@ const inpaintingCanvasHotkeysSelector = createSelector(
|
||||
|
||||
const useInpaintingCanvasHotkeys = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { activeTabName, shouldShowBoundingBox, tool } = useAppSelector(
|
||||
inpaintingCanvasHotkeysSelector
|
||||
);
|
||||
const { activeTabName, shouldShowBoundingBox, tool } =
|
||||
useAppSelector(selector);
|
||||
|
||||
const previousToolRef = useRef<CanvasTool | null>(null);
|
||||
// Toggle lock bounding box
|
||||
|
@ -60,12 +60,7 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
||||
return useCallback(
|
||||
(e: KonvaEventObject<WheelEvent>) => {
|
||||
// stop default scrolling
|
||||
if (
|
||||
activeTabName !== 'outpainting' ||
|
||||
!stageRef.current ||
|
||||
isMoveStageKeyHeld ||
|
||||
!initialCanvasImage
|
||||
)
|
||||
if (!stageRef.current || isMoveStageKeyHeld || !initialCanvasImage)
|
||||
return;
|
||||
|
||||
e.evt.preventDefault();
|
||||
|
@ -36,9 +36,9 @@ import {
|
||||
FaTrash,
|
||||
} from 'react-icons/fa';
|
||||
import {
|
||||
setImageToInpaint,
|
||||
setDoesCanvasNeedScaling,
|
||||
setImageToOutpaint,
|
||||
setInitialCanvasImage,
|
||||
setShouldLockToInitialImage,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { GalleryState } from './gallerySlice';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
@ -320,12 +320,12 @@ const CurrentImageButtons = () => {
|
||||
if (!currentImage) return;
|
||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||
|
||||
dispatch(setImageToInpaint(currentImage));
|
||||
dispatch(setActiveTab('inpainting'));
|
||||
dispatch(setInitialCanvasImage(currentImage));
|
||||
dispatch(setShouldLockToInitialImage(true));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
|
||||
toast({
|
||||
title: 'Sent to Inpainting',
|
||||
title: 'Sent to Unified Canvas',
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
@ -336,12 +336,12 @@ const CurrentImageButtons = () => {
|
||||
if (!currentImage) return;
|
||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||
|
||||
dispatch(setImageToOutpaint(currentImage));
|
||||
dispatch(setActiveTab('outpainting'));
|
||||
dispatch(setInitialCanvasImage(currentImage));
|
||||
dispatch(setShouldLockToInitialImage(false));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
|
||||
toast({
|
||||
title: 'Sent to Inpainting',
|
||||
title: 'Sent to Unified Canvas',
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
|
@ -23,8 +23,9 @@ import {
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import * as ContextMenu from '@radix-ui/react-context-menu';
|
||||
import {
|
||||
setImageToInpaint,
|
||||
setImageToOutpaint,
|
||||
setDoesCanvasNeedScaling,
|
||||
setInitialCanvasImage,
|
||||
setShouldLockToInitialImage,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { hoverableImageSelector } from './gallerySliceSelectors';
|
||||
|
||||
@ -97,10 +98,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
const handleSendToInpainting = () => {
|
||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||
dispatch(setImageToInpaint(image));
|
||||
if (activeTabName !== 'inpainting') {
|
||||
dispatch(setActiveTab('inpainting'));
|
||||
|
||||
dispatch(setInitialCanvasImage(image));
|
||||
dispatch(setShouldLockToInitialImage(true));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
|
||||
if (activeTabName !== 'unifiedCanvas') {
|
||||
dispatch(setActiveTab('unifiedCanvas'));
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Sent to Inpainting',
|
||||
status: 'success',
|
||||
@ -111,10 +117,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
const handleSendToOutpainting = () => {
|
||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||
dispatch(setImageToOutpaint(image));
|
||||
if (activeTabName !== 'outpainting') {
|
||||
dispatch(setActiveTab('outpainting'));
|
||||
|
||||
dispatch(setInitialCanvasImage(image));
|
||||
dispatch(setShouldLockToInitialImage(true));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
|
||||
if (activeTabName !== 'unifiedCanvas') {
|
||||
dispatch(setActiveTab('unifiedCanvas'));
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Sent to Outpainting',
|
||||
status: 'success',
|
||||
|
@ -76,7 +76,7 @@ export default function ImageGallery() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTabName === 'inpainting' || activeTabName === 'outpainting') {
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
dispatch(setGalleryWidth(190));
|
||||
setGalleryMinWidth(190);
|
||||
setGalleryMaxWidth(190);
|
||||
|
@ -15,7 +15,7 @@ export default function MainHeight() {
|
||||
|
||||
return (
|
||||
<IAISelect
|
||||
isDisabled={activeTabName === 'inpainting'}
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label="Height"
|
||||
value={height}
|
||||
flexGrow={1}
|
||||
|
@ -16,7 +16,7 @@ export default function MainWidth() {
|
||||
|
||||
return (
|
||||
<IAISelect
|
||||
isDisabled={activeTabName === 'inpainting'}
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label="Width"
|
||||
value={width}
|
||||
flexGrow={1}
|
||||
|
@ -142,7 +142,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
},
|
||||
];
|
||||
|
||||
const inpaintingHotkeys = [
|
||||
const unifiedCanvasHotkeys = [
|
||||
{
|
||||
title: 'Select Brush',
|
||||
desc: 'Selects the inpainting brush',
|
||||
@ -223,9 +223,6 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
desc: 'Expand your inpainting work area',
|
||||
hotkey: 'Shift+J',
|
||||
},
|
||||
];
|
||||
|
||||
const outpaintingHotkeys = [
|
||||
{
|
||||
title: 'Erase Canvas',
|
||||
desc: 'Erase the images on the canvas',
|
||||
@ -301,17 +298,7 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(inpaintingHotkeys)}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<AccordionButton className="hotkeys-modal-button">
|
||||
<h2>Outpainting Hotkeys</h2>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(outpaintingHotkeys)}
|
||||
{renderHotkeyModalItems(unifiedCanvasHotkeys)}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
@ -1,80 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import IAICanvasControls from 'features/canvas/IAICanvasControls';
|
||||
import IAICanvasResizer from 'features/canvas/IAICanvasResizer';
|
||||
import _ from 'lodash';
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import ImageUploadButton from 'common/components/ImageUploaderButton';
|
||||
import CurrentImageDisplay from 'features/gallery/CurrentImageDisplay';
|
||||
import { OptionsState } from 'features/options/optionsSlice';
|
||||
import {
|
||||
initialCanvasImageSelector,
|
||||
CanvasState,
|
||||
setDoesCanvasNeedScaling,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import IAICanvas from 'features/canvas/IAICanvas';
|
||||
|
||||
const inpaintingDisplaySelector = createSelector(
|
||||
[
|
||||
initialCanvasImageSelector,
|
||||
(state: RootState) => state.canvas,
|
||||
(state: RootState) => state.options,
|
||||
],
|
||||
(initialCanvasImage, canvas: CanvasState, options: OptionsState) => {
|
||||
const { doesCanvasNeedScaling } = canvas;
|
||||
const { showDualDisplay } = options;
|
||||
return {
|
||||
doesCanvasNeedScaling,
|
||||
showDualDisplay,
|
||||
initialCanvasImage,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const InpaintingDisplay = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { showDualDisplay, doesCanvasNeedScaling, initialCanvasImage } =
|
||||
useAppSelector(inpaintingDisplaySelector);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const resizeCallback = _.debounce(
|
||||
() => dispatch(setDoesCanvasNeedScaling(true)),
|
||||
250
|
||||
);
|
||||
window.addEventListener('resize', resizeCallback);
|
||||
return () => window.removeEventListener('resize', resizeCallback);
|
||||
}, [dispatch]);
|
||||
|
||||
const inpaintingComponent = initialCanvasImage ? (
|
||||
<div className="inpainting-main-area">
|
||||
<IAICanvasControls />
|
||||
<div className="inpainting-canvas-area">
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<ImageUploadButton />
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
showDualDisplay ? 'workarea-split-view' : 'workarea-single-view'
|
||||
}
|
||||
>
|
||||
<div className="workarea-split-view-left">{inpaintingComponent}</div>
|
||||
{showDualDisplay && (
|
||||
<div className="workarea-split-view-right">
|
||||
<CurrentImageDisplay />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InpaintingDisplay;
|
@ -1,65 +0,0 @@
|
||||
// import { Feature } from 'app/features';
|
||||
import { Feature } from 'app/features';
|
||||
import { RootState, useAppSelector } from 'app/store';
|
||||
import FaceRestoreHeader from 'features/options/AdvancedOptions/FaceRestore/FaceRestoreHeader';
|
||||
import FaceRestoreOptions from 'features/options/AdvancedOptions/FaceRestore/FaceRestoreOptions';
|
||||
import ImageToImageStrength from 'features/options/AdvancedOptions/ImageToImage/ImageToImageStrength';
|
||||
import InpaintingSettings from 'features/options/AdvancedOptions/Inpainting/InpaintingSettings';
|
||||
import SeedHeader from 'features/options/AdvancedOptions/Seed/SeedHeader';
|
||||
import SeedOptions from 'features/options/AdvancedOptions/Seed/SeedOptions';
|
||||
import UpscaleHeader from 'features/options/AdvancedOptions/Upscale/UpscaleHeader';
|
||||
import UpscaleOptions from 'features/options/AdvancedOptions/Upscale/UpscaleOptions';
|
||||
import VariationsHeader from 'features/options/AdvancedOptions/Variations/VariationsHeader';
|
||||
import VariationsOptions from 'features/options/AdvancedOptions/Variations/VariationsOptions';
|
||||
import MainAdvancedOptionsCheckbox from 'features/options/MainOptions/MainAdvancedOptionsCheckbox';
|
||||
import MainOptions from 'features/options/MainOptions/MainOptions';
|
||||
import OptionsAccordion from 'features/options/OptionsAccordion';
|
||||
import ProcessButtons from 'features/options/ProcessButtons/ProcessButtons';
|
||||
import PromptInput from 'features/options/PromptInput/PromptInput';
|
||||
import InvokeOptionsPanel from 'features/tabs/InvokeOptionsPanel';
|
||||
|
||||
export default function InpaintingPanel() {
|
||||
const showAdvancedOptions = useAppSelector(
|
||||
(state: RootState) => state.options.showAdvancedOptions
|
||||
);
|
||||
|
||||
const imageToImageAccordions = {
|
||||
seed: {
|
||||
header: <SeedHeader />,
|
||||
feature: Feature.SEED,
|
||||
options: <SeedOptions />,
|
||||
},
|
||||
variations: {
|
||||
header: <VariationsHeader />,
|
||||
feature: Feature.VARIATIONS,
|
||||
options: <VariationsOptions />,
|
||||
},
|
||||
face_restore: {
|
||||
header: <FaceRestoreHeader />,
|
||||
feature: Feature.FACE_CORRECTION,
|
||||
options: <FaceRestoreOptions />,
|
||||
},
|
||||
upscale: {
|
||||
header: <UpscaleHeader />,
|
||||
feature: Feature.UPSCALE,
|
||||
options: <UpscaleOptions />,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<InvokeOptionsPanel>
|
||||
<PromptInput />
|
||||
<ProcessButtons />
|
||||
<MainOptions />
|
||||
<ImageToImageStrength
|
||||
label="Image To Image Strength"
|
||||
styleClass="main-option-block image-to-image-strength-main-option"
|
||||
/>
|
||||
<InpaintingSettings />
|
||||
<MainAdvancedOptionsCheckbox />
|
||||
{showAdvancedOptions && (
|
||||
<OptionsAccordion accordionInfo={imageToImageAccordions} />
|
||||
)}
|
||||
</InvokeOptionsPanel>
|
||||
);
|
||||
}
|
@ -17,12 +17,12 @@ import {
|
||||
setShouldShowOptionsPanel,
|
||||
} from 'features/options/optionsSlice';
|
||||
import ImageToImageWorkarea from './ImageToImage';
|
||||
import InpaintingWorkarea from './Inpainting/InpaintingWorkarea';
|
||||
import TextToImageWorkarea from './TextToImage';
|
||||
import Lightbox from 'features/lightbox/Lightbox';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import OutpaintingWorkarea from './Outpainting/OutpaintingWorkarea';
|
||||
import UnifiedCanvasWorkarea from './UnifiedCanvas/UnifiedCanvasWorkarea';
|
||||
import { setShouldShowGallery } from 'features/gallery/gallerySlice';
|
||||
import UnifiedCanvasIcon from 'common/icons/UnifiedCanvasIcon';
|
||||
|
||||
export const tabDict = {
|
||||
txt2img: {
|
||||
@ -35,15 +35,10 @@ export const tabDict = {
|
||||
workarea: <ImageToImageWorkarea />,
|
||||
tooltip: 'Image To Image',
|
||||
},
|
||||
inpainting: {
|
||||
title: <InpaintIcon fill={'black'} boxSize={'2.5rem'} />,
|
||||
workarea: <InpaintingWorkarea />,
|
||||
tooltip: 'Inpainting',
|
||||
},
|
||||
outpainting: {
|
||||
title: <OutpaintIcon fill={'black'} boxSize={'2.5rem'} />,
|
||||
workarea: <OutpaintingWorkarea />,
|
||||
tooltip: 'Outpainting',
|
||||
unifiedCanvas: {
|
||||
title: <UnifiedCanvasIcon fill={'black'} boxSize={'2.5rem'} />,
|
||||
workarea: <UnifiedCanvasWorkarea />,
|
||||
tooltip: 'Unified Canvas',
|
||||
},
|
||||
nodes: {
|
||||
title: <NodesIcon fill={'black'} boxSize={'2.5rem'} />,
|
||||
|
@ -1,21 +0,0 @@
|
||||
import OutpaintingPanel from './OutpaintingPanel';
|
||||
import OutpaintingDisplay from './OutpaintingDisplay';
|
||||
import InvokeWorkarea from 'features/tabs/InvokeWorkarea';
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import { useEffect } from 'react';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
|
||||
export default function OutpaintingWorkarea() {
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
}, [dispatch]);
|
||||
return (
|
||||
<InvokeWorkarea
|
||||
optionsPanel={<OutpaintingPanel />}
|
||||
styleClass="inpainting-workarea-overrides"
|
||||
>
|
||||
<OutpaintingDisplay />
|
||||
</InvokeWorkarea>
|
||||
);
|
||||
}
|
@ -12,7 +12,7 @@ import {
|
||||
import IAICanvas from 'features/canvas/IAICanvas';
|
||||
import IAICanvasOutpaintingControls from 'features/canvas/IAICanvasOutpaintingControls';
|
||||
|
||||
const outpaintingDisplaySelector = createSelector(
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const {
|
||||
@ -31,11 +31,10 @@ const outpaintingDisplaySelector = createSelector(
|
||||
}
|
||||
);
|
||||
|
||||
const OutpaintingDisplay = () => {
|
||||
const UnifiedCanvasDisplay = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { doesCanvasNeedScaling, doesOutpaintingHaveObjects } = useAppSelector(
|
||||
outpaintingDisplaySelector
|
||||
);
|
||||
const { doesCanvasNeedScaling, doesOutpaintingHaveObjects } =
|
||||
useAppSelector(selector);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const resizeCallback = _.debounce(
|
||||
@ -46,17 +45,6 @@ const OutpaintingDisplay = () => {
|
||||
return () => window.removeEventListener('resize', resizeCallback);
|
||||
}, [dispatch]);
|
||||
|
||||
const outpaintingComponent = doesOutpaintingHaveObjects ? (
|
||||
<div className="inpainting-main-area">
|
||||
<IAICanvasOutpaintingControls />
|
||||
<div className="inpainting-canvas-area">
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<ImageUploadButton />
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'workarea-single-view'}>
|
||||
<div className="workarea-split-view-left">
|
||||
@ -67,9 +55,8 @@ const OutpaintingDisplay = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="workarea-split-view-left">{outpaintingComponent}</div> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OutpaintingDisplay;
|
||||
export default UnifiedCanvasDisplay;
|
@ -18,7 +18,7 @@ import ProcessButtons from 'features/options/ProcessButtons/ProcessButtons';
|
||||
import PromptInput from 'features/options/PromptInput/PromptInput';
|
||||
import InvokeOptionsPanel from 'features/tabs/InvokeOptionsPanel';
|
||||
|
||||
export default function OutpaintingPanel() {
|
||||
export default function UnifiedCanvasPanel() {
|
||||
const showAdvancedOptions = useAppSelector(
|
||||
(state: RootState) => state.options.showAdvancedOptions
|
||||
);
|
@ -1,21 +1,21 @@
|
||||
import InpaintingPanel from './InpaintingPanel';
|
||||
import InpaintingDisplay from './InpaintingDisplay';
|
||||
import UnifiedCanvasPanel from './UnifiedCanvasPanel';
|
||||
import UnifiedCanvasDisplay from './UnifiedCanvasDisplay';
|
||||
import InvokeWorkarea from 'features/tabs/InvokeWorkarea';
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import { useEffect } from 'react';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
|
||||
export default function InpaintingWorkarea() {
|
||||
export default function UnifiedCanvasWorkarea() {
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
}, [dispatch]);
|
||||
return (
|
||||
<InvokeWorkarea
|
||||
optionsPanel={<InpaintingPanel />}
|
||||
optionsPanel={<UnifiedCanvasPanel />}
|
||||
styleClass="inpainting-workarea-overrides"
|
||||
>
|
||||
<InpaintingDisplay />
|
||||
<UnifiedCanvasDisplay />
|
||||
</InvokeWorkarea>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user