Skip to content

Commit 327af3d

Browse files
authored
Replace System.Drawing.Common with SkiaSharp (#455)
1 parent 50829ea commit 327af3d

File tree

7 files changed

+84
-76
lines changed

7 files changed

+84
-76
lines changed

Algorithms.Tests/Other/FloodFillTest.cs

+17-16
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
using System;
2-
using System.Drawing;
31
using FluentAssertions;
42
using NUnit.Framework;
3+
using SkiaSharp;
4+
using System;
55

66
namespace Algorithms.Tests.Other;
77

88
public static class Tests
99
{
10-
private static readonly Color Black = Color.FromArgb(255, 0, 0, 0);
11-
private static readonly Color Green = Color.FromArgb(255, 0, 255, 0);
12-
private static readonly Color Violet = Color.FromArgb(255, 255, 0, 255);
13-
private static readonly Color White = Color.FromArgb(255, 255, 255, 255);
14-
private static readonly Color Orange = Color.FromArgb(255, 255, 128, 0);
10+
private const byte Alpha = 255;
11+
private static readonly SKColor Black = new(0, 0, 0, Alpha);
12+
private static readonly SKColor Green = new(0, 255, 0, Alpha);
13+
private static readonly SKColor Violet = new(255, 0, 255, Alpha);
14+
private static readonly SKColor White = new(255, 255, 255, Alpha);
15+
private static readonly SKColor Orange = new(255, 128, 0, Alpha);
1516

1617
[Test]
1718
public static void BreadthFirstSearch_ThrowsArgumentOutOfRangeException()
@@ -63,9 +64,9 @@ public static void DepthFirstSearch_Test3()
6364
TestAlgorithm(Algorithms.Other.FloodFill.DepthFirstSearch, (1, 1), Green, Orange, (6, 4), White);
6465
}
6566

66-
private static Bitmap GenerateTestBitmap()
67+
private static SKBitmap GenerateTestBitmap()
6768
{
68-
Color[,] layout =
69+
SKColor[,] layout =
6970
{
7071
{Violet, Violet, Green, Green, Black, Green, Green},
7172
{Violet, Green, Green, Black, Green, Green, Green},
@@ -76,7 +77,7 @@ private static Bitmap GenerateTestBitmap()
7677
{Violet, Violet, Violet, Violet, Violet, Violet, Violet},
7778
};
7879

79-
Bitmap bitmap = new(7, 7);
80+
SKBitmap bitmap = new(7, 7);
8081
for (int x = 0; x < layout.GetLength(0); x++)
8182
{
8283
for (int y = 0; y < layout.GetLength(1); y++)
@@ -89,16 +90,16 @@ private static Bitmap GenerateTestBitmap()
8990
}
9091

9192
private static void TestAlgorithm(
92-
Action<Bitmap, ValueTuple<int, int>, Color, Color> algorithm,
93+
Action<SKBitmap, ValueTuple<int, int>, SKColor, SKColor> algorithm,
9394
ValueTuple<int, int> fillLocation,
94-
Color targetColor,
95-
Color replacementColor,
95+
SKColor targetColor,
96+
SKColor replacementColor,
9697
ValueTuple<int, int> testLocation,
97-
Color expectedColor)
98+
SKColor expectedColor)
9899
{
99-
Bitmap bitmap = GenerateTestBitmap();
100+
SKBitmap bitmap = GenerateTestBitmap();
100101
algorithm(bitmap, fillLocation, targetColor, replacementColor);
101-
Color actualColor = bitmap.GetPixel(testLocation.Item1, testLocation.Item2);
102+
SKColor actualColor = bitmap.GetPixel(testLocation.Item1, testLocation.Item2);
102103
actualColor.Should().Be(expectedColor);
103104
}
104105
}

Algorithms.Tests/Other/KochSnowflakeTest.cs

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Numerics;
51
using Algorithms.Other;
62
using FluentAssertions;
73
using NUnit.Framework;
4+
using SkiaSharp;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Numerics;
88

99
namespace Algorithms.Tests.Other;
1010

