diff --git a/Voile/Source/Resources/Font.cs b/Voile/Source/Resources/Font.cs index 9e6a998..70421bf 100644 --- a/Voile/Source/Resources/Font.cs +++ b/Voile/Source/Resources/Font.cs @@ -1,5 +1,9 @@ using System.Numerics; +using FreeTypeSharp; +using static FreeTypeSharp.FT; +using static FreeTypeSharp.FT_LOAD; + namespace Voile; public struct Glyph @@ -16,7 +20,7 @@ public struct Glyph /// /// Represents font data. /// -public class Font : Resource +public class Font : Resource, IDisposable { /// /// Internal handle for the font. If it got successfully loaded into the GPU, the value will be other than -1. @@ -27,11 +31,37 @@ public class Font : Resource public byte[]? Buffer { get; private set; } public long BufferSize { get; set; } + internal nint FacePtr; + internal nint LibraryPtr; + public Font(string path, byte[] buffer) : base(path) { Buffer = buffer; } + public void Dispose() + { + unsafe + { + if (FacePtr != IntPtr.Zero) + FT_Done_Face((FT_FaceRec_*)FacePtr); + + if (LibraryPtr != IntPtr.Zero) + FT_Done_FreeType((FT_LibraryRec_*)LibraryPtr); + } + } + + /// + /// Loads a basic ASCII charset for this font. + /// + public void LoadAsciiData() + { + for (char c = ' '; c < 127; c++) + { + GetGlyph(c); + } + } + public void Measure(string text) { foreach (char c in text) @@ -40,9 +70,14 @@ public class Font : Resource } } - internal void AddGlyph(char c, Glyph glyph) + public Glyph GetGlyph(char character) { - _glyphs.Add(c, glyph); + if (_glyphs.TryGetValue(character, out var glyph)) + return glyph; + + var loaded = LoadGlyph(character); + _glyphs[character] = loaded; + return loaded; } internal void GetGlyphBoundingBox(Glyph glyph) @@ -50,5 +85,26 @@ public class Font : Resource } + private unsafe Glyph LoadGlyph(char character) + { + var face = (FT_FaceRec_*)FacePtr; + + // TODO: for now, we're loading glyphs for metrics, but when implementing WebGPU rendering, we want to somehow render them to SDF or bitmap. + FT_Error error = FT_Load_Char(face, character, FT_LOAD_NO_BITMAP); + if (error != 0) + throw new Exception($"Failed to load glyph for '{character}'"); + + var glyph = face->glyph; + var bitmap = glyph->bitmap; + + return new Glyph + { + Width = bitmap.width, + Height = bitmap.rows, + Bearing = new Vector2(glyph->bitmap_left, glyph->bitmap_top), + Advance = (int)glyph->advance.x >> 6, + }; + } + private Dictionary _glyphs = new(); } \ No newline at end of file diff --git a/Voile/Source/Resources/Loaders/FontLoader.cs b/Voile/Source/Resources/Loaders/FontLoader.cs index 67b5eca..6e3fb28 100644 --- a/Voile/Source/Resources/Loaders/FontLoader.cs +++ b/Voile/Source/Resources/Loaders/FontLoader.cs @@ -1,12 +1,10 @@ using System.Numerics; -using System.Runtime.InteropServices; using FreeTypeSharp; using Voile.VFS; using static FreeTypeSharp.FT; using static FreeTypeSharp.FT_LOAD; -using static FreeTypeSharp.FT_Render_Mode_; namespace Voile.Resources; @@ -28,67 +26,28 @@ public class FontLoader : ResourceLoader result.BufferSize = bytesRead; LoadFaceData(result); + result.LoadAsciiData(); return result; } private unsafe void LoadFaceData(Font font) { - LoadFreeType(); + FT_LibraryRec_* lib; + FT_Error error = FT_Init_FreeType(&lib); + if (error != 0) + throw new Exception("Failed to init FreeType"); - fixed (FT_LibraryRec_** lib = &_lib) + font.LibraryPtr = (nint)lib; + + fixed (byte* data = font.Buffer) { - fixed (FT_FaceRec_* face = &_face) - { - FT_Error error; + FT_FaceRec_* face; + error = FT_New_Memory_Face(lib, data, (nint)font.BufferSize, 0, &face); + if (error != 0) + throw new Exception("Failed to load font face"); - var buffer = new Memory(font.Buffer); - var handle = buffer.Pin(); - - error = FT_New_Memory_Face(*lib, (byte*)handle.Pointer, (nint)font.BufferSize, 0, &face); - error = FT_Set_Pixel_Sizes(face, 0, (uint)font.Size); - - error = FT_Select_Charmap(face, FT_Encoding_.FT_ENCODING_UNICODE); - - uint gid; - var charcode = FT_Get_First_Char(face, &gid); - - while (gid != 0) - { - Console.WriteLine($"Codepoint: {(char)charcode}, gid {gid}"); - charcode = FT_Get_Next_Char(face, charcode, &gid); - } - - error = FT_Load_Char(face, 'F', FT_LOAD_NO_BITMAP); - - var metrics = face->glyph->metrics; - - font.AddGlyph('F', new Glyph() - { - Width = metrics.width >> 6, - Height = metrics.height >> 6, - Bearing = new Vector2(metrics.horiBearingX >> 6, metrics.horiBearingY >> 6), - Advance = (int)metrics.horiAdvance >> 6, - }); - - FT_Done_Face(face); - FT_Done_FreeType(*lib); - } + font.FacePtr = (nint)face; } } - - private unsafe void LoadFreeType() - { - fixed (FT_LibraryRec_** lib = &_lib) - { - FT_Error error; - if (_lib == null) - { - error = FT_Init_FreeType(lib); - } - } - } - - private unsafe FT_LibraryRec_* _lib; - private unsafe FT_FaceRec_ _face; } \ No newline at end of file diff --git a/Voile/Source/Resources/Resource.cs b/Voile/Source/Resources/Resource.cs index 7ef4a76..6ab34b9 100644 --- a/Voile/Source/Resources/Resource.cs +++ b/Voile/Source/Resources/Resource.cs @@ -57,7 +57,7 @@ namespace Voile /// /// Represents data usable by Voile. /// - public abstract class Resource : IDisposable + public abstract class Resource { /// /// Path to this resource. @@ -68,9 +68,5 @@ namespace Voile { Path = path; } - - public void Dispose() - { - } } } \ No newline at end of file