using System;
using System.Numerics;
using System.Text;
using Robust.Client.ResourceManagement;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.Graphics
{
///
/// A generic font for rendering of text.
/// Does not contain properties such as size. Those are specific to children such as
///
public abstract class Font
{
///
/// The maximum amount a glyph goes above the baseline, in pixels.
///
public abstract int GetAscent(float scale);
///
/// The maximum glyph height of a line of text in pixels, not relative to the baseline.
///
public abstract int GetHeight(float scale);
///
/// The maximum amount a glyph drops below the baseline, in pixels.
///
public abstract int GetDescent(float scale);
///
/// The distance between the baselines of two consecutive lines, in pixels.
/// Basically, if you encounter a new line, this is how much you need to move down the cursor.
///
public abstract int GetLineHeight(float scale);
///
/// The distance between the edges of two consecutive lines, in pixels.
///
public int GetLineSeparation(float scale)
{
return GetLineHeight(scale) - GetHeight(scale);
}
///
/// Draw a character at a certain baseline position on screen.
///
/// The drawing handle to draw to.
/// The Unicode code point to draw.
/// The baseline from which to draw the character.
/// DPI scale factor to render the font at.
/// The color of the character to draw.
/// If the character is not available, render "�" instead.
/// How much to advance the cursor to draw the next character.
public abstract float DrawChar(
DrawingHandleBase handle, Rune rune, Vector2 baseline, float scale,
Color color, bool fallback=true);
///
/// Gets metrics describing the dimensions and positioning of a single glyph in the font.
///
/// The unicode codepoint to fetch the glyph metrics for.
/// DPI scale factor to render the font at.
///
/// If the character is not available, return data for "�" instead.
/// This can still fail if the font does not define � itself.
///
///
/// null if this font does not have a glyph for the specified character,
/// otherwise the metrics you asked for.
///
///
public abstract CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true);
///
/// Try-pattern version of .
///
public bool TryGetCharMetrics(Rune rune, float scale, out CharMetrics metrics, bool fallback=true)
{
var maybe = GetCharMetrics(rune, scale);
if (maybe.HasValue)
{
metrics = maybe.Value;
return true;
}
metrics = default;
return false;
}
}
///
/// Font type that renders vector fonts such as OTF and TTF fonts from a
///
public sealed class VectorFont : Font
{
public int Size { get; }
internal IFontInstanceHandle Handle { get; }
public VectorFont(FontResource res, int size)
{
Size = size;
Handle = IoCManager.Resolve().MakeInstance(res.FontFaceHandle, size);
}
public override int GetAscent(float scale) => Handle.GetAscent(scale);
public override int GetHeight(float scale) => Handle.GetHeight(scale);
public override int GetDescent(float scale) => Handle.GetDescent(scale);
public override int GetLineHeight(float scale) => Handle.GetLineHeight(scale);
public override float DrawChar(DrawingHandleBase handle, Rune rune, Vector2 baseline, float scale, Color color, bool fallback=true)
{
var metrics = Handle.GetCharMetrics(rune, scale);
if (!metrics.HasValue)
{
if (fallback && !Rune.IsWhiteSpace(rune))
{
rune = new Rune('�');
metrics = Handle.GetCharMetrics(rune, scale);
if (!metrics.HasValue)
return 0;
}
else
return 0;
}
var texture = Handle.GetCharTexture(rune, scale);
if (texture == null)
{
return metrics.Value.Advance;
}
baseline += new Vector2(metrics.Value.BearingX, -metrics.Value.BearingY);
if(handle is DrawingHandleWorld worldhandle)
worldhandle.DrawTextureRect(texture, Box2.FromDimensions(baseline, texture.Size), color);
else
handle.DrawTexture(texture, baseline, color);
return metrics.Value.Advance;
}
public override CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true)
{
var metrics = Handle.GetCharMetrics(rune, scale);
if (metrics == null && !Rune.IsWhiteSpace(rune) && fallback)
return Handle.GetCharMetrics(new Rune('�'), scale);
return metrics;
}
}
public sealed class StackedFont : Font
{
// _main is the "default" font; the top of the Stack.
public readonly Font _main;
public readonly Font[] Stack;
public StackedFont(params Font[] args)
{
if (args.Length < 1)
throw new ArgumentException("At least one font is required");
Stack = args;
_main = args[0];
}
// All metrics methods use the default font (_main).
// Technically these could vary between stacked fonts, but that is a case
// that really should already be avoided for so many other reasons.
public override int GetAscent(float scale) => _main.GetAscent(scale);
public override int GetHeight(float scale) => _main.GetHeight(scale);
public override int GetDescent(float scale) => _main.GetDescent(scale);
public override int GetLineHeight(float scale) => _main.GetLineHeight(scale);
// DrawChar just proxies to the stack, or invokes _main's fallback.
public override float DrawChar(DrawingHandleBase handle, Rune rune, Vector2 baseline, float scale, Color color, bool fallback=true)
{
foreach (var f in Stack)
{
var w = f.DrawChar(handle, rune, baseline, scale, color, fallback: false);
if (w != 0f)
return w;
}
if (fallback)
return _main.DrawChar(handle, rune, baseline, scale, color, fallback: true);
return 0f;
}
public override CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true)
{
foreach (var f in Stack)
{
var m = f.GetCharMetrics(rune, scale, fallback: false);
if (m != null)
return m;
}
if (!Rune.IsWhiteSpace(rune) && fallback)
return _main.GetCharMetrics(rune, scale, fallback: true);
return null;
}
}
public sealed class DummyFont : Font
{
public override int GetAscent(float scale) => default;
public override int GetHeight(float scale) => default;
public override int GetDescent(float scale) => default;
public override int GetLineHeight(float scale) => default;
public override float DrawChar(DrawingHandleBase handle, Rune rune, Vector2 baseline, float scale, Color color, bool fallback=true)
{
// Nada, it's a dummy after all.
return 0;
}
public override CharMetrics? GetCharMetrics(Rune rune, float scale, bool fallback=true)
{
// Nada, it's a dummy after all.
return null;
}
}
}