@@ -39,13 +39,12 @@ public static void TestKochSnowflakeExample()
3939
var bitmapWidth = 600;
4040
var offsetX = bitmapWidth / 10f;
4141
var offsetY = bitmapWidth / 3.7f;
42-
43-
Bitmap bitmap = KochSnowflake.GetKochSnowflake();
42+
SKBitmap bitmap = KochSnowflake.GetKochSnowflake();
4443
bitmap.GetPixel(0, 0)
4544
.Should()
46-
.Be(Color.FromArgb(255, 255, 255, 255), "because the background should be white");
45+
.Be(new SKColor(255, 255, 255, 255), "because the background should be white");
4746
bitmap.GetPixel((int)offsetX, (int)offsetY)
4847
.Should()
49-
.Be(Color.FromArgb(255, 0, 0, 0), "because the snowflake is drawn in black and this is the position of the first vector");
48+
.Be(new SKColor(0, 0, 0, 255), "because the snowflake is drawn in black and this is the position of the first vector");
5049
}
5150
}

Algorithms.Tests/Other/MandelbrotTest.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
2-
using System.Drawing;
32
using Algorithms.Other;
43
using NUnit.Framework;
4+
using SkiaSharp;
55

66
namespace Algorithms.Tests.Other;
77

@@ -28,22 +28,22 @@ public static void MaxStepIsZeroOrNegative_ThrowsArgumentOutOfRangeException()
2828
[Test]
2929
public static void TestBlackAndWhite()
3030
{
31-
Bitmap bitmap = Mandelbrot.GetBitmap(useDistanceColorCoding: false);
31+
SKBitmap bitmap = Mandelbrot.GetBitmap(useDistanceColorCoding: false);
3232
// Pixel outside the Mandelbrot set should be white.
33-
Assert.That(Color.FromArgb(255, 255, 255, 255), Is.EqualTo(bitmap.GetPixel(0, 0)));
33+
Assert.That(new SKColor(255, 255, 255, 255), Is.EqualTo(bitmap.GetPixel(0, 0)));
3434

3535
// Pixel inside the Mandelbrot set should be black.
36-
Assert.That(Color.FromArgb(255, 0, 0, 0), Is.EqualTo(bitmap.GetPixel(400, 300)));
36+
Assert.That(new SKColor(0, 0, 0, 255), Is.EqualTo(bitmap.GetPixel(400, 300)));
3737
}
3838

3939
[Test]
4040
public static void TestColorCoded()
4141
{
42-
Bitmap bitmap = Mandelbrot.GetBitmap(useDistanceColorCoding: true);
42+
SKBitmap bitmap = Mandelbrot.GetBitmap(useDistanceColorCoding: true);
4343
// Pixel distant to the Mandelbrot set should be red.
44-
Assert.That(Color.FromArgb(255, 255, 0, 0), Is.EqualTo(bitmap.GetPixel(0, 0)));
44+
Assert.That(new SKColor(255, 0, 0, 255), Is.EqualTo(bitmap.GetPixel(0, 0)));
4545

4646
// Pixel inside the Mandelbrot set should be black.
47-
Assert.That(Color.FromArgb(255, 0, 0, 0), Is.EqualTo(bitmap.GetPixel(400, 300)));
47+
Assert.That(new SKColor(0, 0, 0, 255), Is.EqualTo(bitmap.GetPixel(400, 300)));
4848
}
4949
}

Algorithms/Algorithms.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19+
<PackageReference Include="SkiaSharp" Version="2.88.8" />
20+
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.8" />
1921
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
2022
<PrivateAssets>all</PrivateAssets>
2123
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2224
</PackageReference>
23-
<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
2425
</ItemGroup>
2526

2627
<ItemGroup>

Algorithms/Other/FloodFill.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
3+
using SkiaSharp;
44

55
namespace Algorithms.Other;
66

