aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx137
-rw-r--r--src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts18
2 files changed, 82 insertions, 73 deletions
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 5c4c83eef..a742673e0 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -22,11 +22,11 @@ import { ImageEditorData } from '../ImageBox';
import { OpenWhereMod } from '../OpenWhere';
import './ImageEditor.scss';
import { ApplyFuncButtons, ImageToolButton } from './ImageEditorButtons';
-import { BrushHandler, BrushType } from './imageEditorUtils/BrushHandler';
+import { BrushHandler } from './imageEditorUtils/BrushHandler';
import { APISuccess, ImageUtility } from './imageEditorUtils/ImageHandler';
import { PointerHandler } from './imageEditorUtils/PointerHandler';
-import { activeColor, bgColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './imageEditorUtils/imageEditorConstants';
-import { BrushMode, CursorData, ImageDimensions, ImageEditTool, ImageToolType, Point } from './imageEditorUtils/imageEditorInterfaces';
+import { activeColor, bgColor, brushWidthOffset, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './imageEditorUtils/imageEditorConstants';
+import { CutMode, CursorData, ImageDimensions, ImageEditTool, ImageToolType, Point } from './imageEditorUtils/imageEditorInterfaces';
import { DocumentView } from '../DocumentView';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ImageField } from '../../../../fields/URLField';
@@ -64,7 +64,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
width: canvasSize,
height: canvasSize,
});
- const [cutType, setCutType] = useState<BrushMode>(BrushMode.IN);
+ const [cutType, setCutType] = useState<CutMode>(CutMode.IN);
// whether to create a new collection or not
const [isNewCollection, setIsNewCollection] = useState(true);
// the current image in the main canvas
@@ -183,7 +183,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
x: currPoint.x - e.movementX / canvasScale,
y: currPoint.y - e.movementY / canvasScale,
};
- const pts = BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor, BrushType.CUT);
+ const pts = BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor);
cutPts.current.push(...pts);
};
@@ -283,7 +283,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
}));
};
- // Get AI Edit
+ // Get AI Edit for Generative Fill
const getEdit = async () => {
const img = currImg.current;
if (!img) return;
@@ -304,32 +304,14 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
// create first image
if (!newCollectionRef.current) {
- if (!isNewCollection && imageRootDoc) {
- // if the parent hasn't been set yet
- if (!parentDoc.current) parentDoc.current = imageRootDoc;
- } else {
- if (!(originalImg.current && imageRootDoc)) return;
- // create new collection and add it to the view
- newCollectionRef.current = Docs.Create.FreeformDocument([], {
- x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
- y: NumCast(imageRootDoc.y),
- _width: newCollectionSize,
- _height: newCollectionSize,
- title: 'Image edit collection',
- });
- DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' });
-
- // opening new tab
- CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
-
- // add the doc to the main freeform
- // eslint-disable-next-line no-use-before-define
- await createNewImgDoc(originalImg.current, true);
- }
+ createNewCollection();
} else {
childrenDocs.current = [];
}
-
+ if (!(originalImg.current && imageRootDoc)) return;
+ // add the doc to the main freeform
+ // eslint-disable-next-line no-use-before-define
+ await createNewImgDoc(originalImg.current, true);
originalImg.current = currImg.current;
originalDoc.current = parentDoc.current;
const { urls } = res as APISuccess;
@@ -356,23 +338,32 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
setLoading(false);
};
- const cutImage = async (currCutType: BrushMode, brushWidth: number, prevEdits: { url: string; saveRes: Doc | undefined }[], firstDoc: boolean) => {
+ /**
+ * This function performs image cutting based on the inputted BrushMode. There are currently four ways to cut images:
+ * 1. By outlining the area that should be kept (BrushMode.IN)
+ * 2. By outlining the area that should be removed (BrushMode.OUT)
+ * 3. By drawing in the area that should be kept (where the image is brushed, the image will remain and everything else will be removed) (BrushMode.DRAW_IN)
+ * 4. By drawing the area that she be removed, so this operates as an eraser (BrushMode.ERASE)
+ * @param currCutType BrushMode enum that determines what kind of cutting operation to perform
+ * @param firstDoc boolean for whether it's the first edited image. This is for positioning of the edited images when they render on the canvas.
+ */
+ const cutImage = async (currCutType: CutMode, brushWidth: number, prevEdits: { url: string; saveRes: Doc | undefined }[], firstDoc: boolean) => {
const img = currImg.current;
const canvas = canvasRef.current;
if (!canvas || !img) return;
const ctx = ImageUtility.getCanvasContext(canvasRef);
if (!ctx) return;
- setLoading(true);
// get the original image
const canvasOriginalImg = ImageUtility.getCanvasImg(img);
if (!canvasOriginalImg) return;
- // NOTE: cutting two diff shapes can be made possible by having the user press a button to set a new shape!
+ setLoading(true);
const currPts = [...cutPts.current];
- if (currCutType !== BrushMode.LINE_OUT) handleReset(); // gets rid of the visible brush strokes (mostly needed for line_in) unless it's erasing (which depends on the brush strokes)
+ if (currCutType !== CutMode.ERASE) handleReset(); // gets rid of the visible brush strokes (mostly needed for line_in) unless it's erasing (which depends on the brush strokes)
let minX = img.width;
let maxX = 0;
let minY = img.height;
let maxY = 0;
+ // currPts is populated by the brush strokes' points, so this code is drawing a path along the points
if (currPts.length) {
ctx.beginPath();
ctx.moveTo(currPts[0].x, currPts[0].y);
@@ -383,44 +374,30 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
maxX = Math.max(currPts[i].x, maxX);
maxY = Math.max(currPts[i].y, maxY);
}
- switch (currCutType) {
- case BrushMode.IN:
+ switch (
+ currCutType // use different canvas operations depending on the type of cutting we're applying
+ ) {
+ case CutMode.IN:
ctx.closePath();
ctx.globalCompositeOperation = 'destination-in';
ctx.fill();
break;
- case BrushMode.OUT:
+ case CutMode.OUT:
ctx.closePath();
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();
break;
- case BrushMode.LINE_IN:
+ case CutMode.DRAW_IN:
ctx.globalCompositeOperation = 'destination-in';
- ctx.lineWidth = brushWidth + 10; // added offset because width gets cut off a little bit
+ ctx.lineWidth = brushWidth + brushWidthOffset; // added offset because width gets cut off a little bit
ctx.stroke();
break;
}
}
- const url = canvas.toDataURL(); // this does the same thing as convert img to canvasurl
+ const url = canvas.toDataURL();
if (!newCollectionRef.current) {
- if (!isNewCollection && imageRootDoc) {
- // if the parent hasn't been set yet
- if (!parentDoc.current) parentDoc.current = imageRootDoc;
- } else {
- if (!(originalImg.current && imageRootDoc)) return;
- // create new collection and add it to the view
- newCollectionRef.current = Docs.Create.FreeformDocument([], {
- x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
- y: NumCast(imageRootDoc.y),
- _width: newCollectionSize,
- _height: newCollectionSize,
- title: 'Image edit collection',
- });
- DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' });
- // opening new tab
- CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
- }
+ createNewCollection();
}
const image = new Image();
@@ -428,7 +405,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
image.onload = async () => {
let finalImg: HTMLImageElement | undefined = image;
let finalImgURL: string = url;
- if (currCutType == BrushMode.IN || currCutType == BrushMode.LINE_IN) {
+ // crop the image for these brush modes to remove excess blank space around the image contents
+ if (currCutType == CutMode.IN || currCutType == CutMode.DRAW_IN) {
const croppedData = cropImage(image, minX, maxX, minY, maxY);
finalImg = croppedData;
finalImgURL = croppedData.src;
@@ -436,6 +414,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
currImg.current = finalImg;
const newImgDoc = await createNewImgDoc(finalImg, firstDoc);
if (newImgDoc) {
+ // set the image to transparent to remove the background / brushstrokes
const docData = newImgDoc[DocData];
docData.backgroundColor = 'transparent';
docData.disableMixBlend = true;
@@ -447,6 +426,34 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
};
};
+ /**
+ * Creates a new collection to put the image edits on. Adds to a new tab on the right if "Create New Collection" is checked.
+ * @returns
+ */
+ const createNewCollection = () => {
+ if (!isNewCollection && imageRootDoc) {
+ // if the parent hasn't been set yet
+ if (!parentDoc.current) parentDoc.current = imageRootDoc;
+ } else {
+ if (!(originalImg.current && imageRootDoc)) return;
+ // create new collection and add it to the view
+ newCollectionRef.current = Docs.Create.FreeformDocument([], {
+ x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
+ y: NumCast(imageRootDoc.y),
+ _width: newCollectionSize,
+ _height: newCollectionSize,
+ title: 'Image edit collection',
+ });
+ DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' });
+ // opening new tab
+ CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
+ }
+ };
+
+ /**
+ * This function crops an image based on the inputted dimensions. This is used to automatically adjust the images that are
+ * edited to be smaller than the original (i.e. for cutting into a small part of the image)
+ */
const cropImage = (image: HTMLImageElement, minX: number, maxX: number, minY: number, maxY: number) => {
const croppedCanvas = document.createElement('canvas');
const croppedCtx = croppedCanvas.getContext('2d');
@@ -617,36 +624,36 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
style={{ width: '100%' }}
text="Keep in"
type={Type.TERT}
- color={cutType == BrushMode.IN ? SettingsManager.userColor : bgColor}
+ color={cutType == CutMode.IN ? SettingsManager.userColor : bgColor}
onClick={() => {
- setCutType(BrushMode.IN);
+ setCutType(CutMode.IN);
}}
/>
<Button
style={{ width: '100%' }}
text="Keep out"
type={Type.TERT}
- color={cutType == BrushMode.OUT ? SettingsManager.userColor : bgColor}
+ color={cutType == CutMode.OUT ? SettingsManager.userColor : bgColor}
onClick={() => {
- setCutType(BrushMode.OUT);
+ setCutType(CutMode.OUT);
}}
/>
<Button
style={{ width: '100%' }}
text="Draw in"
type={Type.TERT}
- color={cutType == BrushMode.LINE_IN ? SettingsManager.userColor : bgColor}
+ color={cutType == CutMode.DRAW_IN ? SettingsManager.userColor : bgColor}
onClick={() => {
- setCutType(BrushMode.LINE_IN);
+ setCutType(CutMode.DRAW_IN);
}}
/>
<Button
style={{ width: '100%' }}
text="Erase"
type={Type.TERT}
- color={cutType == BrushMode.LINE_OUT ? SettingsManager.userColor : bgColor}
+ color={cutType == CutMode.ERASE ? SettingsManager.userColor : bgColor}
onClick={() => {
- setCutType(BrushMode.LINE_OUT);
+ setCutType(CutMode.ERASE);
}}
/>
</div>
diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
index 75659fc53..a14b55439 100644
--- a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
+++ b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts
@@ -17,24 +17,26 @@ export enum ImageToolType {
Cut = 'cut',
}
+export enum CutMode {
+ IN,
+ OUT,
+ DRAW_IN,
+ ERASE,
+}
+
export interface ImageEditTool {
type: ImageToolType;
name: string;
btnText: string;
icon: IconProp;
- applyFunc: (currCutType: BrushMode, brushWidth: number, prevEdits: { url: string; saveRes: Doc | undefined }[], isFirstDoc: boolean) => Promise<void>;
+ // this is the function that the image tool applies, so it can be defined depending on the tool
+ applyFunc: (currCutType: CutMode, brushWidth: number, prevEdits: { url: string; saveRes: Doc | undefined }[], isFirstDoc: boolean) => Promise<void>;
+ // these optional parameters are here because different tools require different brush sizes and defaults
sliderMin?: number;
sliderMax?: number;
sliderDefault?: number;
}
-export enum BrushMode {
- IN,
- OUT,
- LINE_IN,
- LINE_OUT,
-}
-
export interface ImageDimensions {
width: number;
height: number;