Browse Source

Merge pull request #1273 from Francespo/flood-fill-default-behaviour

Flood fill default behaviour
Krzysztof Krysiński 1 tuần trước cách đây
mục cha
commit
010ea53ed9

+ 19 - 3
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs

@@ -10,6 +10,8 @@ using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Enums;
+using BlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing.FloodFill;
 
@@ -47,7 +49,7 @@ public static class FloodFillHelper
         VecI startingPos,
         Color drawingColor,
         float tolerance,
-        int frame, bool lockTransparency)
+        int frame, bool lockTransparency, FloodFillMode fillMode)
     {
         if (selection is not null && !selection.Contains(startingPos.X + 0.5f, startingPos.Y + 0.5f))
             return new();
@@ -84,7 +86,7 @@ public static class FloodFillHelper
             colorSpaceCorrectedColor = fixedColor;
         }
 
-        if ((colorSpaceCorrectedColor.A == 0) || colorToReplace == colorSpaceCorrectedColor)
+        if ((colorSpaceCorrectedColor.A == 0 && fillMode == FloodFillMode.Overlay) || (colorToReplace == colorSpaceCorrectedColor && fillMode == FloodFillMode.Replace))
             return new();
 
         if (colorToReplace.A == 0 && lockTransparency)
@@ -113,7 +115,21 @@ public static class FloodFillHelper
             if (!drawingChunks.ContainsKey(chunkPos))
             {
                 var chunk = Chunk.Create(document.ProcessingColorSpace);
-                chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
+
+                if (fillMode == FloodFillMode.Replace)
+                {
+                    // For replace mode, copy original image data to avoid erasing unfilled pixels
+                    var originalChunk = cache.GetChunk(chunkPos);
+                    originalChunk.Switch(
+                        (Chunk origChunk) => chunk.Surface.DrawingSurface.Canvas.DrawSurface(origChunk.Surface.DrawingSurface, 0, 0),
+                        (EmptyChunk _) => chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent)
+                    );
+                }
+                else
+                {
+                    // For overlay mode, start with transparent
+                    chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
+                }
 
                 drawingChunks[chunkPos] = chunk;
             }

+ 16 - 6
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFill_Change.cs

@@ -1,8 +1,11 @@
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Enums;
+using BlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing.FloodFill;
 
@@ -16,9 +19,10 @@ internal class FloodFill_Change : Change
     private CommittedChunkStorage? chunkStorage = null;
     private int frame;
     private float tolerance;
+    private FloodFillMode fillMode;
 
     [GenerateMakeChangeAction]
-    public FloodFill_Change(Guid memberGuid, VecI pos, Color color, bool referenceAll, float tolerance, bool drawOnMask, int frame)
+    public FloodFill_Change(Guid memberGuid, VecI pos, Color color, bool referenceAll, float tolerance, FloodFillMode fillMode, bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
         this.pos = pos;
@@ -27,6 +31,7 @@ internal class FloodFill_Change : Change
         this.drawOnMask = drawOnMask;
         this.frame = frame;
         this.tolerance = tolerance;
+        this.fillMode = fillMode;
     }
 
     public override bool InitializeAndValidate(Document target)
@@ -48,17 +53,22 @@ internal class FloodFill_Change : Change
         else
             membersToReference.Add(memberGuid);
         bool lockTransparency = target.FindMember(memberGuid) is ImageLayerNode { LockTransparency: true };
-        var floodFilledChunks = FloodFillHelper.FloodFill(membersToReference, target, selection, pos, color, tolerance, frame, lockTransparency);
+        var floodFilledChunks = FloodFillHelper.FloodFill(membersToReference, target, selection, pos, color, tolerance, frame, lockTransparency, fillMode);
         if (floodFilledChunks.Count == 0)
         {
             ignoreInUndo = true;
             return new None();
         }
-
-        foreach (var (chunkPos, chunk) in floodFilledChunks)
+        
+        Paint paint = fillMode switch
         {
-            image.EnqueueDrawTexture(chunkPos * ChunkyImage.FullChunkSize, chunk.Surface, null, false);
-        }
+            FloodFillMode.Overlay => null,  // Default blend mode
+            FloodFillMode.Replace => new Paint() { BlendMode = BlendMode.Src }  // Replace mode
+        };
+        
+        foreach (var (chunkPos, chunk) in floodFilledChunks)
+            image.EnqueueDrawTexture(chunkPos * ChunkyImage.FullChunkSize, chunk.Surface, paint, false);
+        
         var affArea = image.FindAffectedArea();
         chunkStorage = new CommittedChunkStorage(image, affArea.Chunks);
         image.CommitChanges();

