Abandons "inpainting" canvas lock

This commit is contained in:
psychedelicious 2022-11-18 13:02:47 +11:00 committed by blessedcoolant
parent c0005eb063
commit 635e7da05d
15 changed files with 99 additions and 392 deletions

View File

@ -84,14 +84,14 @@ export const store = configureStore({
devTools: {
// Uncommenting these very rapidly called actions makes the redux dev tools output much more readable
actionsDenylist: [
// 'canvas/setCursorPosition',
// 'canvas/setStageCoordinates',
// 'canvas/setStageScale',
// 'canvas/setIsDrawing',
'canvas/setCursorPosition',
'canvas/setStageCoordinates',
'canvas/setStageScale',
'canvas/setIsDrawing',
// 'canvas/setBoundingBoxCoordinates',
// 'canvas/setBoundingBoxDimensions',
// 'canvas/setIsDrawing',
// 'canvas/addPointToCurrentLine',
'canvas/setIsDrawing',
'canvas/addPointToCurrentLine',
],
},
});

View File

@ -1,13 +1,11 @@
import { MutableRefObject, useCallback, useRef } from 'react';
import { MutableRefObject, useRef } from 'react';
import Konva from 'konva';
import { Layer, Stage } from 'react-konva';
import { Stage as StageType } from 'konva/lib/Stage';
import { useAppSelector } from 'app/store';
import {
initialCanvasImageSelector,
canvasSelector,
isStagingSelector,
shouldLockToInitialImageSelector,
} from 'features/canvas/store/canvasSelectors';
import IAICanvasMaskLines from './IAICanvasMaskLines';
import IAICanvasBrushPreview from './IAICanvasBrushPreview';
@ -16,7 +14,6 @@ import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
import useCanvasHotkeys from '../hooks/useCanvasHotkeys';
import _ from 'lodash';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector } from 'features/options/optionsSelectors';
import IAICanvasMaskCompositer from './IAICanvasMaskCompositer';
import useCanvasWheel from '../hooks/useCanvasZoom';
import useCanvasMouseDown from '../hooks/useCanvasMouseDown';
@ -33,20 +30,8 @@ import IAICanvasStagingArea from './IAICanvasStagingArea';
import IAICanvasStagingAreaToolbar from './IAICanvasStagingAreaToolbar';
const selector = createSelector(
[
shouldLockToInitialImageSelector,
canvasSelector,
isStagingSelector,
activeTabNameSelector,
initialCanvasImageSelector,
],
(
shouldLockToInitialImage,
canvas,
isStaging,
activeTabName,
initialCanvasImage
) => {
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
const {
isMaskEnabled,
stageScale,
@ -90,8 +75,6 @@ const selector = createSelector(
tool,
isStaging,
shouldShowIntermediates,
shouldLockToInitialImage,
initialCanvasImage,
};
},
{
@ -118,8 +101,6 @@ const IAICanvas = () => {
tool,
isStaging,
shouldShowIntermediates,
shouldLockToInitialImage,
initialCanvasImage,
} = useAppSelector(selector);
useCanvasHotkeys();
@ -145,34 +126,6 @@ const IAICanvas = () => {
const { handleDragStart, handleDragMove, handleDragEnd } =
useCanvasDragMove();
const dragBoundFunc = useCallback(
(newCoordinates: Vector2d) => {
if (shouldLockToInitialImage && initialCanvasImage) {
newCoordinates.x = _.clamp(
newCoordinates.x,
stageDimensions.width -
Math.floor(initialCanvasImage.width * stageScale),
0
);
newCoordinates.y = _.clamp(
newCoordinates.y,
stageDimensions.height -
Math.floor(initialCanvasImage.height * stageScale),
0
);
}
return newCoordinates;
},
[
initialCanvasImage,
shouldLockToInitialImage,
stageDimensions.height,
stageDimensions.width,
stageScale,
]
);
return (
<div className="inpainting-canvas-container">
<div className="inpainting-canvas-wrapper">
@ -188,7 +141,6 @@ const IAICanvas = () => {
width={stageDimensions.width}
height={stageDimensions.height}
scale={{ x: stageScale, y: stageScale }}
dragBoundFunc={dragBoundFunc}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseOut}

View File

@ -38,22 +38,20 @@ const IAICanvasObjectRenderer = () => {
);
} else if (isCanvasBaseLine(obj)) {
return (
<Group {...obj.clipRect}>
<Line
key={i}
points={obj.points}
stroke={obj.color ? rgbaColorToString(obj.color) : 'rgb(0,0,0)'} // The lines can be any color, just need alpha > 0
strokeWidth={obj.strokeWidth * 2}
tension={0}
lineCap="round"
lineJoin="round"
shadowForStrokeEnabled={false}
listening={false}
globalCompositeOperation={
obj.tool === 'brush' ? 'source-over' : 'destination-out'
}
/>
</Group>
<Line
key={i}
points={obj.points}
stroke={obj.color ? rgbaColorToString(obj.color) : 'rgb(0,0,0)'} // The lines can be any color, just need alpha > 0
strokeWidth={obj.strokeWidth * 2}
tension={0}
lineCap="round"
lineJoin="round"
shadowForStrokeEnabled={false}
listening={false}
globalCompositeOperation={
obj.tool === 'brush' ? 'source-over' : 'destination-out'
}
/>
);
}
})}

View File

@ -9,21 +9,19 @@ import {
setDoesCanvasNeedScaling,
} from 'features/canvas/store/canvasSlice';
import { createSelector } from '@reduxjs/toolkit';
import { canvasSelector, initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
import {
canvasSelector,
initialCanvasImageSelector,
} from 'features/canvas/store/canvasSelectors';
const canvasResizerSelector = createSelector(
canvasSelector,
initialCanvasImageSelector,
activeTabNameSelector,
(canvas, initialCanvasImage, activeTabName) => {
const {
doesCanvasNeedScaling,
shouldLockToInitialImage,
isCanvasInitialized,
} = canvas;
const { doesCanvasNeedScaling, isCanvasInitialized } = canvas;
return {
doesCanvasNeedScaling,
shouldLockToInitialImage,
activeTabName,
initialCanvasImage,
isCanvasInitialized,
@ -35,7 +33,6 @@ const IAICanvasResizer = () => {
const dispatch = useAppDispatch();
const {
doesCanvasNeedScaling,
shouldLockToInitialImage,
activeTabName,
initialCanvasImage,
isCanvasInitialized,
@ -58,11 +55,7 @@ const IAICanvasResizer = () => {
})
);
if (!isCanvasInitialized || shouldLockToInitialImage) {
dispatch(resizeAndScaleCanvas());
} else {
dispatch(resizeCanvas());
}
dispatch(resizeCanvas());
dispatch(setDoesCanvasNeedScaling(false));
}, 0);
@ -72,7 +65,6 @@ const IAICanvasResizer = () => {
doesCanvasNeedScaling,
activeTabName,
isCanvasInitialized,
shouldLockToInitialImage,
]);
return (

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { Box } from 'konva/lib/shapes/Transformer';
import { Vector2d } from 'konva/lib/types';
import _ from 'lodash';
import { useCallback, useEffect, useRef } from 'react';
import { Group, Rect, Transformer } from 'react-konva';
import { useAppDispatch, useAppSelector } from 'app/store';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import {
initialCanvasImageSelector,
canvasSelector,
shouldLockToInitialImageSelector,
} from 'features/canvas/store/canvasSelectors';
roundDownToMultiple,
roundToMultiple,
} from 'common/util/roundDownToMultiple';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import {
setBoundingBoxCoordinates,
setBoundingBoxDimensions,
@ -21,14 +19,10 @@ import {
setIsTransformingBoundingBox,
} from 'features/canvas/store/canvasSlice';
import { GroupConfig } from 'konva/lib/Group';
import { activeTabNameSelector } from 'features/options/optionsSelectors';
const boundingBoxPreviewSelector = createSelector(
shouldLockToInitialImageSelector,
canvasSelector,
initialCanvasImageSelector,
activeTabNameSelector,
(shouldLockToInitialImage, canvas, initialCanvasImage, activeTabName) => {
(canvas) => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
@ -54,14 +48,11 @@ const boundingBoxPreviewSelector = createSelector(
isTransformingBoundingBox,
stageDimensions,
stageScale,
initialCanvasImage,
activeTabName,
shouldSnapToGrid,
tool,
stageCoordinates,
boundingBoxStrokeWidth: (isMouseOverBoundingBox ? 8 : 1) / stageScale,
hitStrokeWidth: 20 / stageScale,
shouldLockToInitialImage,
};
},
{
@ -88,13 +79,10 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
stageCoordinates,
stageDimensions,
stageScale,
initialCanvasImage,
activeTabName,
shouldSnapToGrid,
tool,
boundingBoxStrokeWidth,
hitStrokeWidth,
shouldLockToInitialImage,
} = useAppSelector(boundingBoxPreviewSelector);
const transformerRef = useRef<Konva.Transformer>(null);
@ -139,40 +127,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
[dispatch, shouldSnapToGrid]
);
const dragBoundFunc = useCallback(
(position: Vector2d) => {
/**
* This limits the bounding box's drag coordinates.
*/
if (!shouldLockToInitialImage) return boundingBoxCoordinates;
const { x, y } = position;
const maxX =
stageDimensions.width -
boundingBoxDimensions.width -
(stageDimensions.width % 64);
const maxY =
stageDimensions.height -
boundingBoxDimensions.height -
(stageDimensions.height % 64);
const clampedX = Math.floor(_.clamp(x, 0, maxX));
const clampedY = Math.floor(_.clamp(y, 0, maxY));
return { x: clampedX, y: clampedY };
},
[
shouldLockToInitialImage,
boundingBoxCoordinates,
stageDimensions.width,
stageDimensions.height,
boundingBoxDimensions.width,
boundingBoxDimensions.height,
]
);
const handleOnTransform = useCallback(() => {
/**
* The Konva Transformer changes the object's anchor point and scale factor,
@ -202,15 +156,15 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
dispatch(
setBoundingBoxCoordinates({
x,
y,
x: shouldSnapToGrid ? roundDownToMultiple(x, 64) : x,
y: shouldSnapToGrid ? roundDownToMultiple(y, 64) : y,
})
);
// Reset the scale now that the coords/dimensions have been un-scaled
rect.scaleX(1);
rect.scaleY(1);
}, [dispatch]);
}, [dispatch, shouldSnapToGrid]);
const anchorDragBoundFunc = useCallback(
(
@ -225,40 +179,26 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
*
* We need to snap the new dimensions to steps of 64. But because the whole
* stage is scaled, our actual desired step is actually 64 * the stage scale.
*
* Additionally, we need to ensure we offset the position so that we snap to a
* multiple of 64 that is aligned with the grid, and not from the absolute zero
* coordinate.
*/
return {
x: roundToMultiple(newPos.x, scaledStep),
y: roundToMultiple(newPos.y, scaledStep),
// Calculate the offset of the grid.
const offsetX = oldPos.x % scaledStep;
const offsetY = oldPos.y % scaledStep;
const newCoordinates = {
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
};
return newCoordinates;
},
[scaledStep]
);
const boundBoxFunc = useCallback(
(oldBoundBox: Box, newBoundBox: Box) => {
/**
* The transformer uses this callback to limit valid transformations.
* Unlike anchorDragBoundFunc, it does get a width and height, so
* the logic to constrain the size of the bounding box is very simple.
*/
// On the Inpainting canvas, the bounding box needs to stay in the stage
if (
shouldLockToInitialImage &&
(newBoundBox.width + newBoundBox.x > stageDimensions.width ||
newBoundBox.height + newBoundBox.y > stageDimensions.height ||
newBoundBox.x < 0 ||
newBoundBox.y < 0)
) {
return oldBoundBox;
}
return newBoundBox;
},
[shouldLockToInitialImage, stageDimensions.height, stageDimensions.width]
);
const handleStartedTransforming = () => {
dispatch(setIsTransformingBoundingBox(true));
};
@ -310,11 +250,11 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
globalCompositeOperation={'destination-out'}
/>
<Rect
{...(shouldLockToInitialImage ? { dragBoundFunc } : {})}
listening={!isDrawing && tool === 'move'}
draggable={true}
fillEnabled={false}
height={boundingBoxDimensions.height}
hitStrokeWidth={hitStrokeWidth}
listening={!isDrawing && tool === 'move'}
onDragEnd={handleEndedModifying}
onDragMove={handleOnDragMove}
onMouseDown={handleStartedMoving}
@ -329,7 +269,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
width={boundingBoxDimensions.width}
x={boundingBoxCoordinates.x}
y={boundingBoxCoordinates.y}
hitStrokeWidth={hitStrokeWidth}
/>
<Transformer
anchorCornerRadius={3}
@ -340,7 +279,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
borderDash={[4, 4]}
borderEnabled={true}
borderStroke={'black'}
boundBoxFunc={boundBoxFunc}
draggable={false}
enabledAnchors={tool === 'move' ? undefined : []}
flipEnabled={false}

View File

@ -4,7 +4,6 @@ import {
resizeAndScaleCanvas,
resetCanvas,
resetCanvasView,
setShouldLockToInitialImage,
setTool,
fitBoundingBoxToStage,
} from 'features/canvas/store/canvasSlice';
@ -39,11 +38,10 @@ import {
export const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
const { tool, shouldLockToInitialImage } = canvas;
const { tool } = canvas;
return {
tool,
isStaging,
shouldLockToInitialImage,
};
},
{
@ -55,16 +53,7 @@ export const selector = createSelector(
const IAICanvasOutpaintingControls = () => {
const dispatch = useAppDispatch();
const { tool, isStaging, shouldLockToInitialImage } =
useAppSelector(selector);
const handleToggleShouldLockToInitialImage = (
e: ChangeEvent<HTMLInputElement>
) => {
dispatch(setShouldLockToInitialImage(e.target.checked));
dispatch(resizeAndScaleCanvas());
dispatch(fitBoundingBoxToStage());
};
const { tool, isStaging } = useAppSelector(selector);
return (
<div className="inpainting-settings">
@ -151,11 +140,6 @@ const IAICanvasOutpaintingControls = () => {
onClick={() => dispatch(resetCanvas())}
/>
</ButtonGroup>
<IAICheckbox
label={'Lock Canvas to Initial Image'}
isChecked={shouldLockToInitialImage}
onChange={handleToggleShouldLockToInitialImage}
/>
</div>
);
};

View File

@ -8,9 +8,11 @@ import { MutableRefObject, useCallback } from 'react';
import {
canvasSelector,
initialCanvasImageSelector,
shouldLockToInitialImageSelector,
} from 'features/canvas/store/canvasSelectors';
import { setStageCoordinates, setStageScale } from 'features/canvas/store/canvasSlice';
import {
setStageCoordinates,
setStageScale,
} from 'features/canvas/store/canvasSlice';
import {
CANVAS_SCALE_BY,
MAX_CANVAS_SCALE,
@ -18,13 +20,8 @@ import {
} from '../util/constants';
const selector = createSelector(
[
activeTabNameSelector,
canvasSelector,
initialCanvasImageSelector,
shouldLockToInitialImageSelector,
],
(activeTabName, canvas, initialCanvasImage, shouldLockToInitialImage) => {
[activeTabNameSelector, canvasSelector, initialCanvasImageSelector],
(activeTabName, canvas, initialCanvasImage) => {
const {
isMoveStageKeyHeld,
stageScale,
@ -36,7 +33,6 @@ const selector = createSelector(
stageScale,
activeTabName,
initialCanvasImage,
shouldLockToInitialImage,
stageDimensions,
minimumStageScale,
};
@ -51,7 +47,6 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
stageScale,
activeTabName,
initialCanvasImage,
shouldLockToInitialImage,
stageDimensions,
minimumStageScale,
} = useAppSelector(selector);
@ -83,7 +78,7 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
const newScale = _.clamp(
stageScale * CANVAS_SCALE_BY ** delta,
shouldLockToInitialImage ? minimumStageScale : MIN_CANVAS_SCALE,
MIN_CANVAS_SCALE,
MAX_CANVAS_SCALE
);
@ -92,36 +87,10 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
y: cursorPos.y - mousePointTo.y * newScale,
};
if (shouldLockToInitialImage) {
newCoordinates.x = _.clamp(
newCoordinates.x,
stageDimensions.width -
Math.floor(initialCanvasImage.width * newScale),
0
);
newCoordinates.y = _.clamp(
newCoordinates.y,
stageDimensions.height -
Math.floor(initialCanvasImage.height * newScale),
0
);
}
dispatch(setStageScale(newScale));
dispatch(setStageCoordinates(newCoordinates));
},
[
activeTabName,
stageRef,
isMoveStageKeyHeld,
initialCanvasImage,
stageScale,
shouldLockToInitialImage,
minimumStageScale,
dispatch,
stageDimensions.width,
stageDimensions.height,
]
[stageRef, isMoveStageKeyHeld, initialCanvasImage, stageScale, dispatch]
);
};

View File

@ -6,9 +6,6 @@ export const canvasSelector = (state: RootState): CanvasState => state.canvas;
export const isStagingSelector = (state: RootState): boolean =>
state.canvas.layerState.stagingArea.images.length > 0;
export const shouldLockToInitialImageSelector = (state: RootState): boolean =>
state.canvas.shouldLockToInitialImage;
export const initialCanvasImageSelector = (
state: RootState
): CanvasImage | undefined =>

View File

@ -49,7 +49,6 @@ const initialCanvasState: CanvasState = {
eraserSize: 50,
futureLayerStates: [],
inpaintReplace: 0.1,
initialCanvasImageClipRect: undefined,
isCanvasInitialized: false,
isDrawing: false,
isMaskEnabled: true,
@ -68,7 +67,6 @@ const initialCanvasState: CanvasState = {
shouldAutoSave: false,
shouldDarkenOutsideBoundingBox: false,
shouldLockBoundingBox: false,
shouldLockToInitialImage: false,
shouldPreserveMaskedArea: false,
shouldShowBoundingBox: true,
shouldShowBrush: true,
@ -298,10 +296,6 @@ export const canvasSlice = createSlice({
tool,
strokeWidth: newStrokeWidth,
points: action.payload,
clipRect:
state.shouldLockToInitialImage && state.initialCanvasImageClipRect
? state.initialCanvasImageClipRect
: undefined,
...newColor,
});
@ -375,16 +369,10 @@ export const canvasSlice = createSlice({
state.layerState.objects.find(isCanvasBaseImage);
if (!initialCanvasImage) return;
const { shouldLockToInitialImage, initialCanvasImageClipRect } = state;
let { width: imageWidth, height: imageHeight } = initialCanvasImage;
const { width: imageWidth, height: imageHeight } = initialCanvasImage;
if (shouldLockToInitialImage && initialCanvasImageClipRect) {
imageWidth = initialCanvasImageClipRect.clipWidth;
imageHeight = initialCanvasImageClipRect.clipHeight;
}
const padding = shouldLockToInitialImage ? 1 : 0.95;
const padding = 0.95;
const newScale = calculateScale(
containerWidth,
@ -395,12 +383,8 @@ export const canvasSlice = createSlice({
);
const newDimensions = {
width: shouldLockToInitialImage
? Math.floor(imageWidth * newScale)
: Math.floor(containerWidth),
height: shouldLockToInitialImage
? Math.floor(imageHeight * newScale)
: Math.floor(containerHeight),
width: Math.floor(containerWidth),
height: Math.floor(containerHeight),
};
const newCoordinates = calculateCoordinates(
@ -416,7 +400,7 @@ export const canvasSlice = createSlice({
if (!_.isEqual(state.stageDimensions, newDimensions)) {
state.minimumStageScale = newScale;
state.stageScale = newScale;
state.stageCoordinates = newCoordinates;
state.stageCoordinates = floorCoordinates(newCoordinates);
state.stageDimensions = newDimensions;
}
@ -440,23 +424,16 @@ export const canvasSlice = createSlice({
const { contentRect } = action.payload;
const baseCanvasImage = state.layerState.objects.find(isCanvasBaseImage);
const { shouldLockToInitialImage, initialCanvasImageClipRect } = state;
if (!baseCanvasImage) return;
const {
stageDimensions: { width: stageWidth, height: stageHeight },
} = state;
let { x, y, width, height } = contentRect;
const { x, y, width, height } = contentRect;
if (shouldLockToInitialImage && initialCanvasImageClipRect) {
x = initialCanvasImageClipRect.clipX;
y = initialCanvasImageClipRect.clipY;
width = initialCanvasImageClipRect.clipWidth;
height = initialCanvasImageClipRect.clipHeight;
}
const padding = shouldLockToInitialImage ? 1 : 0.95;
const padding = 0.95;
const newScale = calculateScale(
stageWidth,
stageHeight,
@ -515,39 +492,36 @@ export const canvasSlice = createSlice({
state.futureLayerStates = [];
},
setShouldLockToInitialImage: (state, action: PayloadAction<boolean>) => {
state.shouldLockToInitialImage = action.payload;
},
fitBoundingBoxToStage: (state) => {
const { boundingBoxDimensions, boundingBoxCoordinates, stageDimensions } =
state;
const {
boundingBoxDimensions,
boundingBoxCoordinates,
stageDimensions,
stageScale,
} = state;
const scaledStageWidth = stageDimensions.width / stageScale;
const scaledStageHeight = stageDimensions.height / stageScale;
if (
boundingBoxCoordinates.x < 0 ||
boundingBoxCoordinates.x + boundingBoxDimensions.width >
stageDimensions.width ||
scaledStageWidth ||
boundingBoxCoordinates.y < 0 ||
boundingBoxCoordinates.y + boundingBoxDimensions.height >
stageDimensions.height
scaledStageHeight
) {
const newBoundingBoxDimensions = {
width: roundDownToMultiple(
_.clamp(stageDimensions.width, 64, 512),
64
),
height: roundDownToMultiple(
_.clamp(stageDimensions.height, 64, 512),
64
),
width: roundDownToMultiple(_.clamp(scaledStageWidth, 64, 512), 64),
height: roundDownToMultiple(_.clamp(scaledStageHeight, 64, 512), 64),
};
const newBoundingBoxCoordinates = {
x: roundToMultiple(
stageDimensions.width / 2 - newBoundingBoxDimensions.width / 2,
scaledStageWidth / 2 - newBoundingBoxDimensions.width / 2,
64
),
y: roundToMultiple(
stageDimensions.height / 2 - newBoundingBoxDimensions.height / 2,
scaledStageHeight / 2 - newBoundingBoxDimensions.height / 2,
64
),
};
@ -611,7 +585,6 @@ export const {
prevStagingAreaImage,
commitStagingAreaImage,
discardStagedImages,
setShouldLockToInitialImage,
resizeAndScaleCanvas,
resizeCanvas,
resetCanvasView,

View File

@ -8,13 +8,6 @@ export type CanvasDrawingTool = 'brush' | 'eraser';
export type CanvasTool = CanvasDrawingTool | 'move';
export type ClipRect = {
clipX: number;
clipY: number;
clipWidth: number;
clipHeight: number;
};
export type Dimensions = {
width: number;
height: number;
@ -25,7 +18,6 @@ export type CanvasAnyLine = {
tool: CanvasDrawingTool;
strokeWidth: number;
points: number[];
clipRect: ClipRect | undefined;
};
export type CanvasImage = {
@ -86,7 +78,6 @@ export interface CanvasState {
doesCanvasNeedScaling: boolean;
eraserSize: number;
futureLayerStates: CanvasLayerState[];
initialCanvasImageClipRect?: ClipRect;
inpaintReplace: number;
intermediateImage?: InvokeAI.Image;
isCanvasInitialized: boolean;
@ -107,7 +98,6 @@ export interface CanvasState {
shouldAutoSave: boolean;
shouldDarkenOutsideBoundingBox: boolean;
shouldLockBoundingBox: boolean;
shouldLockToInitialImage: boolean;
shouldPreserveMaskedArea: boolean;
shouldShowBoundingBox: boolean;
shouldShowBrush: boolean;

View File

@ -47,13 +47,6 @@ export const setInitialCanvasImage = (
};
state.futureLayerStates = [];
state.initialCanvasImageClipRect = {
clipX: 0,
clipY: 0,
clipWidth: image.width,
clipHeight: image.height,
};
state.isCanvasInitialized = false;
state.doesCanvasNeedScaling = true;
};

View File

@ -1,11 +1,6 @@
import Konva from 'konva';
import { IRect } from 'konva/lib/types';
const layerToDataURL = (
layer: Konva.Layer,
stageScale: number,
boundingBox?: IRect
) => {
const layerToDataURL = (layer: Konva.Layer, stageScale: number) => {
const tempScale = layer.scale();
const relativeClientRect = layer.getClientRect({
@ -20,21 +15,12 @@ const layerToDataURL = (
const { x, y, width, height } = layer.getClientRect();
const scaledBoundingBox = boundingBox
? {
x: Math.round(boundingBox.x / stageScale),
y: Math.round(boundingBox.y / stageScale),
width: Math.round(boundingBox.width / stageScale),
height: Math.round(boundingBox.height / stageScale),
}
: {
x: Math.round(x),
y: Math.round(y),
width: Math.round(width),
height: Math.round(height),
};
const dataURL = layer.toDataURL(scaledBoundingBox);
const dataURL = layer.toDataURL({
x: Math.round(x),
y: Math.round(y),
width: Math.round(width),
height: Math.round(height),
});
// Unscale the canvas
layer.scale(tempScale);

View File

@ -22,24 +22,12 @@ export const mergeAndUploadCanvas = createAsyncThunk(
const state = getState() as RootState;
const stageScale = state.canvas.stageScale;
const clipRect = state.canvas.initialCanvasImageClipRect;
const boundingBox =
state.canvas.shouldLockToInitialImage && clipRect && saveToGallery
? {
x: clipRect.clipX,
y: clipRect.clipY,
width: clipRect.clipWidth,
height: clipRect.clipHeight,
}
: undefined;
if (!canvasImageLayerRef.current) return;
const { dataURL, boundingBox: originalBoundingBox } = layerToDataURL(
canvasImageLayerRef.current,
stageScale,
boundingBox
stageScale
);
if (!dataURL) return;

View File

@ -38,7 +38,6 @@ import {
import {
setDoesCanvasNeedScaling,
setInitialCanvasImage,
setShouldLockToInitialImage,
} from 'features/canvas/store/canvasSlice';
import { GalleryState } from './gallerySlice';
import { activeTabNameSelector } from 'features/options/optionsSelectors';
@ -317,31 +316,10 @@ const CurrentImageButtons = () => {
const handleClickShowImageDetails = () =>
dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
const handleSendToInpainting = () => {
const handleSendToCanvas = () => {
if (!currentImage) return;
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setShouldLockToInitialImage(true));
dispatch(setInitialCanvasImage(currentImage));
dispatch(setDoesCanvasNeedScaling(true));
if (activeTabName !== 'unifiedCanvas') {
dispatch(setActiveTab('unifiedCanvas'));
}
toast({
title: 'Sent to Unified Canvas',
status: 'success',
duration: 2500,
isClosable: true,
});
};
const handleSendToOutpainting = () => {
if (!currentImage) return;
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setShouldLockToInitialImage(false));
dispatch(setInitialCanvasImage(currentImage));
dispatch(setDoesCanvasNeedScaling(true));
@ -393,17 +371,10 @@ const CurrentImageButtons = () => {
</IAIButton>
<IAIButton
size={'sm'}
onClick={handleSendToInpainting}
onClick={handleSendToCanvas}
leftIcon={<FaShare />}
>
Send to Inpainting
</IAIButton>
<IAIButton
size={'sm'}
onClick={handleSendToOutpainting}
leftIcon={<FaShare />}
>
Send to Outpainting
Send to Unified Canvas
</IAIButton>
<IAIButton
size={'sm'}

View File

@ -25,7 +25,6 @@ import * as ContextMenu from '@radix-ui/react-context-menu';
import {
setDoesCanvasNeedScaling,
setInitialCanvasImage,
setShouldLockToInitialImage,
} from 'features/canvas/store/canvasSlice';
import { hoverableImageSelector } from './gallerySliceSelectors';
@ -96,10 +95,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
});
};
const handleSendToInpainting = () => {
const handleSendToCanvas = () => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setShouldLockToInitialImage(true));
dispatch(setInitialCanvasImage(image));
dispatch(setDoesCanvasNeedScaling(true));
@ -108,26 +106,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}
toast({
title: 'Sent to Inpainting',
status: 'success',
duration: 2500,
isClosable: true,
});
};
const handleSendToOutpainting = () => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setShouldLockToInitialImage(false));
dispatch(setInitialCanvasImage(image));
dispatch(setDoesCanvasNeedScaling(true));
if (activeTabName !== 'unifiedCanvas') {
dispatch(setActiveTab('unifiedCanvas'));
}
toast({
title: 'Sent to Outpainting',
title: 'Sent to Unified Canvas',
status: 'success',
duration: 2500,
isClosable: true,
@ -257,11 +236,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
<ContextMenu.Item onClickCapture={handleSendToImageToImage}>
Send to Image To Image
</ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToInpainting}>
Send to Inpainting
</ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToOutpainting}>
Send to Outpainting
<ContextMenu.Item onClickCapture={handleSendToCanvas}>
Send to Unified Canvas
</ContextMenu.Item>
<DeleteImageModal image={image}>
<ContextMenu.Item data-warning>Delete Image</ContextMenu.Item>