162 lines
4.4 KiB
C#
162 lines
4.4 KiB
C#
using Hexa.NET.ImGui;
|
|
|
|
namespace SDL3_TestingSuite.SDL3;
|
|
|
|
public struct FontMetrics
|
|
{
|
|
public ushort UnitsPerEm;
|
|
|
|
public short TypoAscender;
|
|
public short TypoDescender;
|
|
public short TypoLineGap;
|
|
|
|
public short WinAscent;
|
|
public short WinDescent;
|
|
|
|
public int TypoLineHeight;
|
|
public int WinLineHeight;
|
|
}
|
|
|
|
public static class FontFind
|
|
{
|
|
extension(ImGuiIOPtr io)
|
|
{
|
|
public unsafe ImFontPtr AddFont(string fontPath)
|
|
{
|
|
try
|
|
{
|
|
if (fontPath == null || io.Handle == null) return null;
|
|
FileInfo info = new FileInfo(fontPath);
|
|
if (!info.Exists || info.Length <= 0) return null;
|
|
|
|
FontMetrics metrics = FontFind.ReadFontMetrics(fontPath);
|
|
float size = FontFind.GetRecommendedPixelSize(metrics);
|
|
|
|
ImFontConfigPtr config = ImGui.ImFontConfig();
|
|
config.FontLoaderFlags |= (uint)ImGuiFreeTypeLoaderFlags.LoadColor;
|
|
ImFontPtr font = io.Fonts.AddFontFromFileTTF(fontPath, size, config);
|
|
Program.Logger.Log("Added font: " + Path.GetFileName(fontPath));
|
|
return font;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
}
|
|
return null;
|
|
}
|
|
public unsafe bool RemoveFont(string? fontName)
|
|
{
|
|
if (fontName == null || io.Handle == null) return false;
|
|
|
|
bool flag = false;
|
|
ImVector<ImFontPtr> fonts = io.Fonts.Fonts;
|
|
List<ImFontPtr> toRemove = new List<ImFontPtr>();
|
|
|
|
for (int i = 0; i < fonts.Size; i++)
|
|
{
|
|
ImFontPtr font = fonts[i];
|
|
if (font.GetDebugNameS() == fontName)
|
|
{
|
|
toRemove.Add(font);
|
|
flag = true;
|
|
}
|
|
}
|
|
|
|
foreach (ImFontPtr font in toRemove)
|
|
{
|
|
io.Fonts.RemoveFont(font);
|
|
Program.Logger.Log("Removed font: " + fontName);
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
}
|
|
|
|
public static FontMetrics ReadFontMetrics(string fontPath)
|
|
{
|
|
byte[] data = File.ReadAllBytes(fontPath);
|
|
|
|
ushort numTables = ReadU16Be(4);
|
|
const int tableDir = 12;
|
|
|
|
int headOffset = 0;
|
|
int os2Offset = 0;
|
|
|
|
for (int i = 0; i < numTables; i++)
|
|
{
|
|
int entry = tableDir + i * 16;
|
|
string tag = ReadTag(entry);
|
|
|
|
uint offset = ReadU32Be(entry + 8);
|
|
|
|
if (tag == "head")
|
|
headOffset = (int)offset;
|
|
|
|
if (tag == "OS/2")
|
|
os2Offset = (int)offset;
|
|
}
|
|
|
|
FontMetrics metrics = new FontMetrics();
|
|
|
|
metrics.UnitsPerEm = ReadU16Be(headOffset + 18);
|
|
|
|
if (os2Offset != 0)
|
|
{
|
|
metrics.TypoAscender = ReadS16Be(os2Offset + 68);
|
|
metrics.TypoDescender = ReadS16Be(os2Offset + 70);
|
|
metrics.TypoLineGap = ReadS16Be(os2Offset + 72);
|
|
|
|
metrics.WinAscent = ReadS16Be(os2Offset + 74);
|
|
metrics.WinDescent = ReadS16Be(os2Offset + 76);
|
|
}
|
|
|
|
metrics.TypoLineHeight =
|
|
metrics.TypoAscender - metrics.TypoDescender + metrics.TypoLineGap;
|
|
|
|
metrics.WinLineHeight =
|
|
metrics.WinAscent + metrics.WinDescent;
|
|
|
|
return metrics;
|
|
|
|
ushort ReadU16Be(int o)
|
|
{
|
|
return (ushort)((data[o] << 8) | data[o + 1]);
|
|
}
|
|
|
|
short ReadS16Be(int o)
|
|
{
|
|
return (short)ReadU16Be(o);
|
|
}
|
|
|
|
uint ReadU32Be(int o)
|
|
{
|
|
return (uint)((data[o] << 24) | (data[o + 1] << 16) | (data[o + 2] << 8) | data[o + 3]);
|
|
}
|
|
|
|
string ReadTag(int o)
|
|
{
|
|
char c1 = (char)data[o];
|
|
char c2 = (char)data[o + 1];
|
|
char c3 = (char)data[o + 2];
|
|
char c4 = (char)data[o + 3];
|
|
return new string(new char[] { c1, c2, c3, c4 });
|
|
}
|
|
}
|
|
|
|
public static int GetRecommendedPixelSize(FontMetrics metrics)
|
|
{
|
|
if (metrics.UnitsPerEm == 0 || metrics.TypoLineHeight == 0)
|
|
return 16;
|
|
|
|
const float targetLineHeightPx = 18.0f;
|
|
|
|
float scale = targetLineHeightPx * metrics.UnitsPerEm / metrics.TypoLineHeight;
|
|
|
|
int size = (int)MathF.Round(scale);
|
|
|
|
if (size < 10) size = 10;
|
|
if (size > 72) size = 72;
|
|
|
|
return size;
|
|
}
|
|
} |