using BlurgText; using OpenTK.Graphics.OpenGL; using OPENGL = OpenTK.Graphics.OpenGL; namespace Dashboard.Drawing.OpenGL.Text { public class BlurgEngine : IResourceManager, IGLDisposable { public string Name { get; } = "BlurgEngine"; public Blurg Blurg { get; } public bool SystemFontsEnabled { get; } private readonly List _textures = new List(); public BlurgEngine() { Blurg = new Blurg(AllocateTexture, UpdateTexture); SystemFontsEnabled = Blurg.EnableSystemFonts(); } ~BlurgEngine() { Dispose(false, true); } public DbBlurgFont AddFont(string path) { BlurgFont? font = Blurg.AddFontFile(path) ?? throw new Exception("Failed to load the font file."); return new DbBlurgFont(font, 12f); } public DbBlurgFont AddFont(Stream stream) { string path; Stream dest; for (int i = 0;; i++) { path = Path.GetTempFileName(); try { dest = File.Open(path, FileMode.CreateNew, FileAccess.Write, FileShare.None); } catch (IOException ex) { if (i < 3) continue; else throw new Exception("Could not open a temporary file for writing the font.", ex); } break; } stream.CopyTo(dest); dest.Dispose(); DbBlurgFont? font = AddFont(path); File.Delete(path); return font; } public DbBlurgFont? QueryFont(string family, FontWeight weight, FontSlant slant, FontStretch stretch) { // Ignore the stretch argument. bool italic = slant != FontSlant.Normal; BlurgFont? font = Blurg.QueryFont(family, new BlurgText.FontWeight((int)weight), italic); if (font != null) return new DbBlurgFont(font, 12f); return null; } private void UpdateTexture(IntPtr texture, IntPtr buffer, int x, int y, int width, int height) { GL.BindTexture(TextureTarget.Texture2d, (int)texture); GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer); // GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Red, PixelType.Byte, buffer); } private IntPtr AllocateTexture(int width, int height) { int texture = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2d, texture); GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgba, width, height, 0, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); // GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.R8, width, height, 0, OPENGL.PixelFormat.Red, PixelType.Byte, IntPtr.Zero); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); // GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.One); // GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.One); // GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.One); // GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.Red); _textures.Add(texture); return texture; } private bool _isDisposed = false; private void Dispose(bool disposing, bool safeExit) { if (_isDisposed) return; _isDisposed = true; if (disposing) { Blurg.Dispose(); GC.SuppressFinalize(this); } if (safeExit) { foreach (int texture in _textures) ContextCollector.Global.DeleteTexture(texture); } } public void Dispose() => Dispose(true, true); public void Dispose(bool safeExit) => Dispose(true, safeExit); } }