completed refactoring, added 32x32 test images

This commit is contained in:
mm00
2025-04-01 18:48:58 +02:00
parent f954a77cc5
commit 7c484a9af3
7 changed files with 64 additions and 35 deletions

View File

@@ -7,10 +7,6 @@ namespace SDFMapCreator;
public static class ImageUtil {
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
var image = SixLabors.ImageSharp.Image.Load(path);
using var image16 = image as Image<Rgba64> ?? throw new NotSupportedException($"Image format not supported");
int width = image.Width;
int height = image.Height;
//result = new T[image.Width, image.Height];
return image switch {
Image<Rgba64> img => img.ProcessPixelsRgba64<T>(),
Image<Rgb24> img => img.ProcessPixelsRgb24<T>(),
@@ -21,6 +17,7 @@ public static class ImageUtil {
}
static T[,] ProcessPixelsRgba64<T>(this Image<Rgba64> image) where T : struct, IEquatable<T> {
var max = 65535f;
int width = image.Width;
int height = image.Height;
var result = new T[image.Width, image.Height];
@@ -31,16 +28,16 @@ public static class ImageUtil {
for (int x = 0; x < width; x++) {
switch (result) {
case float[,] f:
f[x, y] = span[x].R;
f[x, y] = span[x].R / max;
break;
case Vector2[,] f:
f[x, y] = new(span[x].R, span[x].G);
f[x, y] = new(span[x].R / max, span[x].G / max);
break;
case Vector3[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
break;
case Vector4[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
break;
}
}
@@ -50,6 +47,7 @@ public static class ImageUtil {
}
static T[,] ProcessPixelsRgb24<T>(this Image<Rgb24> image) where T : struct, IEquatable<T> {
var max = 255f;
int width = image.Width;
int height = image.Height;
var result = new T[image.Width, image.Height];
@@ -60,16 +58,16 @@ public static class ImageUtil {
for (int x = 0; x < width; x++) {
switch (result) {
case float[,] f:
f[x, y] = span[x].R;
f[x, y] = span[x].R / max;
break;
case Vector2[,] f:
f[x, y] = new(span[x].R, span[x].G);
f[x, y] = new(span[x].R / max, span[x].G / max);
break;
case Vector3[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
break;
case Vector4[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f);
break;
}
}
@@ -79,6 +77,7 @@ public static class ImageUtil {
}
static T[,] ProcessPixelsRgba32<T>(this Image<Rgba32> image) where T : struct, IEquatable<T> {
var max = 255f;
int width = image.Width;
int height = image.Height;
var result = new T[image.Width, image.Height];
@@ -89,16 +88,16 @@ public static class ImageUtil {
for (int x = 0; x < width; x++) {
switch (result) {
case float[,] f:
f[x, y] = span[x].R;
f[x, y] = span[x].R / max;
break;
case Vector2[,] f:
f[x, y] = new(span[x].R, span[x].G);
f[x, y] = new(span[x].R / max, span[x].G / max);
break;
case Vector3[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
break;
case Vector4[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
break;
}
}
@@ -108,27 +107,27 @@ public static class ImageUtil {
}
static T[,] ProcessPixelsRgb48<T>(this Image<Rgb48> image) where T : struct, IEquatable<T> {
using var image16 = image as Image<Rgb48> ?? throw new NotSupportedException($"Image format not supported");
var max = 65535f;
int width = image.Width;
int height = image.Height;
var result = new T[image.Width, image.Height];
image16.ProcessPixelRows(accessor => {
image.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;
f[x, y] = span[x].R / max;
break;
case Vector2[,] f:
f[x, y] = new(span[x].R, span[x].G);
f[x, y] = new(span[x].R / max, span[x].G / max);
break;
case Vector3[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max);
break;
case Vector4[,] f:
f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f);
f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f);
break;
}
}
@@ -155,16 +154,16 @@ public static class ImageUtil {
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]);
span[x] = new Rgb48((ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535), (ushort)(f[x,y] * 65535));
break;
case Vector2[,] f:
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0);
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), 0);
break;
case Vector3[,] f:
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z);
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535));
break;
case Vector4[,] f:
span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z);
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535));
break;
}
}

View File

@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
using SDFMapCreator;
public class Program {
private const float MAX = 65535f;
private const float MAX = 1f;
private const float MIN = 0f;
private static readonly int MAX_THREADS = Environment.ProcessorCount - 2;
private const bool outputMasks = true;
@@ -24,10 +24,23 @@ public class Program {
var imagesPath = "images";
for (int i = 0; i < 8; i++) {
var pixels = ImageUtil.LoadImage<Vector3>($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
}
/*
var pixels = ImageUtil.LoadImage<Vector3>($"./sphereempty.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherehalf.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherecut.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherefull.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
*/
//check if all the images in Images are the same resolution
if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) {
@@ -61,7 +74,7 @@ public class Program {
for (var i = 0; i < ImageMasks.Count; i++) {
var mask = ImageMasks[i];
SDFs.Add(SDF(mask));
if (outputSDFs) mask.Mask.SaveImage($"sdf{i}.png");
if (outputSDFs) SDFs[i].SDF.SaveImage($"sdf{i}.png");
}
Console.WriteLine("Creating gradients...");
@@ -75,13 +88,14 @@ public class Program {
// generate final image
var finalImage = new Vector3[width, height];
var currStep = 0f;
var stepIncrement = MAX / (Gradients.Count + 1);
var stepIncrement = MAX / (Gradients.Count);
for (var i = 0; i < Gradients.Count; i++) {
var mask = ImageMasks[i + 1];
var gradient = Gradients[i];
Console.WriteLine($"Applying gradient {i}..., {currStep} -> {currStep + stepIncrement}");
for (var x = 0; x < mask.Mask.GetLength(0); x++) {
for (var y = 0; y < mask.Mask.GetLength(1); y++) {
if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) continue;
if (mask.Mask[x, y].X == 0) continue;
finalImage[x, y] = new(Remap(gradient[x, y].X, MAX, MIN, currStep, currStep + stepIncrement));
}
}
@@ -116,7 +130,7 @@ public class Program {
});
sw.Stop();
Console.WriteLine(
$"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
$"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / (sw.ElapsedMilliseconds+1)} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s");
}
static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
@@ -167,16 +181,20 @@ public class Program {
//convert 1D index to 2D index
var x = (int)(i % width);
var y = (int)(i / width);
Vector2 p = new(x, y);
Vector2 p = new(x/(float)width, y/(float)height); //get the pixel position as a Vector2
float minDist = MAX; //initialize the minimum distance to the maximum possible value
float minDist = MAX + 1; //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 = Vector2.DistanceSquared(p, edge);
if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist;
Vector2 edgeNorm = new(edge.X / (float)width, edge.Y / (float)height);
float dist = Vector2.DistanceSquared(p, edgeNorm);
if (dist < minDist) minDist = dist;
}
if (minDist > MAX)
minDist = MAX;
temp[x, y] = new(minDist);
if (minDist > AbsMax) AbsMax = minDist;
iterCount++;

View File

@@ -55,6 +55,18 @@
<None Update="spherehalf.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="spherehalf32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="spherefull32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="spherecut32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="sphereempty.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B