Skip to content

Commit ff91e7d

Browse files
Simple pen and fill fix (#12)
* Paint bucket now fill area properly * ToolPen now add history on release button
1 parent adb4466 commit ff91e7d

File tree

6 files changed

+111
-91
lines changed

6 files changed

+111
-91
lines changed

Pixed/Models/PixedModel.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ public void Undo()
5858
{
5959
_history.RemoveAt(_historyIndex);
6060
_historyIndex--;
61-
Undo();
6261
return;
6362
}
6463

@@ -68,7 +67,6 @@ public void Undo()
6867
{
6968
_history.RemoveAt(_historyIndex);
7069
_historyIndex--;
71-
Undo();
7270
return;
7371
}
7472

Pixed/Selection/LassoSelection.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private bool IsInSelection(Point point, Frame frame)
5959
private void VisitPixel(Point point, Frame frame)
6060
{
6161
bool frameBorderReached = false;
62-
var entry = PaintUtils.VisitConnectedPixelsHistory(frame.Layers[frame.SelectedLayer], point.X, point.Y, p =>
62+
var visited = PaintUtils.VisitConnectedPixels(frame.Layers[frame.SelectedLayer], point.X, point.Y, p =>
6363
{
6464
var alreadyVisited = GetPixel(point);
6565

@@ -78,12 +78,9 @@ private void VisitPixel(Point point, Frame frame)
7878
return true;
7979
});
8080

81-
for (int a = 0; a < entry.PixelX.Count; a++)
81+
foreach(var pixel in visited)
8282
{
83-
int x = entry.PixelX[a];
84-
int y = entry.PixelY[a];
85-
86-
SetPixel(new Point(x, y), frameBorderReached ? OUTSIDE : INSIDE);
83+
SetPixel(new Point(pixel.X, pixel.Y), frameBorderReached ? OUTSIDE : INSIDE);
8784
}
8885
}
8986

Pixed/Tools/ToolBucket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public override void ApplyTool(int x, int y, Frame frame, ref Bitmap overlay)
1111

1212
var history = PaintUtils.PaintSimiliarConnected(frame.Layers[frame.SelectedLayer], x, y, color);
1313
history.FrameId = frame.Id;
14-
Global.Models[0].AddHistory(history.ToEntry());
14+
Global.CurrentModel.AddHistory(history.ToEntry());
1515
Subjects.RefreshCanvas.OnNext(null);
1616
}
1717
}

Pixed/Tools/ToolPen.cs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,82 @@
11
using Pixed.Models;
22
using Pixed.Services.History;
3+
using Pixed.Utils;
4+
using System;
5+
using System.Collections.Generic;
36
using System.Drawing;
47

58
namespace Pixed.Tools;
69

