Bläddra i källkod

Fixed magic wand tool

flabbet 3 år sedan
förälder
incheckning
ce09ad056d

+ 96 - 1
PixiEditor/Models/ImageManipulation/ShapeCalculator.cs → PixiEditor/Models/ImageManipulation/ToolCalculator.cs

@@ -1,12 +1,107 @@
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools.Tools;
+using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Windows;
 
 namespace PixiEditor.Models.ImageManipulation
 {
-    public static class ShapeCalculator
+    public static class ToolCalculator
     {
+        public static void GetLinearFill(Layer layer, Coordinates startingCoords, int maxWidth, int maxHeight, SKColor newColor, List<Coordinates> output)
+        {
+            Queue<FloodFillRange> floodFillQueue = new Queue<FloodFillRange>();
+            SKColor colorToReplace = layer.GetPixelWithOffset(startingCoords.X, startingCoords.Y);
+            if ((colorToReplace.Alpha == 0 && newColor.Alpha == 0) ||
+                colorToReplace == newColor)
+                return;
+
+            int width = maxWidth;
+            int height = maxHeight;
+            if (startingCoords.X < 0 || startingCoords.Y < 0 || startingCoords.X >= width || startingCoords.Y >= height)
+                return;
+            var visited = new bool[width * height];
+
+            Int32Rect dirtyRect = new Int32Rect(startingCoords.X, startingCoords.Y, 1, 1);
+
+            PerformLinearFill(layer, floodFillQueue, startingCoords, width, colorToReplace, ref dirtyRect, visited, output);
+            PerformFloodFIll(layer, floodFillQueue, colorToReplace, ref dirtyRect, width, height, visited, output);
+        }
+
+        private static void PerformLinearFill(
+            Layer layer, Queue<FloodFillRange> floodFillQueue,
+            Coordinates coords, int width, SKColor colorToReplace, ref Int32Rect dirtyRect, bool[] visited, List<Coordinates> output)
+        {
+            // Find the Left Edge of the Color Area
+            int fillXLeft = coords.X;
+            while (true)
+            {
+                // Indicate that this pixel has been checked
+                int pixelIndex = (coords.Y * width) + fillXLeft;
+                visited[pixelIndex] = true;
+
+                // Move one pixel to the left
+                fillXLeft--;
+                // Exit the loop if we're at edge of the bitmap or the color area
+                if (fillXLeft < 0 || visited[pixelIndex - 1] || layer.GetPixelWithOffset(fillXLeft, coords.Y) != colorToReplace)
+                    break;
+            }
+            int lastCheckedPixelLeft = fillXLeft + 1;
+
+            // Find the Right Edge of the Color Area
+            int fillXRight = coords.X;
+            while (true)
+            {
+                int pixelIndex = (coords.Y * width) + fillXRight;
+                visited[pixelIndex] = true;
+
+                fillXRight++;
+                if (fillXRight >= width || visited[pixelIndex + 1] || layer.GetPixelWithOffset(fillXRight, coords.Y) != colorToReplace)
+                    break;
+            }
+            int lastCheckedPixelRight = fillXRight - 1;
+
+            int relativeY = coords.Y - layer.OffsetY;
+            LineTool.CalculateBresenhamLine(new Coordinates(lastCheckedPixelLeft - layer.OffsetX, relativeY), new Coordinates(lastCheckedPixelRight - layer.OffsetX, relativeY), output);
+            dirtyRect = dirtyRect.Expand(new Int32Rect(lastCheckedPixelLeft, coords.Y, lastCheckedPixelRight - lastCheckedPixelLeft + 1, 1));
+
+            FloodFillRange range = new FloodFillRange(lastCheckedPixelLeft, lastCheckedPixelRight, coords.Y);
+            floodFillQueue.Enqueue(range);
+        }
+
+        private static void PerformFloodFIll(
+            Layer layer, Queue<FloodFillRange> floodFillQueue,
+            SKColor colorToReplace, ref Int32Rect dirtyRect, int width, int height, bool[] pixelsVisited, List<Coordinates> output)
+        {
+            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;
+                int upPixelIndex = (width * (range.Y - 1)) + range.StartX;
+                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 && (!pixelsVisited[upPixelIndex]) && layer.GetPixelWithOffset(i, upY) == colorToReplace)
+                        PerformLinearFill(layer, floodFillQueue, new Coordinates(i, upY), width, colorToReplace, ref dirtyRect, pixelsVisited, output);
+                    //START LOOP DOWNWARDS
+                    if (range.Y < (height - 1) && (!pixelsVisited[downPixelxIndex]) && layer.GetPixelWithOffset(i, downY) == colorToReplace)
+                        PerformLinearFill(layer, floodFillQueue, new Coordinates(i, downY), width, colorToReplace, ref dirtyRect, pixelsVisited, output);
+                    downPixelxIndex++;
+                    upPixelIndex++;
+                }
+            }
+        }
+
         public static void GenerateEllipseNonAlloc(Coordinates start, Coordinates end, bool fill,
             List<Coordinates> output)
         {

+ 12 - 8
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -17,17 +17,23 @@ namespace PixiEditor.Models.Tools.Tools
         private SKPaint paint = new SKPaint() { Style = SKPaintStyle.Stroke };
 
         public static List<Coordinates> GetBresenhamLine(Coordinates start, Coordinates end)
+        {
+            List<Coordinates> output = new List<Coordinates>();
+            CalculateBresenhamLine(start, end, output);
+            return output;
+        }
+
+        public static void CalculateBresenhamLine(Coordinates start, Coordinates end, List<Coordinates> output)
         {
             int x1 = start.X;
             int x2 = end.X;
             int y1 = start.Y;
             int y2 = end.Y;
 
-            List<Coordinates> coordinates = new List<Coordinates>();
             if (x1 == x2 && y1 == y2)
             {
-                coordinates.Add(start);
-                return coordinates;
+                output.Add(start);
+                return;
             }
 
             int d, dx, dy, ai, bi, xi, yi;
@@ -55,7 +61,7 @@ namespace PixiEditor.Models.Tools.Tools
                 dy = y1 - y2;
             }
 
-            coordinates.Add(new Coordinates(x, y));
+            output.Add(new Coordinates(x, y));
 
             if (dx > dy)
             {
@@ -77,7 +83,7 @@ namespace PixiEditor.Models.Tools.Tools
                         x += xi;
                     }
 
-                    coordinates.Add(new Coordinates(x, y));
+                    output.Add(new Coordinates(x, y));
                 }
             }
             else
