From 5753d477fa2f93ec42861fae79444f71d07367c9 Mon Sep 17 00:00:00 2001 From: Nick Skriabin Date: Fri, 20 Jun 2025 04:02:37 +0100 Subject: [PATCH 1/5] Allow bitmask eraser to erase single pixels --- web/libs/editor/src/tools/BitmaskErase.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/libs/editor/src/tools/BitmaskErase.jsx b/web/libs/editor/src/tools/BitmaskErase.jsx index 6541be786101..5a9504a1b353 100644 --- a/web/libs/editor/src/tools/BitmaskErase.jsx +++ b/web/libs/editor/src/tools/BitmaskErase.jsx @@ -108,7 +108,7 @@ const _Tool = types }, addPoint(x, y) { - brush.addPoint(Math.floor(x), Math.floor(y), self.strokeWidth, { erase: true }); + brush.addPoint(x, y, self.strokeWidth, { erase: true }); }, setStroke(val) { From 0362abe84692cca5f942a5048839c199d1611de8 Mon Sep 17 00:00:00 2001 From: Nick Skriabin Date: Fri, 20 Jun 2025 04:13:14 +0100 Subject: [PATCH 2/5] Improve cursor visuals --- .../src/components/ImageView/ImageView.jsx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/web/libs/editor/src/components/ImageView/ImageView.jsx b/web/libs/editor/src/components/ImageView/ImageView.jsx index 53676809256c..fffee05364dc 100644 --- a/web/libs/editor/src/components/ImageView/ImageView.jsx +++ b/web/libs/editor/src/components/ImageView/ImageView.jsx @@ -1335,14 +1335,37 @@ const CursorLayer = observer(({ item, tool }) => { }, [item.stageRef]); const size = useMemo(() => { - return (item.imageIsSmallerThanStage ? tool.strokeWidth : tool.strokeWidth / item.stageToImageRatio) + 2; + return item.imageIsSmallerThanStage ? tool.strokeWidth : tool.strokeWidth / item.stageToImageRatio; }, [tool.strokeWidth, item.imageIsSmallerThanStage, item.stageToImageRatio]); return visible ? ( - - - - + tool.strokeWidth <= 2 ? ( + + + + + ) : ( + + + + + ) ) : null; }); From a216bacdc8cb46f9d32e9e9bad483c82b5f56515 Mon Sep 17 00:00:00 2001 From: Nick Skriabin Date: Fri, 20 Jun 2025 04:21:11 +0100 Subject: [PATCH 3/5] Cleanup --- .../src/components/ImageView/ImageView.jsx | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/web/libs/editor/src/components/ImageView/ImageView.jsx b/web/libs/editor/src/components/ImageView/ImageView.jsx index fffee05364dc..47a3149b7d93 100644 --- a/web/libs/editor/src/components/ImageView/ImageView.jsx +++ b/web/libs/editor/src/components/ImageView/ImageView.jsx @@ -1339,33 +1339,35 @@ const CursorLayer = observer(({ item, tool }) => { }, [tool.strokeWidth, item.imageIsSmallerThanStage, item.stageToImageRatio]); return visible ? ( - tool.strokeWidth <= 2 ? ( - - - - - ) : ( - - - - - ) + + {tool.strokeWidth <= 2 ? ( + <> + + + + ) : ( + <> + + + + )} + ) : null; }); From ef86b5ddcbd64e146f3d4e9618e43920fa5efae3 Mon Sep 17 00:00:00 2001 From: Nick Skriabin Date: Fri, 20 Jun 2025 10:19:03 +0100 Subject: [PATCH 4/5] Fix cursor: none --- web/libs/editor/src/components/ImageView/ImageView.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/libs/editor/src/components/ImageView/ImageView.jsx b/web/libs/editor/src/components/ImageView/ImageView.jsx index 47a3149b7d93..a2250b213c67 100644 --- a/web/libs/editor/src/components/ImageView/ImageView.jsx +++ b/web/libs/editor/src/components/ImageView/ImageView.jsx @@ -1247,7 +1247,6 @@ const EntireStage = observer( item.setStageRef(ref); }} className={[styles["image-element"], ...imagePositionClassnames].join(" ")} - style={{ cursor: "none" }} width={size.width} height={size.height} scaleX={item.zoomScale} From 2727797c6c3fae7ad136b8629a4093043b2105c9 Mon Sep 17 00:00:00 2001 From: Nick Skriabin Date: Fri, 20 Jun 2025 12:48:28 +0100 Subject: [PATCH 5/5] Fixing zoom and scale --- .../src/components/ImageView/ImageView.jsx | 12 +++---- web/libs/editor/src/regions/BitmaskRegion.jsx | 35 +++---------------- .../editor/src/tags/object/Image/Image.js | 10 ------ 3 files changed, 11 insertions(+), 46 deletions(-) diff --git a/web/libs/editor/src/components/ImageView/ImageView.jsx b/web/libs/editor/src/components/ImageView/ImageView.jsx index a2250b213c67..39eab22e03af 100644 --- a/web/libs/editor/src/components/ImageView/ImageView.jsx +++ b/web/libs/editor/src/components/ImageView/ImageView.jsx @@ -464,7 +464,7 @@ const PixelGridLayer = observer(({ item }) => { const { stageWidth, stageHeight } = item; const imageSmallerThanStage = naturalWidth < stageWidth || naturalHeight < stageHeight; - const step = 1 / (item.imageIsSmallerThanStage ? 1 : item.stageToImageRatio); // image pixel + const step = item.stageZoom; // image pixel const { verticalPoints, horizontalPoints } = useMemo(() => { const vPts = []; @@ -1288,14 +1288,14 @@ const ImageLayer = observer(({ item }) => { const { width, height } = useMemo(() => { return { - width: Math.min(imageEntity.naturalWidth, item.stageWidth), - height: Math.min(imageEntity.naturalHeight, item.stageHeight), + width: imageEntity.naturalWidth, + height: imageEntity.naturalHeight, }; }, [imageEntity.naturalWidth, imageEntity.naturalHeight, item.stageWidth, item.stageHeight]); return image ? ( <> - + @@ -1334,8 +1334,8 @@ const CursorLayer = observer(({ item, tool }) => { }, [item.stageRef]); const size = useMemo(() => { - return item.imageIsSmallerThanStage ? tool.strokeWidth : tool.strokeWidth / item.stageToImageRatio; - }, [tool.strokeWidth, item.imageIsSmallerThanStage, item.stageToImageRatio]); + return tool.strokeWidth * item.stageZoom; + }, [tool.strokeWidth, item.stageZoom]); return visible ? ( diff --git a/web/libs/editor/src/regions/BitmaskRegion.jsx b/web/libs/editor/src/regions/BitmaskRegion.jsx index 7227211f491d..1330755a7634 100644 --- a/web/libs/editor/src/regions/BitmaskRegion.jsx +++ b/web/libs/editor/src/regions/BitmaskRegion.jsx @@ -86,10 +86,6 @@ const Model = types } }, - get scale() { - return self.parent?.imageIsSmallerThanStage ? 1 : self.drawingOffset.scale; - }, - /** * Brushes are processed in pixels, so percentages are derived values for them, * unlike for other tools. @@ -118,21 +114,6 @@ const Model = types }; }, - get drawingOffset() { - const { dimensions } = self; - - const scale = Math.min( - dimensions.stageWidth / dimensions.imageWidth, - dimensions.stageHeight / dimensions.imageHeight, - ); - - return { - offsetX: dimensions.imageWidth - dimensions.stageWidth / scale, - offsetY: dimensions.imageHeight - dimensions.stageHeight / scale, - scale, - }; - }, - getImageDataURL() { const canvas = self.getImageSnapshotCanvas(); const imageDataURL = canvas.toDataURL("image/png"); @@ -243,11 +224,10 @@ const Model = types canvasSize() { if (!self.parent) return { width: 0, height: 0 }; const ent = self.parent.currentImageEntity; - const scale = self.parent.imageIsSmallerThanStage ? 1 : self.parent.stageToImageRatio; return { - width: ent.naturalWidth / scale, - height: ent.naturalHeight / scale, + width: ent.naturalWidth * self.parent.stageZoom, + height: ent.naturalHeight * self.parent.stageZoom, }; }, @@ -291,7 +271,7 @@ const Model = types }, updateBBox() { - self.setBBox(getCanvasPixelBounds(self.offscreenCanvas, self.scale)); + self.setBBox(getCanvasPixelBounds(self.offscreenCanvas, self.parent?.stageZoom ?? 1)); }, /** @@ -315,7 +295,6 @@ const Model = types beginPath({ type, strokeWidth, opacity = self.opacity, x = 0, y = 0 }) { self.object.annotation.pauseAutosave(); - const { drawingOffset: offset } = self; const ctx = self.offscreenCanvas.getContext("2d"); // Set up context properties once @@ -356,13 +335,9 @@ const Model = types }, positionToStage(x, y) { - const { drawingOffset: offset } = self; - const smaller = self.parent.imageIsSmallerThanStage; - const ratio = smaller ? 1 : offset.scale; - return { - x: Math.floor(x / ratio + offset.offsetX), - y: Math.floor(y / ratio + offset.offsetY), + x: Math.floor(x / self.parent.stageZoom), + y: Math.floor(y / self.parent.stageZoom), }; }, diff --git a/web/libs/editor/src/tags/object/Image/Image.js b/web/libs/editor/src/tags/object/Image/Image.js index 971452941a91..31266c81027a 100644 --- a/web/libs/editor/src/tags/object/Image/Image.js +++ b/web/libs/editor/src/tags/object/Image/Image.js @@ -284,16 +284,6 @@ const Model = types }[self.rotation]; }, - get stageToImageRatio() { - const ent = self.currentImageEntity; - return Math.min(ent.naturalWidth / self.stageWidth, ent.naturalHeight / self.stageHeight); - }, - - get imageIsSmallerThanStage() { - const ent = self.currentImageEntity; - return ent.naturalWidth < self.stageWidth || ent.naturalHeight < self.stageHeight; - }, - get stageScale() { return self.zoomScale; },