WIP: measure text in Font.

This commit is contained in:
2025-06-29 15:10:37 +02:00
parent 6b108ba56c
commit 0ec4e45c38
4 changed files with 70 additions and 26 deletions

View File

@@ -34,6 +34,8 @@ public class Font : Resource, IDisposable
internal nint FacePtr;
internal nint LibraryPtr;
internal List<int> Codepoints => _glyphs.Keys.ToList();
public Font(string path, byte[] buffer) : base(path)
{
Buffer = buffer;
@@ -62,12 +64,38 @@ public class Font : Resource, IDisposable
}
}
public void Measure(string text)
/// <summary>
/// Measures a given string using the font metrics.
/// </summary>
/// <param name="text">Text to measure.</param>
/// <returns>A <see cref="Rect"/> with the sizes of a given text using this font.</returns>
public Rect Measure(string text)
{
if (string.IsNullOrEmpty(text))
return Rect.Zero;
float totalWidth = 0;
float maxAscent = 0;
float maxDescent = 0;
foreach (char c in text)
{
var glyph = GetGlyph(c);
totalWidth += glyph.Advance;
float ascent = glyph.Bearing.Y;
float descent = glyph.Height - glyph.Bearing.Y;
if (ascent > maxAscent)
maxAscent = ascent;
if (descent > maxDescent)
maxDescent = descent;
}
float totalHeight = maxAscent + maxDescent;
return new Rect(totalWidth, totalHeight);
}
public Glyph GetGlyph(char character)
@@ -80,31 +108,29 @@ public class Font : Resource, IDisposable
return loaded;
}
internal void GetGlyphBoundingBox(Glyph glyph)
{
}
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);
FT_Error error = FT_Set_Pixel_Sizes(face, 0, (uint)Size);
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;
var metrics = glyph->metrics;
return new Glyph
{
Width = bitmap.width,
Height = bitmap.rows,
Bearing = new Vector2(glyph->bitmap_left, glyph->bitmap_top),
Width = metrics.width >> 6,
Height = metrics.height >> 6,
Bearing = new Vector2(metrics.horiBearingX >> 6, metrics.horiBearingY >> 6),
Advance = (int)glyph->advance.x >> 6,
};
}
private Dictionary<char, Glyph> _glyphs = new();
private Dictionary<int, Glyph> _glyphs = new();
}

View File

@@ -65,7 +65,12 @@ namespace Voile.Resources
var resource = ResourceManager.LoadedResources[resourceGuid];
ResourceManager.RemoveResource(resourceGuid);
resource.Dispose();
if (resource is IDisposable disposable)
{
disposable.Dispose();
}
return true;
}

View File

@@ -6,24 +6,29 @@ namespace Voile.UI.Widgets;
public class Label : Widget
{
public override Rect MinimumSize => throw new NotImplementedException();
public override Rect MinimumSize => _textSize;
public string Text { get; set; } = "Hello World!";
public Label(string text)
public string Text
{
Text = text;
get => _text; set
{
_text = value;
MarkDirty();
}
}
public Label(string text, ResourceRef<Font> fontOverride)
{
Text = text;
_text = text;
_fontOverride = fontOverride;
MarkDirty();
Update();
}
protected override void OnInput(UIInputContext action)
{
throw new NotImplementedException();
}
public override void Render(RenderSystem renderer, Style style)
@@ -31,13 +36,19 @@ public class Label : Widget
// TODO: use style here.
if (!_fontOverride.HasValue) return;
renderer.SetTransform(GlobalPosition, Vector2.Zero);
renderer.DrawText(_fontOverride, Text, Color.White);
renderer.DrawText(_fontOverride, _text, Color.White);
}
protected override void OnUpdate()
{
throw new NotImplementedException();
var font = _fontOverride.Value;
_textSize = font.Measure(_text);
Size = _textSize;
}
private ResourceRef<Font> _fontOverride = ResourceRef<Font>.Empty();
private string _text = "Hello, World!";
private Rect _textSize = Rect.Zero;
}