flabbet 4 years ago
parent
commit
964cb5b48c

+ 25 - 0
PixiEditor/Models/Tools/FloodFillRange.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.Tools
+{
+    /// <summary>
+    /// Represents a linear range to be filled and branched from.
+    /// </summary>
+    public struct FloodFillRange
+    {
+        public int StartX;
+        public int EndX;
+        public int Y;
+
+        public FloodFillRange(int startX, int endX, int y)
+        {
+            StartX = startX;
+            EndX = endX;
+            Y = y;
+        }
+    }
+}

+ 81 - 42
PixiEditor/Models/Tools/Tools/FloodFill.cs

@@ -9,7 +9,8 @@ namespace PixiEditor.Models.Tools.Tools
 {
     public class FloodFill : BitmapOperationTool
     {
-        private BitmapManager BitmapManager { get; }
+        private BitmapManager BitmapManager { get; }
+
 
         public FloodFill(BitmapManager bitmapManager)
         {
@@ -21,60 +22,98 @@ namespace PixiEditor.Models.Tools.Tools
 
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color)
         {
-            return Only(ForestFire(layer, coordinates[0], color), layer);
+            return Only(LinearFill(layer, coordinates[0], color), layer);
         }
 
-        public BitmapPixelChanges ForestFire(Layer layer, Coordinates startingCoords, Color newColor)
+        public BitmapPixelChanges LinearFill(Layer layer, Coordinates startingCoords, Color newColor)
         {
             List<Coordinates> changedCoords = new List<Coordinates>();
-
+            Queue<FloodFillRange> floodFillQueue = new Queue<FloodFillRange>();
+            Color colorToReplace = layer.GetPixelWithOffset(startingCoords.X, startingCoords.Y);
             int width = BitmapManager.ActiveDocument.Width;
-            int height = BitmapManager.ActiveDocument.Height;
-
-            var visited = new bool[width, height];
-
-            Color colorToReplace = layer.GetPixelWithOffset(startingCoords.X, startingCoords.Y);
+            int height = BitmapManager.ActiveDocument.Height;
+            var visited = new bool[width * height];
+
 
-            var stack = new Stack<Coordinates>();
-            stack.Push(new Coordinates(startingCoords.X, startingCoords.Y));
+            PerformLinearFill(ref changedCoords, ref floodFillQueue, ref layer, startingCoords, width, ref colorToReplace, ref visited);
+            PerformFloodFIll(ref changedCoords, ref floodFillQueue, ref layer, ref colorToReplace, width, height, ref visited);
 
-            while (stack.Count > 0)
-            {
-                var cords = stack.Pop();
-                var relativeCords = layer.GetRelativePosition(cords);
+            return BitmapPixelChanges.FromSingleColoredArray(changedCoords, newColor);
+        }
 
-                if (cords.X < 0 || cords.X > width - 1)
-                {
-                    continue;
-                }
+        private void PerformLinearFill(ref List<Coordinates> changedCoords, ref Queue<FloodFillRange> floodFillQueue, ref Layer layer, Coordinates coords, int width, ref Color colorToReplace, ref bool[] visited)
+        {
 
-                if (cords.Y < 0 || cords.Y > height - 1)
-                {
-                    continue;
-                }
+            // Find Left Edge of Color Area
+            int lFillLoc = coords.X; //the location to check/fill on the left
+            int pixelIndex = (coords.X * width) + coords.Y;
 
-                if (visited[cords.X, cords.Y])
-                {
-                    continue;
-                }
+            while (true)
+            {
+                int x = pixelIndex % width;
+                int y = pixelIndex / width;
+                // fill with the color
+                changedCoords.Add(new Coordinates(x, y));
+                //**indicate that this pixel has already been checked and filled
+                visited[pixelIndex] = true;
 
-                if (layer.GetPixel(relativeCords.X, relativeCords.Y) == newColor)
-                {
-                    continue;
-                }
+                //**de-increment
+                lFillLoc--;     //de-increment counter
+                pixelIndex--;        //de-increment pixel index
+                //**exit loop if we're at edge of bitmap or color area
+                if (lFillLoc <= 0 || visited[pixelIndex] || layer.GetPixelWithOffset(x, y) != colorToReplace)
+                    break;
+
+            }
+
+            lFillLoc++;
+
+            //FIND RIGHT EDGE OF COLOR AREA
+            int rFillLoc = coords.X; //the location to check/fill on the left
+            pixelIndex = (width * coords.Y) + coords.X;
+            while (true)
+            {
+                int x = pixelIndex % width;
+                int y = pixelIndex / width;
+                changedCoords.Add(new Coordinates(x, y));
+
+                visited[pixelIndex] = true;
+                rFillLoc++;          // increment counter
+                pixelIndex++;
+                if (rFillLoc >= width || layer.GetPixelWithOffset(x, y) != colorToReplace || visited[pixelIndex])
+                    break;
 
-                if (layer.GetPixel(relativeCords.X, relativeCords.Y) == colorToReplace)
-                {
-                    changedCoords.Add(new Coordinates(cords.X, cords.Y));
-                    visited[cords.X, cords.Y] = true;
-                    stack.Push(new Coordinates(cords.X, cords.Y - 1));
-                    stack.Push(new Coordinates(cords.X, cords.Y + 1));
-                    stack.Push(new Coordinates(cords.X - 1, cords.Y));
-                    stack.Push(new Coordinates(cords.X + 1, cords.Y));
-                }
-            }
+            }
+            rFillLoc--;
 
-            return BitmapPixelChanges.FromSingleColoredArray(changedCoords, newColor);
+            FloodFillRange range = new FloodFillRange(lFillLoc, rFillLoc, coords.Y);
+            floodFillQueue.Enqueue(range);
+        }
+
+        private void PerformFloodFIll(ref List<Coordinates> changedCords, ref Queue<FloodFillRange> floodFillQueue, ref Layer layer, ref Color colorToReplace, int width, int height, ref bool[] pixelsVisited)
+        {
+            while (floodFillQueue.Count > 0)
+            {
+                FloodFillRange range = floodFillQueue.Dequeue();
+
+                //START THE LOOP UPWARDS AND DOWNWARDS
+                int upY = range.Y - 1;//so we can pass the y coord by ref
+                int downY = range.Y + 1;
+                int downPixelxIndex = (width * (range.Y + 1)) + range.StartX;//CoordsToPixelIndex(range.StartX,range.Y+1);
+                int upPixelIndex = (width * (range.Y - 1)) + range.StartX;//CoordsToPixelIndex(range.StartX, range.Y - 1);
+                for (int i = range.StartX; i <= range.EndX; i++)
+                {
+                    //START LOOP UPWARDS
+                    //if we're not above the top of the bitmap and the pixel above this one is within the color tolerance
+                    if (range.Y > 0 && layer.GetPixelWithOffset(i, upY) == colorToReplace && (!pixelsVisited[upPixelIndex]))
+                        PerformLinearFill(ref changedCords, ref floodFillQueue,ref layer, new Coordinates(i, upY), width, ref colorToReplace, ref pixelsVisited);
+                    //START LOOP DOWNWARDS
+                    if (range.Y < (height - 1) && layer.GetPixelWithOffset(i, downY) == colorToReplace && (!pixelsVisited[downPixelxIndex]))
+                        PerformLinearFill(ref changedCords, ref floodFillQueue, ref layer, new Coordinates(i, downY), width, ref colorToReplace, ref pixelsVisited);
+                    downPixelxIndex++;
+                    upPixelIndex++;
+                }
+            }
         }
     }
 }

+ 1 - 1
PixiEditor/Models/Tools/Tools/MagicWandTool.cs

@@ -57,7 +57,7 @@ namespace PixiEditor.Models.Tools.Tools
             Selection selection = BitmapManager.ActiveDocument.ActiveSelection;
 
             selection.SetSelection(
-                floodFill.ForestFire(
+                floodFill.LinearFill(
                     layer,
                     new Coordinates((int)document.MouseXOnCanvas, (int)document.MouseYOnCanvas),
                     System.Windows.Media.Colors.White