182 lines
5.7 KiB
C#
182 lines
5.7 KiB
C#
using Quik.CommandMachine;
|
|
using Quik.Media;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Quik.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;
|
|
}
|
|
}
|
|
}
|