1
0
Fork 0

fix(gui): draw single clicks and avoid blending mask with itself

This commit is contained in:
Sean Sube 2023-01-19 17:18:59 -06:00
parent df7bba47dd
commit 5e23f84e5a
1 changed files with 56 additions and 48 deletions

View File

@ -48,19 +48,6 @@ export function MaskCanvas(props: MaskCanvasProps) {
const { source, mask } = props; const { source, mask } = props;
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
function saveMask(): void {
if (doesExist(bufferRef.current)) {
if (maskState.current === MASK_STATE.clean) {
return;
}
bufferRef.current.toBlob((blob) => {
maskState.current = MASK_STATE.clean;
props.onSave(mustExist(blob));
});
}
}
function drawBuffer() { function drawBuffer() {
if (doesExist(brushRef.current) && doesExist(bufferRef.current) && doesExist(canvasRef.current)) { if (doesExist(brushRef.current) && doesExist(bufferRef.current) && doesExist(canvasRef.current)) {
const { ctx } = getClearContext(canvasRef); const { ctx } = getClearContext(canvasRef);
@ -73,10 +60,38 @@ export function MaskCanvas(props: MaskCanvasProps) {
} }
} }
function drawBrush(point: Point): void {
const { ctx } = getClearContext(brushRef);
ctx.fillStyle = grayToRGB(brush.color, brush.strength);
drawCircle(ctx, {
x: point.x,
y: point.y,
}, brush.size);
drawBuffer();
}
function drawClicks(): void {
if (clicks.length > 0) {
const { ctx } = getContext(bufferRef);
ctx.fillStyle = grayToRGB(brush.color, brush.strength);
for (const click of clicks) {
drawCircle(ctx, click, brush.size);
}
clicks.length = 0;
drawBuffer();
}
}
function drawSource(file: Blob): void { function drawSource(file: Blob): void {
const image = new Image(); const image = new Image();
image.onload = () => { image.onload = () => {
const { ctx } = getContext(bufferRef); const { ctx } = getClearContext(bufferRef);
ctx.globalAlpha = 1.0;
ctx.drawImage(image, 0, 0); ctx.drawImage(image, 0, 0);
URL.revokeObjectURL(src); URL.revokeObjectURL(src);
@ -90,12 +105,25 @@ export function MaskCanvas(props: MaskCanvasProps) {
function finishPainting() { function finishPainting() {
if (doesExist(brushRef.current)) { if (doesExist(brushRef.current)) {
getClearContext(brushRef); getClearContext(brushRef);
drawBuffer();
} }
drawClicks();
if (maskState.current === MASK_STATE.painting) { if (maskState.current === MASK_STATE.painting) {
maskState.current = MASK_STATE.dirty; maskState.current = MASK_STATE.dirty;
save(); }
}
function saveMask(): void {
if (doesExist(bufferRef.current)) {
if (maskState.current === MASK_STATE.clean) {
return;
}
bufferRef.current.toBlob((blob) => {
maskState.current = MASK_STATE.clean;
props.onSave(mustExist(blob));
});
} }
} }
@ -118,21 +146,6 @@ export function MaskCanvas(props: MaskCanvasProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setBrush = useStore(state, (s) => s.setBrush); const setBrush = useStore(state, (s) => s.setBrush);
useEffect(() => {
// including clicks.length prevents the initial render from saving a blank canvas
if (doesExist(bufferRef.current) && maskState.current === MASK_STATE.painting && clicks.length > 0) {
const { ctx } = getContext(bufferRef);
ctx.fillStyle = grayToRGB(brush.color, brush.strength);
for (const click of clicks) {
drawCircle(ctx, click, brush.size);
}
clicks.length = 0;
drawBuffer();
}
}, [clicks.length]);
useEffect(() => { useEffect(() => {
if (maskState.current === MASK_STATE.dirty) { if (maskState.current === MASK_STATE.dirty) {
save(); save();
@ -155,6 +168,9 @@ export function MaskCanvas(props: MaskCanvasProps) {
} }
}, [source]); }, [source]);
// last resort to draw lost clicks
drawClicks();
const styles: React.CSSProperties = { const styles: React.CSSProperties = {
border: '1px solid black', border: '1px solid black',
maxHeight: params.height.default, maxHeight: params.height.default,
@ -191,16 +207,13 @@ export function MaskCanvas(props: MaskCanvasProps) {
const canvas = mustExist(canvasRef.current); const canvas = mustExist(canvasRef.current);
const bounds = canvas.getBoundingClientRect(); const bounds = canvas.getBoundingClientRect();
const { ctx } = getContext(bufferRef); setClicks([...clicks, {
ctx.fillStyle = grayToRGB(brush.color, brush.strength);
drawCircle(ctx, {
x: event.clientX - bounds.left, x: event.clientX - bounds.left,
y: event.clientY - bounds.top, y: event.clientY - bounds.top,
}, brush.size); }]);
drawClicks();
maskState.current = MASK_STATE.dirty; maskState.current = MASK_STATE.dirty;
save();
}} }}
onMouseDown={() => { onMouseDown={() => {
maskState.current = MASK_STATE.painting; maskState.current = MASK_STATE.painting;
@ -218,15 +231,10 @@ export function MaskCanvas(props: MaskCanvasProps) {
y: event.clientY - bounds.top, y: event.clientY - bounds.top,
}]); }]);
} else { } else {
const { ctx } = getClearContext(brushRef); drawBrush({
ctx.fillStyle = grayToRGB(brush.color, brush.strength);
drawCircle(ctx, {
x: event.clientX - bounds.left, x: event.clientX - bounds.left,
y: event.clientY - bounds.top, y: event.clientY - bounds.top,
}, brush.size); });
drawBuffer();
} }
}} }}
/> />
@ -272,7 +280,7 @@ export function MaskCanvas(props: MaskCanvasProps) {
onClick={() => { onClick={() => {
floodCanvas(bufferRef, floodBelow); floodCanvas(bufferRef, floodBelow);
drawBuffer(); drawBuffer();
save(); maskState.current = MASK_STATE.dirty;
}}> }}>
Gray to black Gray to black
</Button> </Button>
@ -282,7 +290,7 @@ export function MaskCanvas(props: MaskCanvasProps) {
onClick={() => { onClick={() => {
floodCanvas(bufferRef, floodBlack); floodCanvas(bufferRef, floodBlack);
drawBuffer(); drawBuffer();
save(); maskState.current = MASK_STATE.dirty;
}}> }}>
Fill with black Fill with black
</Button> </Button>
@ -292,7 +300,7 @@ export function MaskCanvas(props: MaskCanvasProps) {
onClick={() => { onClick={() => {
floodCanvas(bufferRef, floodWhite); floodCanvas(bufferRef, floodWhite);
drawBuffer(); drawBuffer();
save(); maskState.current = MASK_STATE.dirty;
}}> }}>
Fill with white Fill with white
</Button> </Button>
@ -302,7 +310,7 @@ export function MaskCanvas(props: MaskCanvasProps) {
onClick={() => { onClick={() => {
floodCanvas(bufferRef, floodAbove); floodCanvas(bufferRef, floodAbove);
drawBuffer(); drawBuffer();
save(); maskState.current = MASK_STATE.dirty;
}}> }}>
Gray to white Gray to white
</Button> </Button>