Move some FreeType operations to Font itself, add lazy loading for Glyph data, remove IDisposable from base Resource.
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
|
using FreeTypeSharp;
|
||||||
|
using static FreeTypeSharp.FT;
|
||||||
|
using static FreeTypeSharp.FT_LOAD;
|
||||||
|
|
||||||
namespace Voile;
|
namespace Voile;
|
||||||
|
|
||||||
public struct Glyph
|
public struct Glyph
|
||||||
@@ -16,7 +20,7 @@ public struct Glyph
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents font data.
|
/// Represents font data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Font : Resource
|
public class Font : Resource, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal handle for the font. If it got successfully loaded into the GPU, the value will be other than -1.
|
/// 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 byte[]? Buffer { get; private set; }
|
||||||
public long BufferSize { get; set; }
|
public long BufferSize { get; set; }
|
||||||
|
|
||||||
|
internal nint FacePtr;
|
||||||
|
internal nint LibraryPtr;
|
||||||
|
|
||||||
public Font(string path, byte[] buffer) : base(path)
|
public Font(string path, byte[] buffer) : base(path)
|
||||||
{
|
{
|
||||||
Buffer = buffer;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a basic ASCII charset for this font.
|
||||||
|
/// </summary>
|
||||||
|
public void LoadAsciiData()
|
||||||
|
{
|
||||||
|
for (char c = ' '; c < 127; c++)
|
||||||
|
{
|
||||||
|
GetGlyph(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Measure(string text)
|
public void Measure(string text)
|
||||||
{
|
{
|
||||||
foreach (char c in 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)
|
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<char, Glyph> _glyphs = new();
|
private Dictionary<char, Glyph> _glyphs = new();
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using FreeTypeSharp;
|
using FreeTypeSharp;
|
||||||
using Voile.VFS;
|
using Voile.VFS;
|
||||||
|
|
||||||
using static FreeTypeSharp.FT;
|
using static FreeTypeSharp.FT;
|
||||||
using static FreeTypeSharp.FT_LOAD;
|
using static FreeTypeSharp.FT_LOAD;
|
||||||
using static FreeTypeSharp.FT_Render_Mode_;
|
|
||||||
|
|
||||||
namespace Voile.Resources;
|
namespace Voile.Resources;
|
||||||
|
|
||||||
@@ -28,67 +26,28 @@ public class FontLoader : ResourceLoader<Font>
|
|||||||
result.BufferSize = bytesRead;
|
result.BufferSize = bytesRead;
|
||||||
|
|
||||||
LoadFaceData(result);
|
LoadFaceData(result);
|
||||||
|
result.LoadAsciiData();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void LoadFaceData(Font font)
|
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_FaceRec_* face;
|
||||||
{
|
error = FT_New_Memory_Face(lib, data, (nint)font.BufferSize, 0, &face);
|
||||||
FT_Error error;
|
if (error != 0)
|
||||||
|
throw new Exception("Failed to load font face");
|
||||||
|
|
||||||
var buffer = new Memory<byte>(font.Buffer);
|
font.FacePtr = (nint)face;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -57,7 +57,7 @@ namespace Voile
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents data usable by Voile.
|
/// Represents data usable by Voile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class Resource : IDisposable
|
public abstract class Resource
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path to this resource.
|
/// Path to this resource.
|
||||||
@@ -68,9 +68,5 @@ namespace Voile
|
|||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user