using System.Diagnostics; using System.Numerics; using BlurgText; using Dashboard.Drawing; using Dashboard.Pal; namespace Dashboard.BlurgText { public interface IBlurgDcExtensionFactory { public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc); } public class BlurgTextExtension(IBlurgDcExtensionFactory dcExtensionFactory) : IApplicationExtension, IFontLoader { private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate); public Application Context { get; private set; } = null!; public string DriverName { get; } = "BlurgText"; public string DriverVendor { get; } = "Dashbord and BlurgText"; public Version DriverVersion { get; } = new Version(1, 0); public IBlurgDcExtensionFactory DcExtensionFactory { get; } = dcExtensionFactory; IContextBase IContextExtensionBase.Context => Context; public bool IsDisposed { get; private set; } = false; public void Require(Application context) { Context = context; context.DeviceContextCreated += OnDeviceContextCreated; } void IContextExtensionBase.Require(IContextBase context) => Require((Application)context); private void RequireDeviceContextExtension(DeviceContext dc) { dc.ExtensionPreload(() => DcExtensionFactory.CreateExtension(this, dc)); } private void OnDeviceContextCreated(object? sender, DeviceContext dc) { RequireDeviceContextExtension(dc); } public void Dispose() { if (IsDisposed) return; IsDisposed = true; _blurg.Dispose(); Context.DeviceContextCreated -= OnDeviceContextCreated; } private static void GlobalTextureUpdate(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 BlurgText.", "Dashboard/BlurgEngine"); } private static IntPtr GlobalTextureAllocation(int width, int height) { Debug.WriteLine("Attempt to create or update a texture from the global BlurgText.", "Dashboard/BlurgEngine"); return IntPtr.Zero; } public IFont Load(FontInfo info) { BlurgFont? font = _blurg.QueryFont( info.Family, info.Weight switch { FontWeight._100 => global::BlurgText.FontWeight.Thin, FontWeight._200 => global::BlurgText.FontWeight.ExtraLight, FontWeight._300 => global::BlurgText.FontWeight.Light, FontWeight._500 => global::BlurgText.FontWeight.Medium, FontWeight._600 => global::BlurgText.FontWeight.SemiBold, FontWeight._700 => global::BlurgText.FontWeight.Bold, FontWeight._800 => global::BlurgText.FontWeight.ExtraBold, FontWeight._900 => global::BlurgText.FontWeight.Black, _ => global::BlurgText.FontWeight.Regular, }, info.Slant switch { FontSlant.Oblique or FontSlant.Italic => true, _ => false, }); if (font == null) throw new Exception("Font not found."); return new BlurgFontProxy(_blurg, font); } public IFont Load(string path) { BlurgFont? font = _blurg.AddFontFile(path); if (font == null) throw new Exception("Font not found."); return new BlurgFontProxy(_blurg, font); } public IFont Load(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(); return Load(path); } } public abstract class BlurgDcExtension : IDeviceContextExtension { public abstract Blurg Blurg { get; } public abstract string DriverName { get; } public abstract string DriverVendor { get; } public abstract Version DriverVersion { get; } public Application Application => Context.Application; public DeviceContext Context { get; private set; } = null!; public abstract void DrawBlurgResult(BlurgResult result, Vector3 position); public void DrawBlurgFormattedText(BlurgFormattedText text, Vector3 position, float width = 0f) { BlurgResult? result = Blurg.BuildFormattedText(text, maxWidth: width); if (result == null) throw new Exception("Could not build formatted text result."); DrawBlurgResult(result, position); } public abstract void Dispose(); IContextBase IContextExtensionBase.Context => Context; public void Require(DeviceContext context) { Context = context; } void IContextExtensionBase.Require(IContextBase context) => Require((DeviceContext)context); } }