@@ -23,7 +23,7 @@ public static class FloodFill
2323
/// <param name="location">The start location on the bitmap.</param>
2424
/// <param name="targetColor">The old color to be replaced.</param>
2525
/// <param name="replacementColor">The new color to replace the old one.</param>
26-
public static void BreadthFirstSearch(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
26+
public static void BreadthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
2727
{
2828
if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height)
2929
{
@@ -46,7 +46,7 @@ public static void BreadthFirstSearch(Bitmap bitmap, (int x, int y) location, Co
4646
/// <param name="location">The start location on the bitmap.</param>
4747
/// <param name="targetColor">The old color to be replaced.</param>
4848
/// <param name="replacementColor">The new color to replace the old one.</param>
49-
public static void DepthFirstSearch(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
49+
public static void DepthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
5050
{
5151
if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height)
5252
{
@@ -56,7 +56,7 @@ public static void DepthFirstSearch(Bitmap bitmap, (int x, int y) location, Colo
5656
DepthFirstFill(bitmap, location, targetColor, replacementColor);
5757
}
5858

59-
private static void BreadthFirstFill(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor, List<(int x, int y)> queue)
59+
private static void BreadthFirstFill(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor, List<(int x, int y)> queue)
6060
{
6161
(int x, int y) currentLocation = queue[0];
6262
queue.RemoveAt(0);
@@ -77,7 +77,7 @@ private static void BreadthFirstFill(Bitmap bitmap, (int x, int y) location, Col
7777
}
7878
}
7979

80-
private static void DepthFirstFill(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
80+
private static void DepthFirstFill(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
8181
{
8282
if (bitmap.GetPixel(location.x, location.y) == targetColor)
8383
{

Algorithms/Other/KochSnowflake.cs

+26-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
43
using System.Numerics;
4+
using SkiaSharp;
55

66
namespace Algorithms.Other;
77

@@ -52,7 +52,7 @@ public static List<Vector2> Iterate(List<Vector2> initialVectors, int steps = 5)
5252
/// <param name="bitmapWidth">The width of the rendered bitmap.</param>
5353
/// <param name="steps">The number of iterations.</param>
5454
/// <returns>The bitmap of the rendered Koch snowflake.</returns>
55-
public static Bitmap GetKochSnowflake(
55+
public static SKBitmap GetKochSnowflake(
5656
int bitmapWidth = 600,
5757
int steps = 5)
5858
{
@@ -124,31 +124,36 @@ private static Vector2 Rotate(Vector2 vector, float angleInDegrees)
124124
/// <param name="bitmapWidth">The width of the rendered bitmap.</param>
125125
/// <param name="bitmapHeight">The height of the rendered bitmap.</param>
126126
/// <returns>The bitmap of the rendered edges.</returns>
127-
private static Bitmap GetBitmap(
127+
private static SKBitmap GetBitmap(
128128
List<Vector2> vectors,
129129
int bitmapWidth,
130130
int bitmapHeight)
131131
{
132-
Bitmap bitmap = new(bitmapWidth, bitmapHeight);
132+
SKBitmap bitmap = new(bitmapWidth, bitmapHeight);
133+
var canvas = new SKCanvas(bitmap);
133134

134-
using (Graphics graphics = Graphics.FromImage(bitmap))
135+
// Set the background white
136+
var rect = SKRect.Create(0, 0, bitmapWidth, bitmapHeight);
137+
138+
var paint = new SKPaint
135139
{
136-
// Set the background white
137-
var imageSize = new Rectangle(0, 0, bitmapWidth, bitmapHeight);
138-
graphics.FillRectangle(Brushes.White, imageSize);
139-
140-
// Draw the edges
141-
for (var i = 0; i < vectors.Count - 1; i++)
142-
{
143-
Pen blackPen = new(Color.Black, 1);
144-
145-
var x1 = vectors[i].X;
146-
var y1 = vectors[i].Y;
147-
var x2 = vectors[i + 1].X;
148-
var y2 = vectors[i + 1].Y;
149-
150-
graphics.DrawLine(blackPen, x1, y1, x2, y2);
151-
}
140+
Style = SKPaintStyle.Fill,
141+
Color = SKColors.White,
142+
};
143+
144+
canvas.DrawRect(rect, paint);
145+
146+
paint.Color = SKColors.Black;
147+
148+
// Draw the edges
149+
for (var i = 0; i < vectors.Count - 1; i++)
150+
{
151+
var x1 = vectors[i].X;
152+
var y1 = vectors[i].Y;
153+
var x2 = vectors[i + 1].X;
154+
var y2 = vectors[i + 1].Y;
155+
156+
canvas.DrawLine(new SKPoint(x1, y1), new SKPoint(x2, y2), paint);
152157
}
153158

154159
return bitmap;

Algorithms/Other/Mandelbrot.cs

+20-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Drawing;
2+
using SkiaSharp;
33

44
namespace Algorithms.Other;
55

@@ -22,6 +22,8 @@ namespace Algorithms.Other;
2222
/// </summary>
2323
public static class Mandelbrot
2424
{
25+
private const byte Alpha = 255;
26+
2527
/// <summary>
2628
/// Method to generate the bitmap of the Mandelbrot set. Two types of coordinates
2729
/// are used: bitmap-coordinates that refer to the pixels and figure-coordinates
@@ -39,7 +41,7 @@ public static class Mandelbrot
3941
/// <param name="maxStep">Maximum number of steps to check for divergent behavior.</param>
4042
/// <param name="useDistanceColorCoding">Render in color or black and white.</param>
4143
/// <returns>The bitmap of the rendered Mandelbrot set.</returns>
42-
public static Bitmap GetBitmap(
44+
public static SKBitmap GetBitmap(
4345
int bitmapWidth = 800,
4446
int bitmapHeight = 600,
4547
double figureCenterX = -0.6,
@@ -69,7 +71,7 @@ public static Bitmap GetBitmap(
6971
$"{nameof(maxStep)} should be greater than zero");
7072
}
7173

72-
var bitmap = new Bitmap(bitmapWidth, bitmapHeight);
74+
var bitmap = new SKBitmap(bitmapWidth, bitmapHeight);
7375
var figureHeight = figureWidth / bitmapWidth * bitmapHeight;
7476

7577
// loop through the bitmap-coordinates
@@ -100,22 +102,22 @@ public static Bitmap GetBitmap(
100102
/// </summary>
101103
/// <param name="distance">Distance until divergence threshold.</param>
102104
/// <returns>The color corresponding to the distance.</returns>
103-
private static Color BlackAndWhiteColorMap(double distance) =>
105+
private static SKColor BlackAndWhiteColorMap(double distance) =>
104106
distance >= 1
105-
? Color.FromArgb(255, 0, 0, 0)
106-
: Color.FromArgb(255, 255, 255, 255);
107+
? new SKColor(0, 0, 0, Alpha)
108+
: new SKColor(255, 255, 255, Alpha);
107109

108110
/// <summary>
109111
/// Color-coding taking the relative distance into account. The Mandelbrot set
110112
/// is black.
111113
/// </summary>
112114
/// <param name="distance">Distance until divergence threshold.</param>
113115
/// <returns>The color corresponding to the distance.</returns>
114-
private static Color ColorCodedColorMap(double distance)
116+
private static SKColor ColorCodedColorMap(double distance)
115117
{
116118
if (distance >= 1)
117119
{
118-
return Color.FromArgb(255, 0, 0, 0);
120+
return new SKColor(0, 0, 0, Alpha);
119121
}
120122

121123
// simplified transformation of HSV to RGB
@@ -126,19 +128,19 @@ private static Color ColorCodedColorMap(double distance)
126128
var hi = (int)Math.Floor(hue / 60) % 6;
127129
var f = hue / 60 - Math.Floor(hue / 60);
128130

129-
var v = (int)val;
130-
var p = 0;
131-
var q = (int)(val * (1 - f * saturation));
132-
var t = (int)(val * (1 - (1 - f) * saturation));
131+
var v = (byte)val;
132+
const byte p = 0;
133+
var q = (byte)(val * (1 - f * saturation));
134+
var t = (byte)(val * (1 - (1 - f) * saturation));
133135

134136
switch (hi)
135137
{
136-
case 0: return Color.FromArgb(255, v, t, p);
137-
case 1: return Color.FromArgb(255, q, v, p);
138-
case 2: return Color.FromArgb(255, p, v, t);
139-
case 3: return Color.FromArgb(255, p, q, v);
140-
case 4: return Color.FromArgb(255, t, p, v);
141-
default: return Color.FromArgb(255, v, p, q);
138+
case 0: return new SKColor(v, t, p, Alpha);
139+
case 1: return new SKColor(q, v, p, Alpha);
140+
case 2: return new SKColor(p, v, t, Alpha);
141+
case 3: return new SKColor(p, q, v, Alpha);
142+
case 4: return new SKColor(t, p, v, Alpha);
143+
default: return new SKColor(v, p, q, Alpha);
142144
}
143145
}
144146

0 commit comments

Comments
 (0)