using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Quik.Media; using Quik.Media.Font; namespace Quik.Media { /// /// Abstract class that represents a font. /// public abstract class QFont : IDisposable { public abstract FontFace Face { get; } public string Family => Face.Family; public FontSlant Slant => Face.Slant; public FontWeight Weight => Face.Weight; public FontStretch Stretch => Face.Stretch; public abstract bool HasRune(int rune); protected abstract QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options); private readonly Dictionary _atlasses = new Dictionary(); public void Get(int codepoint, float size, out FontGlyph glyph) { SizedFontCollection collection; if (!_atlasses.TryGetValue(size, out collection)) { collection = new SizedFontCollection(size); _atlasses.Add(size, collection); } collection.Get(codepoint, out glyph, this); } // IDisposable private bool isDisposed = false; private void DisposePrivate(bool disposing) { if (isDisposed) return; Dispose(disposing); isDisposed = true; } protected virtual void Dispose(bool disposing) { } public void Dispose() => DisposePrivate(true); private class SizedFontCollection { public float Size { get; } private readonly Dictionary glyphs = new Dictionary(); private readonly FontAtlas atlas; public SizedFontCollection(float size) { Size = size; QuikApplication.Current.Platform.GetMaximumImage(out int height, out int width); // Do no allow to create a texture that is greater than 16 square characters at 200 DPI. width = Math.Min(width, (int)(size * 200 * 16)); height = Math.Min(height, (int)(size * 200 * 16)); // width = height = 256; atlas = new FontAtlas(width, height, QuikApplication.Current.FontProvider.RasterizerOptions.Sdf); } public void Get(int codepoint, out FontGlyph glyph, QFont font) { if (glyphs.TryGetValue(codepoint, out glyph)) return; QImage image = font.Render( out QGlyphMetrics metrics, codepoint, Size, QuikApplication.Current.FontProvider.RasterizerOptions); if (image != null) { image.LockBits2d(out QImageLock l, QImageLockOptions.Default); atlas.PutGlyph(codepoint, l, out FontAtlasGlyphInfo glyphInfo); image.UnlockBits(); image.Dispose(); glyph = new FontGlyph(codepoint, glyphInfo.Image, metrics, glyphInfo.UVs); } else { glyph = new FontGlyph(codepoint, null, metrics, default); } glyphs[codepoint] = glyph; } } } public readonly struct FontGlyph { public readonly int CodePoint; public readonly QImage Image; public readonly QGlyphMetrics Metrics; public readonly QRectangle UVs; public FontGlyph(int codepoint, QImage image, in QGlyphMetrics metrics, in QRectangle uvs) { CodePoint = codepoint; Image = image; Metrics = metrics; UVs = uvs; } } public struct FontRasterizerOptions { public float Resolution { get; set; } public bool Sdf { get; set; } public static readonly FontRasterizerOptions Default = new FontRasterizerOptions() { Resolution = 96.0f, Sdf = false }; } }