diff --git a/SDFMapCreator/ImageUtil.cs b/SDFMapCreator/ImageUtil.cs new file mode 100644 index 0000000..7588e19 --- /dev/null +++ b/SDFMapCreator/ImageUtil.cs @@ -0,0 +1,86 @@ +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + +namespace SDFMapCreator; + +public static class ImageUtil { + public static T[,] LoadImage(string path) where T : struct, IEquatable { + var image = SixLabors.ImageSharp.Image.Load(path); + using var image16 = image as Image ?? throw new NotSupportedException($"Image format not supported"); + int width = image.Width; + int height = image.Height; + var result = new T[image.Width, image.Height]; + image16.ProcessPixelRows(accessor => { + //we use Y as the row index and X as the column index + for (int y = 0; y < height; y++) { + var span = accessor.GetRowSpan(y); + for (int x = 0; x < width; x++) { + switch (result) { + case float[,] f: + f[x, y] = span[x].R; + break; + case Vector2[,] f: + f[x,y] = new (span[x].R, span[x].G); + break; + case Vector3[,] f: + f[x,y] = new (span[x].R, span[x].G, span[x].B); + break; + case Vector4[,] f: + f[x,y] = new (span[x].R, span[x].G, span[x].B, 1f); + break; + } + } + } + }); + ImageData(image, path); + return result; + } + + public static void SaveImage(this T[,] array, string path) where T : struct, IEquatable { + var width = array.GetLength(0); + var height = array.GetLength(1); + uint channels = array switch { + float[,] => 1, + Vector2[,] => 2, + Vector3[,] => 3, + Vector4[,] => 4, + _ => throw new NotSupportedException($"Type {typeof(T)} is not supported.") + }; + Console.Write($"Writing image {path}..."); + using Image image = new(width, height); + image.ProcessPixelRows(accessor => { + for (int y = 0; y < height; y++) { + var span = accessor.GetRowSpan(y); + for (int x = 0; x < width; x++) { + switch (array) { + case float[,] f: + span[x] = new Rgb48((ushort)f[x, y], (ushort)f[x, y], (ushort)f[x,y]); + break; + case Vector2[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0); + break; + case Vector3[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + break; + case Vector4[,] f: + span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + break; + } + } + } + }); + Console.WriteLine($"Done!"); + image.Save(path); + } + + private static void ImageData(SixLabors.ImageSharp.Image image1, string path) { + Console.WriteLine( + $""" + Image file: {path} + Resolution: {image1.Width}x{image1.Height} + Total Pixels: {image1.Width*image1.Height} |{"NaN"} channels, {image1.PixelType.BitsPerPixel/4} bits per channel + + """); + } +} \ No newline at end of file diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index bc855e7..9f4680b 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -1,124 +1,7 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; -//using ImageMagick; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using float2 = System.Numerics.Vector2; -using float3 = System.Numerics.Vector3; - -public record Image(float3[,] Pixels, int Width, int Height); - -public record MaskData(float3[,] Mask, Image Image, List Edges); - -public record TransitionMaskData(float3[,] Mask, Image ImageA, Image ImageB); - -public record SDFData(float3[,] SDF); - -public static class ArrayExt { - public static float3[,] To2DFloat3(this float[] array, uint width, uint height) { - float3[,] result = new float3[width, height]; - for (int i = 0; i < width * height; i++) { - uint x = (uint)(i % width); - uint y = (uint)(i / width); - result[y, x] = new(array[i * 3], array[i * 3 + 1], array[i * 3 + 2]); - } - return result; - } - - public static float[] ToFloatArray(this float3[,] array) { - float[] result = new float[array.GetLength(0) * array.GetLength(1) * 3]; - for (int x = 0; x < array.GetLength(0); x++) { - for (int y = 0; y < array.GetLength(1); y++) { - result[x * array.GetLength(1) * 3 + y * 3] = array[x, y].X; - result[x * array.GetLength(1) * 3 + y * 3 + 1] = array[x, y].Y; - result[x * array.GetLength(1) * 3 + y * 3 + 2] = array[x, y].Z; - } - } - return result; - } -} - -public static class ImageUtil { - public static T[,] LoadImage(string path) where T : struct, IEquatable { - var image = SixLabors.ImageSharp.Image.Load(path); - using var image16 = image as Image ?? throw new NotSupportedException($"Image format not supported"); - int width = image.Width; - int height = image.Height; - var result = new T[image.Width, image.Height]; - image16.ProcessPixelRows(accessor => { - //we use Y as the row index and X as the column index - for (int y = 0; y < height; y++) { - var span = accessor.GetRowSpan(y); - for (int x = 0; x < width; x++) { - switch (result) { - case float[,] f: - f[x, y] = span[x].R; - break; - case Vector2[,] f: - f[x,y] = new (span[x].R, span[x].G); - break; - case Vector3[,] f: - f[x,y] = new (span[x].R, span[x].G, span[x].B); - break; - case Vector4[,] f: - f[x,y] = new (span[x].R, span[x].G, span[x].B, 1f); - break; - } - } - } - }); - ImageData(image, path); - return result; - } - - public static void SaveImage(this T[,] array, string path) where T : struct, IEquatable { - var width = array.GetLength(0); - var height = array.GetLength(1); - uint channels = array switch { - float[,] => 1, - Vector2[,] => 2, - Vector3[,] => 3, - Vector4[,] => 4, - _ => throw new NotSupportedException($"Type {typeof(T)} is not supported.") - }; - Console.Write($"Writing image {path}..."); - using Image image = new(width, height); - image.ProcessPixelRows(accessor => { - for (int y = 0; y < height; y++) { - var span = accessor.GetRowSpan(y); - for (int x = 0; x < width; x++) { - switch (array) { - case float[,] f: - span[x] = new Rgb48((ushort)f[x, y], (ushort)f[x, y], (ushort)f[x,y]); - break; - case Vector2[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0); - break; - case Vector3[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); - break; - case Vector4[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); - break; - } - } - } - }); - Console.WriteLine($"Done!"); - image.Save(path); - } - - private static void ImageData(SixLabors.ImageSharp.Image image1, string path) { - Console.WriteLine( - $""" - Image file: {path} - Resolution: {image1.Width}x{image1.Height} - Total Pixels: {image1.Width*image1.Height} |{"NaN"} channels, {image1.PixelType.BitsPerPixel/4} bits per channel - - """); - } -} +using SDFMapCreator; public class Program { private const float MAX = 65535f; @@ -132,14 +15,13 @@ public class Program { static List ImageMasks = new(); static List TransitionMasks = new(); static List SDFs = new(); - static List Gradients = new(); + static List Gradients = new(); static void ConsoleUpdateLine(string s) => Console.Write("\r" + s); - + public static void Main(string[] args) { Console.WriteLine("Reading images..."); - //foreach image in arguments load the image - + var imagesPath = "images"; for (int i = 0; i < 8; i++) { @@ -191,7 +73,7 @@ public class Program { } // generate final image - var finalImage = new float3[width, height]; + var finalImage = new Vector3[width, height]; var currStep = 0f; var stepIncrement = MAX / (Gradients.Count + 1); for (var i = 0; i < Gradients.Count; i++) { @@ -237,13 +119,13 @@ public class Program { $"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s"); } - static float3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) { + static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) { uint width = (uint)mask.ImageA.Width; uint height = (uint)mask.ImageA.Height; int iterCount = 0; var sw = new Stopwatch(); sw.Start(); - float3[,] temp = new float3[width, height]; + Vector3[,] temp = new Vector3[width, height]; var min = MAX; var max = MIN; @@ -275,7 +157,7 @@ public class Program { static SDFData SDF(MaskData mask) { var width = (uint)mask.Mask.GetLength(0); var height = (uint)mask.Mask.GetLength(1); - var temp = new float3[width, height]; + var temp = new Vector3[width, height]; float AbsMax = MIN; int iterCount = 0; var sw = new Stopwatch(); @@ -285,13 +167,13 @@ public class Program { //convert 1D index to 2D index var x = (int)(i % width); var y = (int)(i / width); - float2 p = new(x, y); + Vector2 p = new(x, y); float minDist = MAX; //initialize the minimum distance to the maximum possible value //loop through all the pixels in the mask foreach (var edge in mask.Edges) { - float dist = float2.DistanceSquared(p, edge); + float dist = Vector2.DistanceSquared(p, edge); if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist; } @@ -323,13 +205,12 @@ public class Program { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float EuclideanDistance(float2 a, float2 b) => + private static float EuclideanDistance(Vector2 a, Vector2 b) => MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2)); - - static float3[,] GetABMask(float3[,] A, float3[,] B, uint resX, uint resY) { - var temp = new float3[resX, resY]; + static Vector3[,] GetABMask(Vector3[,] A, Vector3[,] B, uint resX, uint resY) { + var temp = new Vector3[resX, resY]; Parallel.For(0, resX * resY, parallelOptions, (i) => { uint x = (uint)(i % resX); @@ -344,8 +225,8 @@ public class Program { return temp; } - static float3[,] SelfMask(float3[,] A, uint resX, uint resY) { - var temp = new float3[resX, resY]; + static Vector3[,] SelfMask(Vector3[,] A, uint resX, uint resY) { + var temp = new Vector3[resX, resY]; Parallel.For(0, resX * resY, parallelOptions, (i) => { uint x = (uint)(i % resX); @@ -358,7 +239,7 @@ public class Program { return temp; } - static bool EdgeKernel(float3[,] mask, int x, int y, uint width, uint height) { + static bool EdgeKernel(Vector3[,] mask, int x, int y, uint width, uint height) { //if we are already empty, return false if (mask[x, y].X == 0) return false; //if we are on the edge of the image, return false diff --git a/SDFMapCreator/Records.cs b/SDFMapCreator/Records.cs new file mode 100644 index 0000000..eb7eef0 --- /dev/null +++ b/SDFMapCreator/Records.cs @@ -0,0 +1,8 @@ +using System.Numerics; + +namespace SDFMapCreator; + +public record Image(Vector3[,] Pixels, int Width, int Height); +public record MaskData(Vector3[,] Mask, Image Image, List Edges); +public record SDFData(Vector3[,] SDF); +public record TransitionMaskData(Vector3[,] Mask, Image ImageA, Image ImageB); \ No newline at end of file