Fixes empty canvas detection

This commit is contained in:
psychedelicious 2022-10-31 11:06:56 +11:00
parent bb5d77a9fb
commit 7a4ceb0f7c

View File

@ -2,6 +2,79 @@ import Konva from 'konva';
import { IRect } from 'konva/lib/types';
import { MaskLine } from '../inpaintingSlice';
/**
* Re-draws the mask canvas onto a new Konva stage.
*/
export const generateMaskCanvas = (
image: HTMLImageElement,
lines: MaskLine[]
): {
stage: Konva.Stage;
layer: Konva.Layer;
} => {
const { width, height } = image;
const offscreenContainer = document.createElement('div');
const stage = new Konva.Stage({
container: offscreenContainer,
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
lines.forEach((line) =>
layer.add(
new Konva.Line({
points: line.points,
stroke: 'rgb(0,0,0)',
strokeWidth: line.strokeWidth * 2,
tension: 0,
lineCap: 'round',
lineJoin: 'round',
shadowForStrokeEnabled: false,
globalCompositeOperation:
line.tool === 'brush' ? 'source-over' : 'destination-out',
})
)
);
layer.draw();
offscreenContainer.remove();
return { stage, layer };
};
/**
* Check if the bounding box region has only fully transparent pixels.
*/
export const checkIsRegionEmpty = (
stage: Konva.Stage,
boundingBox: IRect
): boolean => {
const imageData = stage
.toCanvas()
.getContext('2d')
?.getImageData(
boundingBox.x,
boundingBox.y,
boundingBox.width,
boundingBox.height
);
if (!imageData) {
throw new Error('Unable to get image data from generated canvas');
}
const pixelBuffer = new Uint32Array(imageData.data.buffer);
return !pixelBuffer.some((color) => color !== 0);
};
/**
* Generating a mask image from InpaintingCanvas.tsx is not as simple
* as calling toDataURL() on the canvas, because the mask may be represented
@ -16,61 +89,20 @@ const generateMask = (
image: HTMLImageElement,
lines: MaskLine[],
boundingBox: IRect
) => {
const { x, y, width, height } = boundingBox;
): { maskDataURL: string; isMaskEmpty: boolean } => {
// create an offscreen canvas and add the mask to it
const { stage, layer } = generateMaskCanvas(image, lines);
const offscreenContainer = document.createElement('div');
const stage = new Konva.Stage({
container: offscreenContainer,
width: image.width,
height: image.height,
});
const layer = new Konva.Layer();
stage.add(layer);
lines.forEach((line) =>
layer.add(
new Konva.Line({
points: line.points,
stroke: 'rgb(255,255,255)',
strokeWidth: line.strokeWidth * 2,
tension: 0,
lineCap: 'round',
lineJoin: 'round',
shadowForStrokeEnabled: false,
globalCompositeOperation:
line.tool === 'brush' ? 'source-over' : 'destination-out',
})
)
);
// check if mask is empty
const pixelBuffer = new Uint32Array(
layer.getContext().getImageData(x, y, width, height).data.buffer
);
const isMaskEmpty = !pixelBuffer.some((color) => color !== 0);
if (isMaskEmpty) {
layer.add(
new Konva.Rect({
...boundingBox,
fill: 'rgb(0,0,0)',
})
);
}
// check if the mask layer is empty
const isMaskEmpty = checkIsRegionEmpty(stage, boundingBox);
// composite the image onto the mask layer
layer.add(
new Konva.Image({ image: image, globalCompositeOperation: 'source-out' })
);
const maskDataURL = stage.toDataURL();
offscreenContainer.remove();
return { maskDataURL, isMaskEmpty };
};