@@ -100,11 +106,9 @@ namespace PixiEditor.Models.Tools.Tools
                         y += yi;
                     }
 
-                    coordinates.Add(new Coordinates(x, y));
+                    output.Add(new Coordinates(x, y));
                 }
             }
-
-            return coordinates;
         }
 
         public LineTool()

+ 14 - 10
PixiEditor/Models/Tools/Tools/MagicWandTool.cs

@@ -17,13 +17,12 @@ namespace PixiEditor.Models.Tools.Tools
 {
     public class MagicWandTool : ReadonlyTool, ICachedDocumentTool
     {
-        private readonly FloodFillTool floodFill;
-
         private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
 
         private BitmapManager BitmapManager { get; }
 
         private IEnumerable<Coordinates> oldSelection;
+        private List<Coordinates> newSelection = new List<Coordinates>();
 
         public override string Tooltip => "Magic Wand (W). Flood's the selection";
 
@@ -56,20 +55,25 @@ namespace PixiEditor.Models.Tools.Tools
 
             Selection selection = BitmapManager.ActiveDocument.ActiveSelection;
 
-            /*selection.SetSelection(
-                floodFill.LinearFill(
-                    layer,
-                    new Coordinates((int)document.MouseXOnCanvas, (int)document.MouseYOnCanvas),
-                    SKColors.White
-                    ).ChangedPixels.Keys,
-                selectionType);*/
+            newSelection.Clear();
+
+            ToolCalculator.GetLinearFill(
+                   layer,
+                   new Coordinates(
+                       (int)document.MouseXOnCanvas,
+                       (int)document.MouseYOnCanvas),
+                   BitmapManager.ActiveDocument.Width,
+                   BitmapManager.ActiveDocument.Height,
+                   SKColors.White,
+                   newSelection);
+
+            selection.SetSelection(newSelection, selectionType);
 
             SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelection, selectionType);
         }
 
         public MagicWandTool(BitmapManager manager)
         {
-            floodFill = new FloodFillTool(manager);
             BitmapManager = manager;
 
             Toolbar = new MagicWandToolbar();

+ 2 - 2
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -68,7 +68,7 @@ namespace PixiEditor.Models.Tools.Tools
         public IEnumerable<Coordinates> GetRectangleSelectionForPoints(Coordinates start, Coordinates end)
         {
             List<Coordinates> result = new List<Coordinates>();
-            ShapeCalculator.GenerateRectangleNonAlloc(
+            ToolCalculator.GenerateRectangleNonAlloc(
                 start, end, true, 1, result);
             return result;
         }
@@ -76,7 +76,7 @@ namespace PixiEditor.Models.Tools.Tools
         public IEnumerable<Coordinates> GetCircleSelectionForPoints(Coordinates start, Coordinates end)
         {
             List<Coordinates> result = new List<Coordinates>();
-            ShapeCalculator.GenerateEllipseNonAlloc(
+            ToolCalculator.GenerateEllipseNonAlloc(
                 start, end, true, result);
             return result;
         }