using System; using System.IO; using System.Runtime.InteropServices; using WellspringCS; namespace MoonWorks.Graphics.Font { public unsafe class Font : GraphicsResource { public Texture Texture { get; } public float PixelsPerEm { get; } public float DistanceRange { get; } internal IntPtr Handle { get; } private byte* StringBytes; private int StringBytesLength; /// /// Loads a TTF or OTF font from a path for use in MSDF rendering. /// Note that there must be an msdf-atlas-gen JSON and image file alongside. /// /// public unsafe static Font Load( GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, string fontPath ) { var fontFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read); var fontFileByteBuffer = NativeMemory.Alloc((nuint) fontFileStream.Length); var fontFileByteSpan = new Span(fontFileByteBuffer, (int) fontFileStream.Length); fontFileStream.ReadExactly(fontFileByteSpan); fontFileStream.Close(); var atlasFileStream = new FileStream(Path.ChangeExtension(fontPath, ".json"), FileMode.Open, FileAccess.Read); var atlasFileByteBuffer = NativeMemory.Alloc((nuint) atlasFileStream.Length); var atlasFileByteSpan = new Span(atlasFileByteBuffer, (int) atlasFileStream.Length); atlasFileStream.ReadExactly(atlasFileByteSpan); atlasFileStream.Close(); var handle = Wellspring.Wellspring_CreateFont( (IntPtr) fontFileByteBuffer, (uint) fontFileByteSpan.Length, (IntPtr) atlasFileByteBuffer, (uint) atlasFileByteSpan.Length, out float pixelsPerEm, out float distanceRange ); var texture = Texture.FromImageFile(graphicsDevice, commandBuffer, Path.ChangeExtension(fontPath, ".png")); NativeMemory.Free(fontFileByteBuffer); NativeMemory.Free(atlasFileByteBuffer); return new Font(graphicsDevice, handle, texture, pixelsPerEm, distanceRange); } private Font(GraphicsDevice device, IntPtr handle, Texture texture, float pixelsPerEm, float distanceRange) : base(device) { Handle = handle; Texture = texture; PixelsPerEm = pixelsPerEm; DistanceRange = distanceRange; StringBytesLength = 32; StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); } public unsafe bool TextBounds( string text, int pixelSize, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, out Wellspring.Rectangle rectangle ) { var byteCount = System.Text.Encoding.UTF8.GetByteCount(text); if (StringBytesLength < byteCount) { StringBytes = (byte*) NativeMemory.Realloc(StringBytes, (nuint) byteCount); } fixed (char* chars = text) { System.Text.Encoding.UTF8.GetBytes(chars, text.Length, StringBytes, byteCount); var result = Wellspring.Wellspring_TextBounds( Handle, pixelSize, (Wellspring.HorizontalAlignment) horizontalAlignment, (Wellspring.VerticalAlignment) verticalAlignment, (IntPtr) StringBytes, (uint) byteCount, out rectangle ); if (result == 0) { Logger.LogWarn("Could not decode string: " + text); return false; } } return true; } protected override void Dispose(bool disposing) { if (!IsDisposed) { if (disposing) { Texture.Dispose(); } Wellspring.Wellspring_DestroyFont(Handle); } base.Dispose(disposing); } } }