using System.Diagnostics; using System.Drawing; using System.Numerics; using BlurgText; using Dashboard.OpenGL; using OpenTK.Graphics.OpenGL; using OPENGL = OpenTK.Graphics.OpenGL; namespace Dashboard.Drawing.OpenGL.Text { public class BlurgEngine : IResourceManager, IGLDisposable, ITypeSetter { public string Name { get; } = "BlurgEngine"; public Blurg Blurg { get; } public bool SystemFontsEnabled { get; } private readonly List _textures = new List(); public BlurgEngine() : this(false) { } private BlurgEngine(bool global) { if (global) Blurg = new Blurg(AllocateTextureGlobal, UpdateTextureGlobal); else Blurg = new Blurg(AllocateTexture, UpdateTexture); SystemFontsEnabled = Blurg.EnableSystemFonts(); } ~BlurgEngine() { Dispose(false, true); } public SizeF MeasureString(IFont font, string value) { return MeasureStringInternal(InternFont(font), value); } private SizeF MeasureStringInternal(DbBlurgFont font, string value) { Vector2 v = Blurg.MeasureString(font.Font, font.Size, value); return new SizeF(v.X, v.Y); } public IFont LoadFont(Stream stream) { string path; Stream dest; for (int i = 0;; i++) { path = Path.GetTempFileName(); try { dest = File.Open(path, FileMode.OpenOrCreate, 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 = (DbBlurgFont)LoadFont(path); return font; } public IFont LoadFont(string path) { BlurgFont? font = Blurg.AddFontFile(path) ?? throw new Exception("Failed to load the font file."); return new DbBlurgFont(Blurg, font, 12f) { Path = path }; } public IFont LoadFont(NamedFont font) { // Ignore the stretch argument. bool italic = font.Slant != FontSlant.Normal; BlurgFont? loaded = Blurg.QueryFont(font.Family, new BlurgText.FontWeight((int)font.Weight), italic); if (loaded != null) return new DbBlurgFont(Blurg, loaded, 12f); else throw new Exception("Font not found."); } public DbBlurgFont InternFont(IFont font) { if (font is NamedFont named) { return (DbBlurgFont)LoadFont(named); } else if (font is DbBlurgFont dblurg) { if (dblurg.Owner != Blurg) { if (dblurg.Path == null) return (DbBlurgFont)LoadFont(new NamedFont(dblurg.Family, dblurg.Size, dblurg.Weight, dblurg.Slant, dblurg.Stretch)); else return (DbBlurgFont)LoadFont(dblurg.Path); } else { return dblurg; } } else { throw new Exception("Unsupported font resource."); } } 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); /// /// The global Blurg engine implements the needed methods for command queues to work. /// public static BlurgEngine Global { get; } = new BlurgEngine(true); private static void UpdateTextureGlobal(IntPtr userdata, IntPtr buffer, int x, int y, int width, int height) { // Report the user error. Debug.WriteLine("Attempt to create or update a texture from the global BlurgEngine.", "Dashboard/BlurgEngine"); } private static IntPtr AllocateTextureGlobal(int width, int height) { Debug.WriteLine("Attempt to create or update a texture from the global BlurgEngine.", "Dashboard/BlurgEngine"); return IntPtr.Zero; } } }