using System; using System.Runtime.InteropServices; using WellspringCS; namespace MoonWorks.Graphics.Font { public unsafe class TextBatch : IDisposable { public const int MAX_CHARS = 4096; public const int MAX_VERTICES = MAX_CHARS * 4; public const int MAX_INDICES = MAX_CHARS * 6; private GraphicsDevice GraphicsDevice { get; } public IntPtr Handle { get; } public Buffer VertexBuffer { get; protected set; } = null; public Buffer IndexBuffer { get; protected set; } = null; public uint PrimitiveCount { get; protected set; } public Font CurrentFont { get; private set; } private byte* StringBytes; private int StringBytesLength; private bool IsDisposed; public TextBatch(GraphicsDevice graphicsDevice) { GraphicsDevice = graphicsDevice; Handle = Wellspring.Wellspring_CreateTextBatch(); StringBytesLength = 128; StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); VertexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, MAX_VERTICES); IndexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Index, MAX_INDICES); } public void Start(Font font) { Wellspring.Wellspring_StartTextBatch(Handle, font.Handle); CurrentFont = font; PrimitiveCount = 0; } public unsafe bool Add( string text, float x, float y, int pixelSize, float depth, Color color, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Baseline ) { 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_Draw( Handle, x, y, pixelSize, depth, new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A }, (Wellspring.HorizontalAlignment) horizontalAlignment, (Wellspring.VerticalAlignment) verticalAlignment, (IntPtr) StringBytes, (uint) byteCount ); if (result == 0) { Logger.LogWarn("Could not decode string: " + text); return false; } } return true; } // Call this after you have made all the Draw calls you want. public unsafe void UploadBufferData(CommandBuffer commandBuffer) { Wellspring.Wellspring_GetBufferData( Handle, out uint vertexCount, out IntPtr vertexDataPointer, out uint vertexDataLengthInBytes, out IntPtr indexDataPointer, out uint indexDataLengthInBytes ); if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0) { commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes); commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes); } PrimitiveCount = vertexCount / 2; // FIXME: is this jank? } protected virtual void Dispose(bool disposing) { if (!IsDisposed) { if (disposing) { VertexBuffer.Dispose(); IndexBuffer.Dispose(); } NativeMemory.Free(StringBytes); Wellspring.Wellspring_DestroyTextBatch(Handle); IsDisposed = true; } } ~TextBatch() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: false); } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } } }