Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions Algorithms.Tests/Other/FloodFillTest.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
using System;
using System.Drawing;
using FluentAssertions;
using NUnit.Framework;
using SkiaSharp;
using System;

namespace Algorithms.Tests.Other;

public static class Tests
{
private static readonly Color Black = Color.FromArgb(255, 0, 0, 0);
private static readonly Color Green = Color.FromArgb(255, 0, 255, 0);
private static readonly Color Violet = Color.FromArgb(255, 255, 0, 255);
private static readonly Color White = Color.FromArgb(255, 255, 255, 255);
private static readonly Color Orange = Color.FromArgb(255, 255, 128, 0);
private const byte Alpha = 255;
private static readonly SKColor Black = new(0, 0, 0, Alpha);
private static readonly SKColor Green = new(0, 255, 0, Alpha);
private static readonly SKColor Violet = new(255, 0, 255, Alpha);
private static readonly SKColor White = new(255, 255, 255, Alpha);
private static readonly SKColor Orange = new(255, 128, 0, Alpha);

[Test]
public static void BreadthFirstSearch_ThrowsArgumentOutOfRangeException()
Expand Down Expand Up @@ -63,9 +64,9 @@ public static void DepthFirstSearch_Test3()
TestAlgorithm(Algorithms.Other.FloodFill.DepthFirstSearch, (1, 1), Green, Orange, (6, 4), White);
}

private static Bitmap GenerateTestBitmap()
private static SKBitmap GenerateTestBitmap()
{
Color[,] layout =
SKColor[,] layout =
{
{Violet, Violet, Green, Green, Black, Green, Green},
{Violet, Green, Green, Black, Green, Green, Green},
Expand All @@ -76,7 +77,7 @@ private static Bitmap GenerateTestBitmap()
{Violet, Violet, Violet, Violet, Violet, Violet, Violet},
};

Bitmap bitmap = new(7, 7);
SKBitmap bitmap = new(7, 7);
for (int x = 0; x < layout.GetLength(0); x++)
{
for (int y = 0; y < layout.GetLength(1); y++)
Expand All @@ -89,16 +90,16 @@ private static Bitmap GenerateTestBitmap()
}

private static void TestAlgorithm(
Action<Bitmap, ValueTuple<int, int>, Color, Color> algorithm,
Action<SKBitmap, ValueTuple<int, int>, SKColor, SKColor> algorithm,
ValueTuple<int, int> fillLocation,
Color targetColor,
Color replacementColor,
SKColor targetColor,
SKColor replacementColor,
ValueTuple<int, int> testLocation,
Color expectedColor)
SKColor expectedColor)
{
Bitmap bitmap = GenerateTestBitmap();
SKBitmap bitmap = GenerateTestBitmap();
algorithm(bitmap, fillLocation, targetColor, replacementColor);
Color actualColor = bitmap.GetPixel(testLocation.Item1, testLocation.Item2);
SKColor actualColor = bitmap.GetPixel(testLocation.Item1, testLocation.Item2);
actualColor.Should().Be(expectedColor);
}
}
15 changes: 7 additions & 8 deletions Algorithms.Tests/Other/KochSnowflakeTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using Algorithms.Other;
using FluentAssertions;
using NUnit.Framework;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Numerics;

namespace Algorithms.Tests.Other;

Expand Down Expand Up @@ -39,13 +39,12 @@ public static void TestKochSnowflakeExample()
var bitmapWidth = 600;
var offsetX = bitmapWidth / 10f;
var offsetY = bitmapWidth / 3.7f;

Bitmap bitmap = KochSnowflake.GetKochSnowflake();
SKBitmap bitmap = KochSnowflake.GetKochSnowflake();
bitmap.GetPixel(0, 0)
.Should()
.Be(Color.FromArgb(255, 255, 255, 255), "because the background should be white");
.Be(new SKColor(255, 255, 255, 255), "because the background should be white");
bitmap.GetPixel((int)offsetX, (int)offsetY)
.Should()
.Be(Color.FromArgb(255, 0, 0, 0), "because the snowflake is drawn in black and this is the position of the first vector");
.Be(new SKColor(0, 0, 0, 255), "because the snowflake is drawn in black and this is the position of the first vector");
}
}
14 changes: 7 additions & 7 deletions Algorithms.Tests/Other/MandelbrotTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Drawing;
using Algorithms.Other;
using NUnit.Framework;
using SkiaSharp;

namespace Algorithms.Tests.Other;

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

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

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

// Pixel inside the Mandelbrot set should be black.
Assert.That(Color.FromArgb(255, 0, 0, 0), Is.EqualTo(bitmap.GetPixel(400, 300)));
Assert.That(new SKColor(0, 0, 0, 255), Is.EqualTo(bitmap.GetPixel(400, 300)));
}
}
3 changes: 2 additions & 1 deletion Algorithms/Algorithms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.8" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 5 additions & 5 deletions Algorithms/Other/FloodFill.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using SkiaSharp;

namespace Algorithms.Other;

Expand All @@ -23,7 +23,7 @@ public static class FloodFill
/// <param name="location">The start location on the bitmap.</param>
/// <param name="targetColor">The old color to be replaced.</param>
/// <param name="replacementColor">The new color to replace the old one.</param>
public static void BreadthFirstSearch(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
public static void BreadthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
{
if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height)
{
Expand All @@ -46,7 +46,7 @@ public static void BreadthFirstSearch(Bitmap bitmap, (int x, int y) location, Co
/// <param name="location">The start location on the bitmap.</param>
/// <param name="targetColor">The old color to be replaced.</param>
/// <param name="replacementColor">The new color to replace the old one.</param>
public static void DepthFirstSearch(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
public static void DepthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
{
if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height)
{
Expand All @@ -56,7 +56,7 @@ public static void DepthFirstSearch(Bitmap bitmap, (int x, int y) location, Colo
DepthFirstFill(bitmap, location, targetColor, replacementColor);
}

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

private static void DepthFirstFill(Bitmap bitmap, (int x, int y) location, Color targetColor, Color replacementColor)
private static void DepthFirstFill(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor)
{
if (bitmap.GetPixel(location.x, location.y) == targetColor)
{
Expand Down
47 changes: 26 additions & 21 deletions Algorithms/Other/KochSnowflake.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using SkiaSharp;

namespace Algorithms.Other;

Expand Down Expand Up @@ -52,7 +52,7 @@ public static List<Vector2> Iterate(List<Vector2> initialVectors, int steps = 5)
/// <param name="bitmapWidth">The width of the rendered bitmap.</param>
/// <param name="steps">The number of iterations.</param>
/// <returns>The bitmap of the rendered Koch snowflake.</returns>
public static Bitmap GetKochSnowflake(
public static SKBitmap GetKochSnowflake(
int bitmapWidth = 600,
int steps = 5)
{
Expand Down Expand Up @@ -124,31 +124,36 @@ private static Vector2 Rotate(Vector2 vector, float angleInDegrees)
/// <param name="bitmapWidth">The width of the rendered bitmap.</param>
/// <param name="bitmapHeight">The height of the rendered bitmap.</param>
/// <returns>The bitmap of the rendered edges.</returns>
private static Bitmap GetBitmap(
private static SKBitmap GetBitmap(
List<Vector2> vectors,
int bitmapWidth,
int bitmapHeight)
{
Bitmap bitmap = new(bitmapWidth, bitmapHeight);
SKBitmap bitmap = new(bitmapWidth, bitmapHeight);
var canvas = new SKCanvas(bitmap);

using (Graphics graphics = Graphics.FromImage(bitmap))
// Set the background white
var rect = SKRect.Create(0, 0, bitmapWidth, bitmapHeight);

var paint = new SKPaint
{
// Set the background white
var imageSize = new Rectangle(0, 0, bitmapWidth, bitmapHeight);
graphics.FillRectangle(Brushes.White, imageSize);

// Draw the edges
for (var i = 0; i < vectors.Count - 1; i++)
{
Pen blackPen = new(Color.Black, 1);

var x1 = vectors[i].X;
var y1 = vectors[i].Y;
var x2 = vectors[i + 1].X;
var y2 = vectors[i + 1].Y;

graphics.DrawLine(blackPen, x1, y1, x2, y2);
}
Style = SKPaintStyle.Fill,
Color = SKColors.White,
};

canvas.DrawRect(rect, paint);

paint.Color = SKColors.Black;

// Draw the edges
for (var i = 0; i < vectors.Count - 1; i++)
{
var x1 = vectors[i].X;
var y1 = vectors[i].Y;
var x2 = vectors[i + 1].X;
var y2 = vectors[i + 1].Y;

canvas.DrawLine(new SKPoint(x1, y1), new SKPoint(x2, y2), paint);
}

return bitmap;
Expand Down
38 changes: 20 additions & 18 deletions Algorithms/Other/Mandelbrot.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Drawing;
using SkiaSharp;

namespace Algorithms.Other;

Expand All @@ -22,6 +22,8 @@ namespace Algorithms.Other;
/// </summary>
public static class Mandelbrot
{
private const byte Alpha = 255;

/// <summary>
/// Method to generate the bitmap of the Mandelbrot set. Two types of coordinates
/// are used: bitmap-coordinates that refer to the pixels and figure-coordinates
Expand All @@ -39,7 +41,7 @@ public static class Mandelbrot
/// <param name="maxStep">Maximum number of steps to check for divergent behavior.</param>
/// <param name="useDistanceColorCoding">Render in color or black and white.</param>
/// <returns>The bitmap of the rendered Mandelbrot set.</returns>
public static Bitmap GetBitmap(
public static SKBitmap GetBitmap(
int bitmapWidth = 800,
int bitmapHeight = 600,
double figureCenterX = -0.6,
Expand Down Expand Up @@ -69,7 +71,7 @@ public static Bitmap GetBitmap(
$"{nameof(maxStep)} should be greater than zero");
}

var bitmap = new Bitmap(bitmapWidth, bitmapHeight);
var bitmap = new SKBitmap(bitmapWidth, bitmapHeight);
var figureHeight = figureWidth / bitmapWidth * bitmapHeight;

// loop through the bitmap-coordinates
Expand Down Expand Up @@ -100,22 +102,22 @@ public static Bitmap GetBitmap(
/// </summary>
/// <param name="distance">Distance until divergence threshold.</param>
/// <returns>The color corresponding to the distance.</returns>
private static Color BlackAndWhiteColorMap(double distance) =>
private static SKColor BlackAndWhiteColorMap(double distance) =>
distance >= 1
? Color.FromArgb(255, 0, 0, 0)
: Color.FromArgb(255, 255, 255, 255);
? new SKColor(0, 0, 0, Alpha)
: new SKColor(255, 255, 255, Alpha);

/// <summary>
/// Color-coding taking the relative distance into account. The Mandelbrot set
/// is black.
/// </summary>
/// <param name="distance">Distance until divergence threshold.</param>
/// <returns>The color corresponding to the distance.</returns>
private static Color ColorCodedColorMap(double distance)
private static SKColor ColorCodedColorMap(double distance)
{
if (distance >= 1)
{
return Color.FromArgb(255, 0, 0, 0);
return new SKColor(0, 0, 0, Alpha);
}

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

var v = (int)val;
var p = 0;
var q = (int)(val * (1 - f * saturation));
var t = (int)(val * (1 - (1 - f) * saturation));
var v = (byte)val;
const byte p = 0;
var q = (byte)(val * (1 - f * saturation));
var t = (byte)(val * (1 - (1 - f) * saturation));

switch (hi)
{
case 0: return Color.FromArgb(255, v, t, p);
case 1: return Color.FromArgb(255, q, v, p);
case 2: return Color.FromArgb(255, p, v, t);
case 3: return Color.FromArgb(255, p, q, v);
case 4: return Color.FromArgb(255, t, p, v);
default: return Color.FromArgb(255, v, p, q);
case 0: return new SKColor(v, t, p, Alpha);
case 1: return new SKColor(q, v, p, Alpha);
case 2: return new SKColor(p, v, t, Alpha);
case 3: return new SKColor(p, q, v, Alpha);
case 4: return new SKColor(t, p, v, Alpha);
default: return new SKColor(v, p, q, Alpha);
}
}

Expand Down