710
internal class ToolPen : BaseTool
811
{
9-
private DynamicHistoryEntry _historyEntry = new();
12+
private int _prevX = -1;
13+
private int _prevY = -1;
14+
private List<Pixel> _pixels = [];
1015
public override void ApplyTool(int x, int y, Frame frame, ref Bitmap overlay)
1116
{
12-
_historyEntry.FrameId = frame.Id;
13-
_historyEntry.LayerId = frame.Layers[frame.SelectedLayer].Id;
14-
_historyEntry.PixelX.Add(x);
15-
_historyEntry.PixelY.Add(y);
16-
_historyEntry.OldColor.Add(frame.GetPixel(x, y));
17-
_historyEntry.NewColor.Add(GetToolColor());
18-
frame.SetPixel(x, y, GetToolColor());
19-
Subjects.RefreshCanvas.OnNext(null);
17+
_prevX = x;
18+
_prevY = y;
19+
20+
var color = GetToolColor();
21+
DrawOnOverlay(color, x, y, frame, ref overlay);
2022
}
2123

2224
public override void MoveTool(int x, int y, Frame frame, ref Bitmap overlay)
2325
{
24-
this.ApplyTool(x, y, frame, ref overlay);
26+
if((Math.Abs(x - _prevX) > 1) || (Math.Abs(y - _prevY) > 1))
27+
{
28+
var interpolatedPixels = MathUtils.GetBresenhamLine(x, y, _prevX, _prevY);
29+
30+
foreach(var pixel in interpolatedPixels)
31+
{
32+
ApplyTool(pixel.X, pixel.Y, frame, ref overlay);
33+
}
34+
}
35+
else
36+
{
37+
ApplyTool(x, y, frame, ref overlay);
38+
}
39+
40+
_prevX = x;
41+
_prevY = y;
2542
}
2643

2744
public override void ReleaseTool(int x, int y, Frame frame, ref Bitmap overlay)
2845
{
29-
base.ReleaseTool(x, y, frame, ref overlay);
30-
Global.Models[0].AddHistory(_historyEntry.ToEntry());
31-
_historyEntry = new DynamicHistoryEntry();
46+
var history = SetPixelsToFrame(frame);
47+
Global.CurrentModel.AddHistory(history);
48+
_pixels.Clear();
49+
_prevX = -1;
50+
_prevY = -1;
51+
Subjects.RefreshCanvas.OnNext(null);
52+
}
53+
54+
private void DrawOnOverlay(UniColor color, int x, int y, Frame frame, ref Bitmap overlay)
55+
{
56+
overlay.SetPixel(x, y, color);
57+
58+
if(color == UniColor.Transparent)
59+
{
60+
frame.SetPixel(x, y, color);
61+
}
62+
63+
this._pixels.Add(new Pixel(x, y, color));
64+
}
65+
66+
private HistoryEntry SetPixelsToFrame(Frame frame)
67+
{
68+
DynamicHistoryEntry entry = new()
69+
{
70+
FrameId = frame.Id,
71+
LayerId = frame.Layers[frame.SelectedLayer].Id
72+
};
73+
foreach (var pixel in this._pixels)
74+
{
75+
var oldColor = frame.GetPixel(pixel.X, pixel.Y);
76+
frame.SetPixel(pixel.X, pixel.Y, pixel.Color);
77+
entry.Add(pixel.X, pixel.Y, oldColor, pixel.Color);
78+
}
79+
80+
return entry.ToEntry();
3281
}
3382
}

Pixed/Utils/CollectionUtils.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Pixed.Utils
5+
{
6+
internal static class CollectionUtils
7+
{
8+
public static T Pop<T>(this IList<T> list)
9+
{
10+
T last = list.Last();
11+
list.Remove(last);
12+
return last;
13+
}
14+
}
15+
}

Pixed/Utils/PaintUtils.cs

