commit 852fc3ff63813738efb33ee3fe3ea0bca4c26083 Author: glax Date: Sun May 12 22:35:52 2024 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.MSDF-Test/.idea/.gitignore b/.idea/.idea.MSDF-Test/.idea/.gitignore new file mode 100644 index 0000000..efcb8f9 --- /dev/null +++ b/.idea/.idea.MSDF-Test/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/.idea.MSDF-Test.iml +/contentModel.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.MSDF-Test/.idea/encodings.xml b/.idea/.idea.MSDF-Test/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.MSDF-Test/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.MSDF-Test/.idea/indexLayout.xml b/.idea/.idea.MSDF-Test/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.MSDF-Test/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.MSDF-Test/.idea/vcs.xml b/.idea/.idea.MSDF-Test/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.MSDF-Test/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/MSDF-Test.sln b/MSDF-Test.sln new file mode 100644 index 0000000..713b185 --- /dev/null +++ b/MSDF-Test.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSDF-Test", "MSDF-Test\MSDF-Test.csproj", "{73871F30-FF9E-48D3-9DF3-8B7809FBC6B8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {73871F30-FF9E-48D3-9DF3-8B7809FBC6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73871F30-FF9E-48D3-9DF3-8B7809FBC6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73871F30-FF9E-48D3-9DF3-8B7809FBC6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73871F30-FF9E-48D3-9DF3-8B7809FBC6B8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/MSDF-Test/Extensions.cs b/MSDF-Test/Extensions.cs new file mode 100644 index 0000000..ae458c7 --- /dev/null +++ b/MSDF-Test/Extensions.cs @@ -0,0 +1,22 @@ +using System.Drawing; + +namespace MSDF_Test; + +public static class Extensions +{ + + internal static Color MultiplyWith(this Color color, double factor) + { + return Color.FromArgb((int)(color.R * factor), (int)(color.G * factor), (int)(color.B * factor)); + } + + internal static Color Add(this Color c1, Color c2) + { + return Color.FromArgb(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B); + } + + internal static PointF Scale(this PointF point, float factor) + { + return new PointF(point.X * factor, point.Y * factor); + } +} \ No newline at end of file diff --git a/MSDF-Test/Glyph.cs b/MSDF-Test/Glyph.cs new file mode 100644 index 0000000..97fbd83 --- /dev/null +++ b/MSDF-Test/Glyph.cs @@ -0,0 +1,72 @@ +using System.Drawing; + +namespace MSDF_Test; + +public class Glyph +{ + private Bitmap originalBitmap; + private const float MaxColor = 255; //255 for Bitmaps + private const float DistanceRange = 255; //255 for Bitmaps + + public Glyph(Bitmap texture) + { + this.originalBitmap = texture; + } + + internal Bitmap GetBitmap(int size, Color foreground, Color background) + { + Bitmap ret = new(size, size, originalBitmap.PixelFormat); + for (int x = 0; x < ret.Width; x++) + { + for (int y = 0; y < ret.Height; y++) + { + PointF scaledPoint = new (x, y); + ret.SetPixel(x, y, GeneratePixel(scaledPoint.Scale((float)originalBitmap.Width / size)) ? foreground : background); + } + } + + return ret; + } + + private bool GeneratePixel(PointF point) + { + Color s = SampleBilinear(point); + float sample = Median(s); + float d = ColorDistance(sample); + return d >= 0; + } + + private Color SampleBilinear(PointF point) + { + int x1 = (int)Math.Floor(point.X - .5); + int y1 = (int)Math.Floor(point.Y - .5); + int x2 = x1 + 1; + int y2 = y1 + 1; + double wx = point.X - x1 - .5; + double wy = point.Y - y1 - .5; + + if (x1 >= 0 && y1 >= 0 && x2 <= (originalBitmap.Width - 1) && y2 <= (originalBitmap.Height - 1)) + { + Color x1y1 = originalBitmap.GetPixel(x1, y1).MultiplyWith((1 - wx) * (1 - wy)); + Color x1y2 = originalBitmap.GetPixel(x1, y2).MultiplyWith((1 - wx) * wy); + Color x2y1 = originalBitmap.GetPixel(x2, y1).MultiplyWith(wx * (1 - wy)); + Color x2y2 = originalBitmap.GetPixel(x2, y2).MultiplyWith(wx * wy); + + return x1y1.Add(x1y2).Add(x2y1).Add(x2y2); + } + + return Color.Black; //Edges can not be interpolated. Just return 0 + } + + internal static float Median(float r, float g, float b) + { + return Math.Max(Math.Min(r, g), Math.Min(Math.Max(r, g), b)); + } + + internal static float Median(Color c) => Median(c.R, c.G, c.B); + + internal static float ColorDistance(float sample) + { + return ((sample / MaxColor) - .5f) * DistanceRange; + } +} \ No newline at end of file diff --git a/MSDF-Test/MSDF-Test.csproj b/MSDF-Test/MSDF-Test.csproj new file mode 100644 index 0000000..82fbc8e --- /dev/null +++ b/MSDF-Test/MSDF-Test.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + MSDF_Test + enable + enable + + + + + + + + diff --git a/MSDF-Test/MSDF.cs b/MSDF-Test/MSDF.cs new file mode 100644 index 0000000..40f5e88 --- /dev/null +++ b/MSDF-Test/MSDF.cs @@ -0,0 +1,35 @@ +using System.Drawing; +using Newtonsoft.Json; + +namespace MSDF_Test; + +public class MSDF +{ + private readonly Texture _texture; + private readonly Dictionary _glyphs = new(); + + public MSDF(string texturePath, int glyphSize, int padding) + { + this._texture = new Texture(texturePath, glyphSize, padding); + List glyphBitmaps = this._texture.GetGlyphBitmaps(); + for(int i = 32; i < 128; i++) + _glyphs.Add((char)(i+1), new Glyph(glyphBitmaps[i])); + } + + public Bitmap Render(string str, int size) + { + Bitmap ret = new(size * str.Length, size); + using (Graphics grD = Graphics.FromImage(ret)) + { + for(int i = 0; i < str.Length; i++) + { + if (_glyphs.TryGetValue(str[i], out Glyph? glyph)) + { + int destOffsetX = i * size; + grD.DrawImage(glyph.GetBitmap(size, Color.White, Color.Transparent), new PointF(destOffsetX, 0)); + } + } + } + return ret; + } +} \ No newline at end of file diff --git a/MSDF-Test/Program.cs b/MSDF-Test/Program.cs new file mode 100644 index 0000000..162d20c --- /dev/null +++ b/MSDF-Test/Program.cs @@ -0,0 +1,24 @@ +using System.Drawing; +using System.Drawing.Imaging; +using MSDF_Test; + +MSDF msdf = new ("T_RobotoBlack_FONT.png", 66, 3); + +Console.Write("String: "); +string? str; +do +{ + str = Console.ReadLine(); +}while(str is null); +Console.Write("Size: "); +string? sizeStr; +int size; +do +{ + sizeStr = Console.ReadLine(); +}while(sizeStr is null || !int.TryParse(sizeStr, out size)); + +Console.WriteLine("Rendering..."); +Bitmap render = msdf.Render(str, size); +render.Save("render.png", ImageFormat.Png); +Console.WriteLine("Done."); \ No newline at end of file diff --git a/MSDF-Test/Texture.cs b/MSDF-Test/Texture.cs new file mode 100644 index 0000000..6ea44f1 --- /dev/null +++ b/MSDF-Test/Texture.cs @@ -0,0 +1,37 @@ +using System.Drawing; + +namespace MSDF_Test; + +public struct Texture +{ + internal Bitmap Image; + internal int GlyphSize; + internal int Padding; + + public Texture(Bitmap image, int glyphSize, int padding) + { + this.Image = image; + this.GlyphSize = glyphSize; + this.Padding = padding; + } + + internal List GetGlyphBitmaps() + { + List ret = new(); + for (int y = Padding; y < Image.Width - Padding; y += GlyphSize + Padding) + { + for (int x = Padding; x < Image.Height - Padding; x += GlyphSize + Padding) + { + Point topLeft = new (x, y); + ret.Add(Image.Clone(new Rectangle(topLeft, new Size(GlyphSize, GlyphSize)), Image.PixelFormat)); + } + } + + return ret; + } + + public Texture(string imagePath, int glyphSize, int padding) : this((Bitmap)Bitmap.FromFile(imagePath), glyphSize, padding) + { + + } +} \ No newline at end of file