mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): wip canvas nodes migration 2
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import { CanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
import Konva from 'konva';
|
||||
import { Stage } from 'konva/lib/Stage';
|
||||
import { IRect } from 'konva/lib/types';
|
||||
|
||||
/**
|
||||
@ -12,10 +13,50 @@ import { IRect } from 'konva/lib/types';
|
||||
* drawing the mask and compositing everything correctly to output a valid
|
||||
* mask image.
|
||||
*/
|
||||
const generateMask = (
|
||||
export const getStageDataURL = (stage: Stage, boundingBox: IRect): string => {
|
||||
// create an offscreen canvas and add the mask to it
|
||||
// const { stage, offscreenContainer } = buildMaskStage(lines, boundingBox);
|
||||
|
||||
const dataURL = stage.toDataURL({ ...boundingBox });
|
||||
|
||||
// const imageData = stage
|
||||
// .toCanvas()
|
||||
// .getContext('2d')
|
||||
// ?.getImageData(
|
||||
// boundingBox.x,
|
||||
// boundingBox.y,
|
||||
// boundingBox.width,
|
||||
// boundingBox.height
|
||||
// );
|
||||
|
||||
// offscreenContainer.remove();
|
||||
|
||||
// return { dataURL, imageData };
|
||||
|
||||
return dataURL;
|
||||
};
|
||||
|
||||
export const getStageImageData = (
|
||||
stage: Stage,
|
||||
boundingBox: IRect
|
||||
): ImageData | undefined => {
|
||||
const imageData = stage
|
||||
.toCanvas()
|
||||
.getContext('2d')
|
||||
?.getImageData(
|
||||
boundingBox.x,
|
||||
boundingBox.y,
|
||||
boundingBox.width,
|
||||
boundingBox.height
|
||||
);
|
||||
|
||||
return imageData;
|
||||
};
|
||||
|
||||
export const buildMaskStage = (
|
||||
lines: CanvasMaskLine[],
|
||||
boundingBox: IRect
|
||||
): { dataURL: string; imageData: ImageData } => {
|
||||
): { stage: Stage; offscreenContainer: HTMLDivElement } => {
|
||||
// create an offscreen canvas and add the mask to it
|
||||
const { width, height } = boundingBox;
|
||||
|
||||
@ -57,20 +98,5 @@ const generateMask = (
|
||||
stage.add(baseLayer);
|
||||
stage.add(maskLayer);
|
||||
|
||||
const dataURL = stage.toDataURL({ ...boundingBox });
|
||||
const imageData = stage
|
||||
.toCanvas()
|
||||
.getContext('2d')
|
||||
?.getImageData(
|
||||
boundingBox.x,
|
||||
boundingBox.y,
|
||||
boundingBox.width,
|
||||
boundingBox.height
|
||||
);
|
||||
|
||||
offscreenContainer.remove();
|
||||
|
||||
return { dataURL, imageData };
|
||||
return { stage, offscreenContainer };
|
||||
};
|
||||
|
||||
export default generateMask;
|
||||
|
@ -1,24 +1,27 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
|
||||
import { isCanvasMaskLine } from '../store/canvasTypes';
|
||||
import generateMask from './generateMask';
|
||||
import {
|
||||
buildMaskStage,
|
||||
getStageDataURL,
|
||||
getStageImageData,
|
||||
} from './generateMask';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import {
|
||||
areAnyPixelsBlack,
|
||||
getImageDataTransparency,
|
||||
getIsImageDataWhite,
|
||||
} from 'common/util/arrayBuffer';
|
||||
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
||||
import { masks } from 'dateformat';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'getCanvasDataURLs' });
|
||||
|
||||
export const getCanvasDataURLs = (state: RootState) => {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const canvasStage = getCanvasStage();
|
||||
|
||||
if (!canvasBaseLayer || !canvasStage) {
|
||||
log.error(
|
||||
{ namespace: 'getCanvasDataURLs' },
|
||||
'Unable to find canvas / stage'
|
||||
);
|
||||
moduleLog.error('Unable to find canvas / stage');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,30 +58,46 @@ export const getCanvasDataURLs = (state: RootState) => {
|
||||
|
||||
const absPos = canvasBaseLayer.getAbsolutePosition();
|
||||
|
||||
const { dataURL: maskDataURL, imageData: maskImageData } = generateMask(
|
||||
isMaskEnabled ? objects.filter(isCanvasMaskLine) : [],
|
||||
{
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
}
|
||||
);
|
||||
|
||||
const baseDataURL = canvasBaseLayer.toDataURL({
|
||||
const offsetBoundingBox = {
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
});
|
||||
};
|
||||
|
||||
const { stage: maskStage, offscreenContainer } = buildMaskStage(
|
||||
isMaskEnabled ? objects.filter(isCanvasMaskLine) : [],
|
||||
offsetBoundingBox
|
||||
);
|
||||
|
||||
const maskDataURL = maskStage.toDataURL(offsetBoundingBox);
|
||||
|
||||
const maskImageData = maskStage
|
||||
.toCanvas()
|
||||
.getContext('2d')
|
||||
?.getImageData(
|
||||
offsetBoundingBox.x,
|
||||
offsetBoundingBox.y,
|
||||
offsetBoundingBox.width,
|
||||
offsetBoundingBox.height
|
||||
);
|
||||
|
||||
offscreenContainer.remove();
|
||||
|
||||
if (!maskImageData) {
|
||||
moduleLog.error('Unable to get mask stage context');
|
||||
return;
|
||||
}
|
||||
|
||||
const baseDataURL = canvasBaseLayer.toDataURL(offsetBoundingBox);
|
||||
|
||||
const ctx = canvasBaseLayer.getContext();
|
||||
|
||||
const baseImageData = ctx.getImageData(
|
||||
boundingBox.x + absPos.x,
|
||||
boundingBox.y + absPos.y,
|
||||
boundingBox.width,
|
||||
boundingBox.height
|
||||
offsetBoundingBox.x,
|
||||
offsetBoundingBox.y,
|
||||
offsetBoundingBox.width,
|
||||
offsetBoundingBox.height
|
||||
);
|
||||
|
||||
const {
|
||||
@ -86,6 +105,7 @@ export const getCanvasDataURLs = (state: RootState) => {
|
||||
isFullyTransparent: baseIsFullyTransparent,
|
||||
} = getImageDataTransparency(baseImageData);
|
||||
|
||||
// const doesMaskHaveBlackPixels = false;
|
||||
const doesMaskHaveBlackPixels = areAnyPixelsBlack(maskImageData);
|
||||
|
||||
if (state.system.enableImageDebugging) {
|
||||
|
@ -0,0 +1,102 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
|
||||
import {
|
||||
CanvasObject,
|
||||
Dimensions,
|
||||
isCanvasMaskLine,
|
||||
} from '../store/canvasTypes';
|
||||
import { buildMaskStage, getStageImageData } from './generateMask';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import {
|
||||
areAnyPixelsBlack,
|
||||
getImageDataTransparency,
|
||||
} from 'common/util/arrayBuffer';
|
||||
import { getNodeType } from 'features/nodes/util/getNodeType';
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'getCanvasNodeTypes' });
|
||||
|
||||
export type GetCanvasNodeTypeArg = {
|
||||
objects: CanvasObject[];
|
||||
boundingBoxCoordinates: Vector2d;
|
||||
boundingBoxDimensions: Dimensions;
|
||||
stageScale: number;
|
||||
isMaskEnabled: boolean;
|
||||
};
|
||||
|
||||
export const getCanvasNodeType = (arg: GetCanvasNodeTypeArg) => {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const canvasStage = getCanvasStage();
|
||||
|
||||
if (!canvasBaseLayer || !canvasStage) {
|
||||
moduleLog.error('Unable to find canvas / stage');
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
objects,
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
stageScale,
|
||||
isMaskEnabled,
|
||||
} = arg;
|
||||
|
||||
const boundingBox = {
|
||||
...boundingBoxCoordinates,
|
||||
...boundingBoxDimensions,
|
||||
};
|
||||
|
||||
const tempScale = canvasBaseLayer.scale();
|
||||
|
||||
canvasBaseLayer.scale({
|
||||
x: 1 / stageScale,
|
||||
y: 1 / stageScale,
|
||||
});
|
||||
|
||||
const absPos = canvasBaseLayer.getAbsolutePosition();
|
||||
|
||||
const scaledBoundingBox = {
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
};
|
||||
|
||||
const { stage: maskStage, offscreenContainer } = buildMaskStage(
|
||||
isMaskEnabled ? objects.filter(isCanvasMaskLine) : [],
|
||||
scaledBoundingBox
|
||||
);
|
||||
|
||||
const maskImageData = getStageImageData(maskStage, scaledBoundingBox);
|
||||
|
||||
offscreenContainer.remove();
|
||||
|
||||
if (!maskImageData) {
|
||||
moduleLog.error('Unable to get mask stage context');
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = canvasBaseLayer.getContext();
|
||||
|
||||
const baseImageData = ctx.getImageData(
|
||||
boundingBox.x + absPos.x,
|
||||
boundingBox.y + absPos.y,
|
||||
boundingBox.width,
|
||||
boundingBox.height
|
||||
);
|
||||
|
||||
canvasBaseLayer.scale(tempScale);
|
||||
|
||||
const {
|
||||
isPartiallyTransparent: baseIsPartiallyTransparent,
|
||||
isFullyTransparent: baseIsFullyTransparent,
|
||||
} = getImageDataTransparency(baseImageData);
|
||||
|
||||
const doesMaskHaveBlackPixels = areAnyPixelsBlack(maskImageData);
|
||||
|
||||
return getNodeType(
|
||||
baseIsPartiallyTransparent,
|
||||
baseIsFullyTransparent,
|
||||
doesMaskHaveBlackPixels
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user