Optimized Font Loading

System fonts only load if no glyph is found in the already loaded fonts.

fixes #2
This commit is contained in:
JacobTech 2023-01-08 11:13:58 -05:00
parent 7d32666095
commit 4a958ae060
4 changed files with 231 additions and 44 deletions

View File

@ -10,7 +10,7 @@
<IncludeSymbols>False</IncludeSymbols>
<RepositoryUrl>https://git.jacobtech.com/JacobTech.com/GraphicsManager</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>1.0.0-beta1</Version>
<Version>1.0.1-beta01</Version>
</PropertyGroup>
<ItemGroup>

View File

@ -1,25 +1,48 @@
using System.Diagnostics;
using System.Reflection;
using System.Security.Cryptography;
using SharpFont;
namespace GraphicsManager.Objects.Core;
public class Font
{
private static List<Face>? System = null;
private List<Face> _Faces = new();
private Library lib;
internal static Library lib = new();
public IReadOnlyList<Face> Fonts => _Faces.AsReadOnly();
internal static Dictionary<string, Face> AllFileFonts = new();
internal static Dictionary<byte[], Face> AllMemoryFonts = new();
internal static List<string>? _SystemPre = null;
private static List<Font> AllFonts = new();
private static bool Backup = false;
internal Font()
internal void AddSystemFontFace(string path)
{
Console.WriteLine("Added font: " + path);
if (!AllFileFonts.ContainsKey(path)) AllFileFonts.Add(path, new(lib, path, 0));
if (!_Faces.Contains(AllFileFonts[path]))
{
foreach (Font ft in AllFonts)
{
ft._Faces.Add(AllFileFonts[path]);
}
}
}
private Font()
{
Name = null!;
Assembly = null!;
Embeded = false;
if (lib is null) lib = new();
//TODO add more systemfont dection methods
if (System is null)
if (!Backup)
{
System = new();
AllMemoryFonts.Add(SHA256.HashData(Tools.GetResourceBytes("GraphicsManager.Resources.Fonts.TektonPro-Regular.otf")), new Face(lib, Tools.GetResourceBytes("GraphicsManager.Resources.Fonts.TektonPro-Regular.otf"), 0));
Backup = true;
}
if (_SystemPre is null)
{
_SystemPre = new();
if (OperatingSystem.IsLinux())
{
try
@ -38,7 +61,7 @@ public class Font
string[] files = proc.StandardOutput.ReadToEnd().Split($": {Environment.NewLine}");
for (int i = 0; i < files.Length; i++)
{
System!.Add(new Face(lib, files[i], 0));
_SystemPre.Add(files[i]);
}
}
catch
@ -46,11 +69,7 @@ public class Font
}
}
}
for (int i = 0; i < System!.Count; i++) _Faces.Add(System![i]);
//TODO add a reserved backup font
}
public IReadOnlyList<Face> Faces => _Faces.AsReadOnly();
public void SetEmbeddedFont(string Font, Assembly? Assembly = null)
{
_ = Font ?? throw new ArgumentNullException(nameof(Font));
@ -59,9 +78,14 @@ public class Font
byte[] f = (Assembly is null
? Tools.GetResourceBytes(Base + Font)
: Tools.GetResourceBytes(Assembly!, $"{Base}{Font}"));
if (HasTopFont) _Faces[0] = new Face(lib, f, 0);
else _Faces.Insert(0, new Face(lib, f, 0));
HasTopFont = true;
byte[] hash = SHA256.HashData(f);
if (AllMemoryFonts.ContainsKey(hash)) AllMemoryFonts.Add(hash, new(lib, f, 0));
if (!_Faces.Contains(AllMemoryFonts[hash])) _Faces.Insert(0, AllMemoryFonts[hash]);
else
{
_Faces.Remove(AllMemoryFonts[hash]);
_Faces.Insert(0, AllMemoryFonts[hash]);
}
this.Assembly = Assembly;
this.Embeded = true;
this.Name = Font;
@ -77,17 +101,28 @@ public class Font
Name = Font,
HasTopFont = true
};
AllFonts.Add(fontclass);
string Base = "GraphicsManager.Resources.Fonts.";
if (Assembly is not null) Base = string.Empty;
byte[] f = (Assembly is null
? Tools.GetResourceBytes(Base + Font)
: Tools.GetResourceBytes(Assembly!, $"{Base}{Font}"));
fontclass._Faces.Insert(0, new Face(fontclass.lib, f, 0));
byte[] hash = SHA256.HashData(f);
if (AllMemoryFonts.ContainsKey(hash)) AllMemoryFonts.Add(hash, new(lib, f, 0));
fontclass._Faces.Insert(0, AllMemoryFonts[hash]);
return fontclass;
}
public static Font MakeFontFromSystem(uint PixelHeight = 20) => new Font() { PixelHeight = PixelHeight};
private static Font? Cache = null;
public static Font MakeFontFromSystem(uint PixelHeight = 20)
{
if (Cache is null)
{
Cache = new() { PixelHeight = PixelHeight };
AllFonts.Add(Cache);
}
return Cache;
}
public static Font MakeFontFromFile(string Font)
{
_ = Font ?? throw new ArgumentNullException(nameof(Font));
@ -98,7 +133,8 @@ public class Font
Name = Font,
HasTopFont = true
};
fontclass._Faces.Insert(0, new Face(fontclass.lib, Font, 0));
AllFonts.Add(fontclass);
fontclass._Faces.Insert(0, new Face(lib, Font, 0));
return fontclass;
}