Lines changed: 30 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static List<Pixel> GetSimiliarConnectedPixels(Frame frame, int x, int y)
1212
{
1313
int targetColor = frame.GetPixel(x, y);
1414

15-
List<Point> visited = [];
15+
List<Pixel> visited = [];
1616
return VisitConnectedPixels(frame.Layers[frame.SelectedLayer], x, y, p =>
1717
{
1818
if (visited.Contains(p))
@@ -33,107 +33,68 @@ public static DynamicHistoryEntry PaintSimiliarConnected(Layer layer, int x, int
3333
return new DynamicHistoryEntry();
3434
}
3535

36-
DynamicHistoryEntry paintedPixels = VisitConnectedPixelsHistory(layer, x, y, pixel =>
36+
DynamicHistoryEntry entry = new()
37+
{
38+
LayerId = layer.Id
39+
};
40+
var pixels = VisitConnectedPixels(layer, x, y, pixel =>
3741
{
3842
var sourceColor = layer.GetPixel(pixel.X, pixel.Y);
3943
if (sourceColor == targetColor)
4044
{
41-
layer.SetPixel(pixel.X, pixel.Y, replacementColor);
4245
return true;
4346
}
4447

4548
return false;
4649
});
4750

48-
return paintedPixels;
49-
}
50-
51-
public static DynamicHistoryEntry VisitConnectedPixelsHistory(Layer layer, int x, int y, Func<Point, bool> visitor)
52-
{
53-
Queue<Point> queue = [];
54-
DynamicHistoryEntry entry = new()
51+
foreach(var pixel in pixels)
5552
{
56-
LayerId = layer.Id
57-
};
58-
int[] dy = [-1, 0, 1, 0];
59-
int[] dx = [0, 1, 0, -1];
60-
61-
queue.Enqueue(new Point(x, y));
62-
int oldColor = layer.GetPixel(x, y);
63-
visitor.Invoke(new Point(x, y));
64-
int newColor = layer.GetPixel(x, y);
65-
entry.Add(x, y, oldColor, newColor);
66-
67-
int loopCount = 0;
68-
int cellCount = layer.Width * layer.Height;
69-
while (queue.Count > 0)
70-
{
71-
loopCount++;
72-
73-
var current = queue.Dequeue();
74-
75-
for (int i = 0; i < 4; i++)
76-
{
77-
int nextX = current.X + dx[i];
78-
int nextY = current.Y + dy[i];
79-
try
80-
{
81-
oldColor = layer.GetPixel(nextX, nextY);
82-
bool isValid = visitor(new Point(nextX, nextY));
83-
newColor = layer.GetPixel(nextX, nextY);
84-
85-
if (isValid)
86-
{
87-
queue.Enqueue(new Point(nextX, nextY));
88-
entry.Add(nextX, nextY, oldColor, newColor);
89-
}
90-
}
91-
catch (Exception e)
92-
{
93-
//Ignored
94-
}
95-
}
96-
97-
if (loopCount > 10 * cellCount)
98-
{
99-
break;
100-
}
53+
layer.SetPixel(pixel.X, pixel.Y, replacementColor);
54+
entry.Add(pixel.X, pixel.Y, pixel.Color, replacementColor);
10155
}
10256

10357
return entry;
10458
}
10559

106-
public static List<Pixel> VisitConnectedPixels(Layer layer, int x, int y, Func<Point, bool> visitor)
60+
public static List<Pixel> VisitConnectedPixels(Layer layer, int x, int y, Func<Pixel, bool> visitor) //TODO optimize
10761
{
108-
Queue<Point> queue = [];
109-
List<Pixel> points = [];
62+
List<Point> toVisit = [];
63+
List<Point> visited = [];
64+
List<Pixel> pixels = [];
11065
int[] dy = [-1, 0, 1, 0];
11166
int[] dx = [0, 1, 0, -1];
11267

113-
queue.Enqueue(new Point(x, y));
114-
visitor.Invoke(new Point(x, y));
115-
points.Add(new Pixel(x, y, layer.GetPixel(x, y)));
68+
toVisit.Add(new Point(x, y));
69+
int color = layer.GetPixel(x, y);
70+
visitor.Invoke(new Pixel(x, y, color));
71+
pixels.Add(new Pixel(x, y, color));
11672

11773
int loopCount = 0;
11874
int cellCount = layer.Width * layer.Height;
119-
while (queue.Count > 0)
75+
while (toVisit.Count > 0)
12076
{
121-
loopCount++;
77+
var current = toVisit.Pop();
78+
79+
if (visited.Contains(current)) continue;
12280

123-
var current = queue.Dequeue();
81+
visited.Add(current);
82+
83+
loopCount++;
12484

12585
for (int i = 0; i < 4; i++)
12686
{
12787
int nextX = current.X + dx[i];
12888
int nextY = current.Y + dy[i];
12989
try
13090
{
131-
bool isValid = visitor(new Point(nextX, nextY));
91+
color = layer.GetPixel(nextX, nextY);
92+
bool isValid = visitor(new Pixel(nextX, nextY, color));
13293

133-
if (isValid)
94+
if (isValid && layer.ContainsPixel(nextX, nextY))
13495
{
135-
queue.Enqueue(new Point(nextX, nextY));
136-
points.Add(new Pixel(nextX, nextY, layer.GetPixel(nextX, nextY)));
96+
toVisit.Add(new Point(nextX, nextY));
97+
pixels.Add(new Pixel(nextX, nextY, color));
13798
}
13899
}
139100
catch (Exception e)
@@ -148,6 +109,6 @@ public static List<Pixel> VisitConnectedPixels(Layer layer, int x, int y, Func<P
148109
}
149110
}
150111

151-
return points;
112+
return pixels;
152113
}
153114
}

0 commit comments

Comments
 (0)