aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/imageEditor/ImageEditor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/imageEditor/ImageEditor.tsx')
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx265
1 files changed, 143 insertions, 122 deletions
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index d9f46876e..980f3e566 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -4,7 +4,7 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable react/function-component-definition */
import { Checkbox, FormControlLabel, Slider, TextField } from '@mui/material';
-import { IconButton } from 'browndash-components';
+import { Button, IconButton, Type } from 'browndash-components';
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
import { CgClose } from 'react-icons/cg';
@@ -85,7 +85,6 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
// constants for image cutting
const cutPts = useRef<Point[]>([]);
-
/**
*
* @param type The new tool type we are changing to
@@ -356,7 +355,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
setLoading(false);
};
- const cutImage = async () => {
+ const cutImage = async (currCutType: BrushMode, brushWidth: number, prevEdits: { url: string; saveRes: Doc | undefined }[]) => {
const img = currImg.current;
const canvas = canvasRef.current;
if (!canvas || !img) return;
@@ -381,21 +380,22 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
maxX = Math.max(cutPts.current[i].x, maxX);
maxY = Math.max(cutPts.current[i].y, maxY);
}
- ctx.closePath();
- switch (cutType) { // may need to move this before the drawing at all for the line cases
+ switch (currCutType) {
case BrushMode.IN:
+ ctx.closePath();
ctx.globalCompositeOperation = 'destination-in';
ctx.fill();
break;
case BrushMode.OUT:
+ ctx.closePath();
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();
break;
- case BrushMode.LINE_OUT:
- ctx.globalCompositeOperation = 'destination-out';
- break;
case BrushMode.LINE_IN:
ctx.globalCompositeOperation = 'destination-in';
+ ctx.lineWidth = brushWidth + 20;
+ ctx.stroke();
+ break;
}
}
@@ -423,31 +423,41 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
const image = new Image();
image.src = url;
image.onload = async () => {
- const croppedCanvas = document.createElement('canvas');
- const croppedCtx = croppedCanvas.getContext('2d');
- if (!croppedCtx) return;
- croppedCanvas.width = maxX - minX;
- croppedCanvas.height = maxY - minY;
- croppedCtx.globalCompositeOperation = 'source-over';
- croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
- croppedCtx.drawImage(image, -minX, -minY);
- const croppedURL = croppedCanvas.toDataURL();
- const croppedImage = new Image();
- croppedImage.src = croppedURL;
-
- currImg.current = croppedImage;
- const newImgDoc = await createNewImgDoc(croppedImage, isFirstDoc.current);
- if (isFirstDoc.current) isFirstDoc.current = false;
+ let finalImg: HTMLImageElement | undefined = image;
+ let finalImgURL: string = url;
+ if (currCutType == BrushMode.IN) {
+ const croppedData = cropImage(image, minX, maxY, minY, maxY);
+ finalImg = croppedData;
+ finalImgURL = croppedData.src;
+ }
+ currImg.current = finalImg;
+ const newImgDoc = await createNewImgDoc(finalImg, isFirstDoc.current);
if (newImgDoc) {
const docData = newImgDoc[DocData];
docData.backgroundColor = 'transparent';
+ if (isFirstDoc.current) isFirstDoc.current = false;
+ setEdits([...prevEdits, { url: finalImgURL, saveRes: undefined }]);
}
- setEdits(prevEdits => [...prevEdits, { url: croppedURL, saveRes: undefined }]);
setLoading(false);
cutPts.current.length = 0;
};
};
+ const cropImage = (image: HTMLImageElement, minX: number, maxX: number, minY: number, maxY: number) => {
+ const croppedCanvas = document.createElement('canvas');
+ const croppedCtx = croppedCanvas.getContext('2d');
+ if (!croppedCtx) return image;
+ croppedCanvas.width = maxX - minX;
+ croppedCanvas.height = maxY - minY;
+ croppedCtx.globalCompositeOperation = 'source-over';
+ croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
+ croppedCtx.drawImage(image, -minX, -minY);
+ const croppedURL = croppedCanvas.toDataURL();
+ const croppedImage = new Image();
+ croppedImage.src = croppedURL;
+ return croppedImage;
+ };
+
// adjusts all the img positions to be aligned
const adjustImgPositions = () => {
if (!parentDoc.current) return;
@@ -577,7 +587,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
labelPlacement="end"
sx={{ whiteSpace: 'nowrap' }}
/>
- <ApplyFuncButtons onClick={currTool.applyFunc} loading={loading} onReset={handleReset} btnText={currTool.btnText} />
+ <ApplyFuncButtons onClick={() => currTool.applyFunc(cutType, cursorData.width, edits)} loading={loading} onReset={handleReset} btnText={currTool.btnText} />
<IconButton color={activeColor} tooltip="close" icon={<CgClose size="16px" />} onClick={handleViewClose} />
</div>
</div>
@@ -587,109 +597,120 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
// the side icons including tool type, the slider, and undo/redo
function renderSideIcons() {
return (
- <div className="iconContainer">
- <div className="imageToolsContainer">
- {imageEditTools.map(tool => {
- return ImageToolButton(tool, tool.type === currTool.type, changeTool);
- })}
- </div>
- {currTool.type == ImageToolType.Cut &&
- <div className="cutToolContainer">
- <Button
- text="Keep in"
- type={Type.TERT}
- color={cutType == BrushMode.IN ? SettingsManager.userVariantColor : bgColor}
- onClick={() => {
- setCutType(BrushMode.IN);
- }}/>
- <Button
- text="Keep out"
- type={Type.TERT}
- color={cutType == BrushMode.OUT ? SettingsManager.userVariantColor : bgColor}
- onClick={() => {
- setCutType(BrushMode.OUT);
- }}/>
- <Button
- text="Draw in"
- type={Type.TERT}
- color={cutType == BrushMode.LINE_IN ? SettingsManager.userVariantColor : bgColor}
- onClick={() => {
- setCutType(BrushMode.LINE_IN);
- }}/>
- <Button
- text="Erase"
- type={Type.TERT}
- color={cutType == BrushMode.LINE_OUT ? SettingsManager.userVariantColor : bgColor}
- onClick={() => {
- setCutType(BrushMode.LINE_OUT);
- }}/>
- </div>}
- <div className="sliderContainer" onPointerDown={e => e.stopPropagation()}>
- {currTool.type === ImageToolType.GenerativeFill && (
- <Slider
- sx={{
- '& input[type="range"]': {
- WebkitAppearance: 'slider-vertical',
- },
+ <div className="sideControlsContainer" style={{ backgroundColor: bgColor }}>
+ <div className="sideControls">
+ <div className="imageToolsContainer">
+ {imageEditTools.map(tool => {
+ return ImageToolButton(tool, tool.type === currTool.type, changeTool);
+ })}
+ </div>
+ {currTool.type == ImageToolType.Cut && (
+ <div className="cutToolsContainer">
+ <Button
+ style={{ width: '100%' }}
+ text="Keep in"
+ type={Type.TERT}
+ color={cutType == BrushMode.IN ? SettingsManager.userColor : bgColor}
+ onClick={() => {
+ setCutType(BrushMode.IN);
+ }}
+ />
+ <Button
+ style={{ width: '100%' }}
+ text="Keep out"
+ type={Type.TERT}
+ color={cutType == BrushMode.OUT ? SettingsManager.userColor : bgColor}
+ onClick={() => {
+ setCutType(BrushMode.OUT);
+ }}
+ />
+ <Button
+ style={{ width: '100%' }}
+ text="Draw in"
+ type={Type.TERT}
+ color={cutType == BrushMode.LINE_IN ? SettingsManager.userColor : bgColor}
+ onClick={() => {
+ setCutType(BrushMode.LINE_IN);
+ }}
+ />
+ <Button
+ style={{ width: '100%' }}
+ text="Erase"
+ type={Type.TERT}
+ color={cutType == BrushMode.LINE_OUT ? SettingsManager.userColor : bgColor}
+ onClick={() => {
+ setCutType(BrushMode.LINE_OUT);
+ }}
+ />
+ </div>
+ )}
+ <div className="sliderContainer" onPointerDown={e => e.stopPropagation()}>
+ {currTool.type === ImageToolType.GenerativeFill && (
+ <Slider
+ sx={{
+ '& input[type="range"]': {
+ WebkitAppearance: 'slider-vertical',
+ },
+ }}
+ orientation="vertical"
+ min={genFillTool.sliderMin}
+ max={genFillTool.sliderMax}
+ defaultValue={genFillTool.sliderDefault}
+ size="small"
+ valueLabelDisplay="auto"
+ onChange={(e, val) => {
+ setCursorData(prev => ({ ...prev, width: val as number }));
+ }}
+ />
+ )}
+ {currTool.type === ImageToolType.Cut && (
+ <Slider
+ sx={{
+ '& input[type="range"]': {
+ WebkitAppearance: 'slider-vertical',
+ },
+ }}
+ orientation="vertical"
+ min={cutTool.sliderMin}
+ max={cutTool.sliderMax}
+ defaultValue={cutTool.sliderDefault}
+ size="small"
+ valueLabelDisplay="auto"
+ onChange={(e, val) => {
+ setCursorData(prev => ({ ...prev, width: val as number }));
+ }}
+ />
+ )}
+ </div>
+ {/* Undo and Redo */}
+ <div className="undoRedoContainer">
+ <IconButton
+ style={{ cursor: 'pointer' }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ handleUndo();
}}
- orientation="vertical"
- min={genFillTool.sliderMin}
- max={genFillTool.sliderMax}
- defaultValue={genFillTool.sliderDefault}
- size="small"
- valueLabelDisplay="auto"
- onChange={(e, val) => {
- setCursorData(prev => ({ ...prev, width: val as number }));
+ onPointerUp={e => {
+ e.stopPropagation();
}}
+ color={activeColor}
+ tooltip="Undo"
+ icon={<IoMdUndo />}
/>
- )}
- {currTool.type === ImageToolType.Cut && (
- <Slider
- sx={{
- '& input[type="range"]': {
- WebkitAppearance: 'slider-vertical',
- },
+ <IconButton
+ style={{ cursor: 'pointer' }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ handleRedo();
}}
- orientation="vertical"
- min={cutTool.sliderMin}
- max={cutTool.sliderMax}
- defaultValue={cutTool.sliderDefault}
- size="small"
- valueLabelDisplay="auto"
- onChange={(e, val) => {
- setCursorData(prev => ({ ...prev, width: val as number }));
+ onPointerUp={e => {
+ e.stopPropagation();
}}
+ color={activeColor}
+ tooltip="Redo"
+ icon={<IoMdRedo />}
/>
- )}
- </div>
- {/* Undo and Redo */}
- <div className="undoRedoContainer">
- <IconButton
- style={{ cursor: 'pointer' }}
- onPointerDown={e => {
- e.stopPropagation();
- handleUndo();
- }}
- onPointerUp={e => {
- e.stopPropagation();
- }}
- color={activeColor}
- tooltip="Undo"
- icon={<IoMdUndo />}
- />
- <IconButton
- style={{ cursor: 'pointer' }}
- onPointerDown={e => {
- e.stopPropagation();
- handleRedo();
- }}
- onPointerUp={e => {
- e.stopPropagation();
- }}
- color={activeColor}
- tooltip="Redo"
- icon={<IoMdRedo />}
- />
+ </div>
</div>
</div>
);