a1f4e6a4dc
Due to unforseen naming conflicts, the project has been rebranded under the ReFuel umbrealla and will now be referred to as Dashboard from now on. Other changes will occur to suit the library more for the engine whilst keeping the freestanding nature of the library. Rename folder. Rename to Dashboard.OpenTK Rename to Dashboard.Media.Defaults. Do the last renames and path fixes.
182 lines
5.7 KiB
C#
182 lines
5.7 KiB
C#
using Dashboard.CommandMachine;
|
|
using Dashboard.Media;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Dashboard.Typography
|
|
{
|
|
public static class Typesetter
|
|
{
|
|
private ref struct LineEnumerator
|
|
{
|
|
private ReadOnlySpan<char> Entire, Segment;
|
|
private bool Final;
|
|
|
|
public ReadOnlySpan<char> Current => Segment;
|
|
|
|
public LineEnumerator(ReadOnlySpan<char> value)
|
|
{
|
|
Entire = value;
|
|
Segment = ReadOnlySpan<char>.Empty;
|
|
Final = false;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
Segment = ReadOnlySpan<char>.Empty;
|
|
Final = false;
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
if (Final)
|
|
{
|
|
return false;
|
|
}
|
|
else if (Segment == ReadOnlySpan<char>.Empty)
|
|
{
|
|
int index = Entire.IndexOf('\n');
|
|
if (index == -1)
|
|
{
|
|
Segment = Entire;
|
|
}
|
|
else
|
|
{
|
|
Segment = Entire.Slice(0, index);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Entire.Overlaps(Segment, out int offset);
|
|
|
|
if (offset + Segment.Length >= Entire.Length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ReadOnlySpan<char> rest = Entire.Slice(offset + Segment.Length + 1);
|
|
|
|
int index = rest.IndexOf('\n');
|
|
if (index == -1)
|
|
{
|
|
Segment = rest;
|
|
Final = true;
|
|
}
|
|
else
|
|
{
|
|
Segment = rest.Slice(0, index);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static QVec2 MeasureHorizontal(ReadOnlySpan<char> str, float size, QFont font)
|
|
{
|
|
var enumerator = new LineEnumerator(str);
|
|
|
|
float width = 0.0f;
|
|
float height = 0.0f;
|
|
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ReadOnlySpan<char> line = enumerator.Current;
|
|
|
|
float lineHeight = 0.0f;
|
|
|
|
foreach (Rune r in line.EnumerateRunes())
|
|
{
|
|
int codepoint = r.Value;
|
|
font.Get(codepoint, size, out FontGlyph glyph);
|
|
|
|
width += glyph.Metrics.Advance.X;
|
|
lineHeight = Math.Max(lineHeight, glyph.Metrics.Size.Y);
|
|
}
|
|
|
|
height += lineHeight;
|
|
}
|
|
|
|
return new QVec2(width, height);
|
|
}
|
|
|
|
public static void TypesetHorizontalDirect(this CommandList list, ReadOnlySpan<char> str, QVec2 origin, float size, QFont font)
|
|
{
|
|
Dictionary<QImage, FontDrawInfo> drawInfo = new Dictionary<QImage, FontDrawInfo>();
|
|
var enumerator = new LineEnumerator(str);
|
|
|
|
QVec2 pen = origin;
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ReadOnlySpan<char> line = enumerator.Current;
|
|
float rise = 0.0f;
|
|
float fall = 0.0f;
|
|
|
|
// Find out all the code pages required, and the line height.
|
|
foreach (Rune r in line.EnumerateRunes())
|
|
{
|
|
int codepoint = r.Value;
|
|
font.Get(codepoint, size, out FontGlyph glyph);
|
|
float crise = glyph.Metrics.HorizontalBearing.Y;
|
|
float cfall = glyph.Metrics.Size.Y - crise;
|
|
|
|
rise = Math.Max(crise, rise);
|
|
fall = Math.Max(cfall, fall);
|
|
}
|
|
|
|
pen += new QVec2(0, rise);
|
|
|
|
foreach (Rune r in line.EnumerateRunes())
|
|
{
|
|
FontDrawInfo info;
|
|
int codepoint = r.Value;
|
|
|
|
font.Get(codepoint, size, out FontGlyph glyph);
|
|
ref readonly QGlyphMetrics metrics = ref glyph.Metrics;
|
|
QImage? image = glyph.Image;
|
|
|
|
if (image == null)
|
|
{
|
|
pen += new QVec2(metrics.Advance.X, 0);
|
|
continue;
|
|
}
|
|
|
|
if (!drawInfo.TryGetValue(image, out info))
|
|
{
|
|
info = new FontDrawInfo();
|
|
info.Image = image;
|
|
info.rectangles = new List<QRectangle>();
|
|
drawInfo[image] = info;
|
|
}
|
|
|
|
QRectangle dest = new QRectangle(
|
|
pen + new QVec2(metrics.HorizontalBearing.X + metrics.Size.X, metrics.Size.Y - metrics.HorizontalBearing.Y),
|
|
pen + new QVec2(metrics.HorizontalBearing.X, -metrics.HorizontalBearing.Y));
|
|
|
|
info.rectangles.Add(dest);
|
|
info.rectangles.Add(glyph.UVs);
|
|
|
|
pen.X += metrics.Advance.X;
|
|
}
|
|
|
|
pen.X = origin.X;
|
|
pen.Y += fall;
|
|
}
|
|
|
|
// Now for each rectangle we can dispatch draw calls.
|
|
foreach (FontDrawInfo info in drawInfo.Values)
|
|
{
|
|
list.Image(info.Image, info.rectangles.ToArray(), true);
|
|
}
|
|
}
|
|
|
|
private struct FontDrawInfo
|
|
{
|
|
public QImage Image;
|
|
public List<QRectangle> rectangles;
|
|
}
|
|
}
|
|
}
|