View File

@ -1,12 +1,24 @@
using GraphicsManager.Structs;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Graphics.ES30;
using OpenTK.Mathematics;
using OpenTK.Windowing.Desktop;
using SharpFont;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using GL = OpenTK.Graphics.OpenGL4.GL;
using Image = SixLabors.ImageSharp.Image;
using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat;
using PixelInternalFormat = OpenTK.Graphics.OpenGL4.PixelInternalFormat;
using PixelStoreParameter = OpenTK.Graphics.OpenGL4.PixelStoreParameter;
using PixelType = OpenTK.Graphics.OpenGL4.PixelType;
using TextureMagFilter = OpenTK.Graphics.OpenGL4.TextureMagFilter;
using TextureMinFilter = OpenTK.Graphics.OpenGL4.TextureMinFilter;
using TextureParameterName = OpenTK.Graphics.OpenGL4.TextureParameterName;
using TextureTarget = OpenTK.Graphics.OpenGL4.TextureTarget;
using TextureUnit = OpenTK.Graphics.OpenGL4.TextureUnit;
using TextureWrapMode = OpenTK.Graphics.OpenGL4.TextureWrapMode;
using VertexAttribPointerType = OpenTK.Graphics.OpenGL4.VertexAttribPointerType;
namespace GraphicsManager.Objects.Core;
@ -46,35 +58,35 @@ public class Texture
{
}
internal static Character GetChar(Font l, char charter, Face[] faces)
internal static Character GetChar(Font l, char charter)
{
Character t = new();
for (int i = 0; i < faces.Length; i++)
int last = 0;
for (int i = 0; i < l.Fonts.Count; i++)
{
try
{
try
{
faces[i].SetPixelSizes(0, l.PixelHeight);
l.Fonts[i].SetPixelSizes(0, l.PixelHeight);
last = i;
}
catch (Exception e)
{
continue;
}
faces[i].SelectCharmap(Encoding.Unicode);
l.Fonts[i].SelectCharmap(Encoding.Unicode);
ushort temp = ((ushort)charter);
if (faces[i].GetCharIndex(temp) == 0) continue;
faces[i].LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph = faces[i].Glyph;
if (l.Fonts[i].GetCharIndex(temp) == 0) continue;
l.Fonts[i].LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph = l.Fonts[i].Glyph;
FTBitmap bitmap = glyph.Bitmap;
t = new()
return new()
{
Size = new Vector2(bitmap.Width, bitmap.Rows),
Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop),
Advance = glyph.Advance.X.Value,
};
return t;
}
catch (Exception ex)
{
@ -82,14 +94,65 @@ public class Texture
continue;
}
}
for (int i = 0; i < Font._SystemPre.Count; i++)
{
try
{
Face tmp = new(Font.lib, Font._SystemPre[i], 0);
try
{
tmp.SetPixelSizes(0, l.PixelHeight);
}
catch (Exception e)
{
tmp.Dispose();
continue;
}
tmp.SelectCharmap(Encoding.Unicode);
ushort temp2 = ((ushort)charter);
if (tmp.GetCharIndex(temp2) == 0)
{
tmp.Dispose();
continue;
}
tmp.LoadChar(temp2, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph2 = tmp.Glyph;
FTBitmap bitmap2 = glyph2.Bitmap;
l.AddSystemFontFace(Font._SystemPre[i]);
return new()
{
Size = new Vector2(bitmap2.Width, bitmap2.Rows),
Bearing = new Vector2(glyph2.BitmapLeft, glyph2.BitmapTop),
Advance = glyph2.Advance.X.Value,
};
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
l.Fonts[last].SetPixelSizes(0, l.PixelHeight);
l.Fonts[last].SelectCharmap(Encoding.Unicode);
ushort temp22 = ((ushort)charter);
l.Fonts[last].LoadChar(temp22, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph22 = l.Fonts[0].Glyph;
FTBitmap bitmap22 = glyph22.Bitmap;
return new()
{
Size = new Vector2(bitmap22.Width, bitmap22.Rows),
Bearing = new Vector2(glyph22.BitmapLeft, glyph22.BitmapTop),
Advance = (int)glyph22.Advance.X.Value,
};
return t;
}
internal static Texture TextureForChar(IGLFWGraphicsContext con, Font l, char charter, Face[] faces)
internal static Texture TextureForChar(IGLFWGraphicsContext con, Font l, char charter)
{
Texture t = new();
for (int i = 0; i < faces.Length; i++)
Texture? t = null;
int last = 0;
for (int i = 0; i < l.Fonts.Count; i++)
{
try
{
@ -97,7 +160,8 @@ public class Texture
if (Label._characters[con][l].ContainsKey(charter)) return Label._characters[con][l][(ushort)charter].Texture;
try
{
faces[i].SetPixelSizes(0, l.PixelHeight);
l.Fonts[i].SetPixelSizes(0, l.PixelHeight);
last = i;
}
catch (Exception e)
{
@ -108,13 +172,13 @@ public class Texture
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.ActiveTexture(TextureUnit.Texture0);
faces[i].SelectCharmap(Encoding.Unicode);
l.Fonts[i].SelectCharmap(Encoding.Unicode);
ushort temp = ((ushort)charter);
if (faces[i].GetCharIndex(temp) == 0) continue;
faces[i].LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph = faces[i].Glyph;
if (l.Fonts[i].GetCharIndex(temp) == 0) continue;
l.Fonts[i].LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph = l.Fonts[i].Glyph;
FTBitmap bitmap = glyph.Bitmap;
t = new();
t.handel = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, t.handel);
GL.TexImage2D(TextureTarget.Texture2D, 0,
@ -131,6 +195,7 @@ public class Texture
};
Label._characters[con][l].Add(temp, cha);
return t;
}
catch (Exception ex)
{
@ -139,6 +204,91 @@ public class Texture
}
}
if (t is null)
{
for (int i = 0; i < Font._SystemPre.Count; i++)
{
try
{
Face tmp = new(Font.lib, Font._SystemPre[i], 0);
try
{
tmp.SetPixelSizes(0, l.PixelHeight);
}
catch (Exception e)
{
tmp.Dispose();
continue;
}
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.ActiveTexture(TextureUnit.Texture0);
tmp.SelectCharmap(Encoding.Unicode);
ushort temp2 = ((ushort)charter);
if (tmp.GetCharIndex(temp2) == 0)
{
tmp.Dispose();
continue;
}
tmp.LoadChar(temp2, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph2 = tmp.Glyph;
FTBitmap bitmap2 = glyph2.Bitmap;
l.AddSystemFontFace(Font._SystemPre[i]);
t = new();
t.handel = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, t.handel);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.R8, bitmap2.Width, bitmap2.Rows, 0,
PixelFormat.Red, PixelType.UnsignedByte, bitmap2.Buffer);
Character cha2 = new()
{
Size = new Vector2(bitmap2.Width, bitmap2.Rows),
Bearing = new Vector2(glyph2.BitmapLeft, glyph2.BitmapTop),
Advance = (int)glyph2.Advance.X.Value,
Texture = t,
};
Label._characters[con][l].Add(temp2, cha2);
return t;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
if (!Label._characters[con].ContainsKey(l)) Label._characters[con].Add(l, new Dictionary<uint, Character>());
if (Label._characters[con][l].ContainsKey(charter)) return Label._characters[con][l][(ushort)charter].Texture;
l.Fonts[last].SetPixelSizes(0, l.PixelHeight);
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.ActiveTexture(TextureUnit.Texture0);
l.Fonts[last].SelectCharmap(Encoding.Unicode);
ushort temp = ((ushort)charter);
l.Fonts[last].LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
GlyphSlot glyph = l.Fonts[0].Glyph;
FTBitmap bitmap = glyph.Bitmap;
if (t is null) t = new();
else return t;
t.handel = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, t.handel);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0,
PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer);
Character cha = new()
{
Size = new Vector2(bitmap.Width, bitmap.Rows),
Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop),
Advance = (int)glyph.Advance.X.Value,
Texture = t,
};
Label._characters[con][l].Add(temp, cha);
}
return t;
}

View File

@ -15,7 +15,7 @@ namespace GraphicsManager.Objects;
public class Label : IRenderObject
{
public static readonly Dictionary<IGLFWGraphicsContext, Shader> DefaultTextShader = new();
public static readonly Font DefaultFont = new();
public static readonly Font DefaultFont = Font.MakeFontFromSystem();
public ContextMenu? ContextMenu { get; set; } = null;
public IParent? Parent { get; private set; }
public ObjectAnchor Anchor { get; set; } = ObjectAnchor.Left | ObjectAnchor.Top;
@ -74,7 +74,7 @@ public class Label : IRenderObject
character = value[i];
else
character = PasswordChar.Value;
Character cha = Texture.GetChar(Font, character, Font.Faces.ToArray());
Character cha = Texture.GetChar(Font, character);
if (character == '\n')
{
@ -155,9 +155,10 @@ public class Label : IRenderObject
c = PasswordChar.Value;
if (!_characters[Window!.Context][Font].ContainsKey(c))
{
var f = Texture.TextureForChar(Window!.Context, Font, c, Font.Faces.ToArray());
var f = Texture.TextureForChar(Window!.Context, Font, c);
f.LoadText();
}
if (!_characters[Window!.Context][Font].ContainsKey(c)) continue;
Character ch = _characters[Window!.Context][Font][c];
int maxx = 0;
if (c == '\n')