using System; using System.IO; using Quik.FreeType; using Quik.Media; using Quik.Media.Color; namespace Quik.Media.Defaults { public class QFontFreeType : QFont { private MemoryStream ms; private FTFace face; public override FontInfo Info => throw new NotImplementedException(); public QFontFreeType(Stream stream) { ms = new MemoryStream(); stream.CopyTo(ms); FTError e = FT.NewMemoryFace(Ft, ms.GetBuffer(), ms.Length, 0, out face); if (e != FTError.None) { throw new Exception("Could not load font face from stream."); } } public override bool HasRune(int rune) { return FT.GetCharIndex(face, (ulong)rune) != 0; } public override QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options) { FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution)); QGlyphMetrics[] allMetrics = new QGlyphMetrics[256]; // Figure out the map size needed. int pixels = 0; for (int i = 0; i < 256; i++) { uint index = FT.GetCharIndex(face, (ulong)(codepage + i)); FT.LoadGlyph(face, index, FTLoadFlags.Default); ref readonly FTGlyphMetrics metrics = ref face.Glyph.Metrics; allMetrics[i] = new QGlyphMetrics( codepage + i, new QVec2(metrics.Width, metrics.Height), new QVec2(metrics.HorizontalBearingX/64f, metrics.HorizontalBearingY/64f), new QVec2(metrics.VerticalBearingX/64f, metrics.VerticalBearingY/64f), new QVec2(metrics.HorizontalAdvance/64f, metrics.VerticalAdvance/64f) ); pixels = (int)Math.Max(pixels, Math.Max(face.Glyph.Metrics.Width/64f, face.Glyph.Metrics.Height/64f)); } int bits = Math.ILogB(pixels); if (1 << bits != pixels) { pixels = 1 << bits + 1; } // Now we can create a bitmap and render our glyphs. QImageBuffer buffer = new QImageBuffer(QImageFormat.RedU8, (int)pixels, (int)pixels, 256); for (int i = 0; i < 256; i++) { uint index = FT.GetCharIndex(face, (ulong)(codepage + i)); FT.LoadGlyph(face, index, FTLoadFlags.Default); FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Mono); ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap; buffer.LockBits3d(out QImageLock dst, QImageLockOptions.Default, i); if (bitmap.Buffer != IntPtr.Zero) unsafe { for (int j = 0; j < bitmap.Rows; j++) { Buffer.MemoryCopy( (byte*)bitmap.Buffer + (j * bitmap.Pitch), (byte*)dst.ImagePtr + (j * dst.Width), dst.Width, bitmap.Width); } } buffer.UnlockBits(); } return new QFontPage(this, codepage, size, options, buffer, allMetrics); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { ms.Dispose(); } FT.DoneFace(face); } private static FTLibrary Ft => FTProvider.Ft; } }