+ 11 - 0
src/PixiEditor.ChangeableDocument/Enums/FloodFillMode.cs

@@ -0,0 +1,11 @@
+using System.ComponentModel;
+
+namespace PixiEditor.ChangeableDocument.Enums;
+
+public enum FloodFillMode
+{
+    [Description("OVERLAY")]
+    Overlay,
+    [Description("REPLACE")]
+    Replace,
+}

+ 8 - 6
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -41,16 +41,17 @@
         },
         "Select",
         {
-            "ToolName": "MagicWand",
-            "Settings": {
-              "Spacing": 0
-            }
+          "ToolName": "MagicWand",
+          "Settings": {
+            "Spacing": 0
+          }
         },
         "Lasso",
         {
           "ToolName": "FloodFill",
           "Settings": {
-            "Tolerance": 0
+            "Tolerance": 0,
+            "FillMode": "Overlay"
           }
         },
         "RasterLine",
@@ -111,7 +112,8 @@
         {
           "ToolName": "FloodFill",
           "Settings": {
-            "ExposeTolerance": true
+            "ExposeTolerance": true,
+            "ExposeFillMode": true
           }
         },
         {

+ 2 - 0
src/PixiEditor/Data/Localization/Languages/en.json

@@ -321,6 +321,8 @@
   "ELLIPSE_TOOL": "Ellipse",
   "ERASER_TOOL": "Eraser",
   "FLOOD_FILL_TOOL": "Flood Fill",
+  "FLOOD_FILL_MODE_LABEL" : "Fill mode",
+  "OVERLAY" : "Overlay",
   "LASSO_TOOL": "Lasso",
   "LINE_TOOL": "Line",
   "MAGIC_WAND_TOOL": "Magic Wand",

+ 6 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/FloodFillToolExecutor.cs

@@ -7,6 +7,7 @@ using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.Models.Controllers.InputDevice;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
@@ -18,6 +19,7 @@ internal class FloodFillToolExecutor : UpdateableChangeExecutor
     private Guid memberGuid;
     private Color color;
     private float tolerance;
+    private FloodFillMode fillMode;
 
     public override ExecutionState Start()
     {
@@ -40,15 +42,16 @@ internal class FloodFillToolExecutor : UpdateableChangeExecutor
         color = colorsVM.PrimaryColor;
         var pos = controller!.LastPixelPosition;
         tolerance = fillTool.Tolerance;
-
-        internals!.ActionAccumulator.AddActions(new FloodFill_Action(memberGuid, pos, color, considerAllLayers, tolerance, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        fillMode = fillTool.FillMode;
+        
+        internals!.ActionAccumulator.AddActions(new FloodFill_Action(memberGuid, pos, color, considerAllLayers, tolerance, fillMode, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
 
         return ExecutionState.Success;
     }
 
     public override void OnPixelPositionChange(VecI pos, MouseOnCanvasEventArgs args)
     {
-        internals!.ActionAccumulator.AddActions(new FloodFill_Action(memberGuid, pos, color, considerAllLayers, tolerance, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        internals!.ActionAccumulator.AddActions(new FloodFill_Action(memberGuid, pos, color, considerAllLayers, tolerance, fillMode, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
     }
 
     public override void OnLeftMouseButtonUp(VecD argsPositionOnCanvas)

+ 4 - 1
src/PixiEditor/Models/Handlers/Tools/IFloodFillToolHandler.cs

@@ -1,7 +1,10 @@
-namespace PixiEditor.Models.Handlers.Tools;
+using PixiEditor.ChangeableDocument.Enums;
+
+namespace PixiEditor.Models.Handlers.Tools;
 
 internal interface IFloodFillToolHandler : IToolHandler
 {
     public bool ConsiderAllLayers { get; }
     public float Tolerance { get; }
+    FloodFillMode FillMode { get; }
 }

+ 3 - 0
src/PixiEditor/ViewModels/Tools/Tools/FloodFillToolViewModel.cs

@@ -6,6 +6,7 @@ using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.UI.Common.Fonts;
 using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
@@ -33,6 +34,8 @@ internal class FloodFillToolViewModel : ToolViewModel, IFloodFillToolHandler
 
     [Settings.Percent("TOLERANCE_LABEL", ExposedByDefault = false)]
     public float Tolerance => GetValue<float>();
+    [Settings.Enum("FLOOD_FILL_MODE_LABEL", FloodFillMode.Overlay, ExposedByDefault = false)]
+    public FloodFillMode FillMode => GetValue<FloodFillMode>();
 
     public override string DefaultIcon => PixiPerfectIcons.Bucket;