189 lines
4.3 KiB
C#
189 lines
4.3 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace MoonWorks.Graphics.Font.MSDF;
|
|
|
|
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;
|
|
|
|
public GraphicsDevice GraphicsDevice;
|
|
public Font Font;
|
|
|
|
public Buffer VertexBuffer;
|
|
public Buffer IndexBuffer;
|
|
|
|
public byte* VertexData;
|
|
public byte* IndexData;
|
|
|
|
private int CurrentVertexDataLengthInBytes;
|
|
private int CurrentIndexDataLengthInBytes;
|
|
|
|
private byte* StringBytes;
|
|
private int StringBytesCount = 16;
|
|
|
|
private bool IsDisposed;
|
|
|
|
public TextBatch(GraphicsDevice graphicsDevice)
|
|
{
|
|
GraphicsDevice = graphicsDevice;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
VertexBuffer = Buffer.Create<FontVertex>(GraphicsDevice, BufferUsageFlags.Vertex, MAX_VERTICES);
|
|
IndexBuffer = Buffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, MAX_INDICES);
|
|
VertexData = (byte*) NativeMemory.Alloc((nuint) (MAX_VERTICES * Unsafe.SizeOf<FontVertex>()));
|
|
IndexData = (byte*) NativeMemory.Alloc((nuint) (MAX_INDICES * Unsafe.SizeOf<uint>()));
|
|
StringBytes = (byte*) NativeMemory.Alloc(128);
|
|
}
|
|
|
|
public void AddText(
|
|
string text,
|
|
float x,
|
|
float y,
|
|
float depth,
|
|
Color color,
|
|
HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left,
|
|
VerticalAlignment verticalAlignment = VerticalAlignment.Baseline
|
|
) {
|
|
uint decodeState;
|
|
uint codepoint;
|
|
|
|
var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
|
|
|
|
if (StringBytesCount < byteCount)
|
|
{
|
|
StringBytes = (byte*) NativeMemory.Realloc(StringBytes, (nuint) byteCount);
|
|
StringBytesCount = byteCount;
|
|
}
|
|
|
|
fixed (char* chars = text)
|
|
{
|
|
var bytesWritten = System.Text.Encoding.UTF8.GetBytes(chars, text.Length, StringBytes, byteCount);
|
|
|
|
y += GetVerticalAlignOffset(verticalAlignment);
|
|
|
|
if (horizontalAlignment == HorizontalAlignment.Right)
|
|
{
|
|
// TODO: check text bounds to adjust alignment
|
|
}
|
|
|
|
for (var i = 0; i < bytesWritten; i += 1)
|
|
{
|
|
if (Decoder.Decode(&decodeState, &codepoint, StringBytes[i]) > 0)
|
|
{
|
|
if (decodeState == 1)
|
|
{
|
|
// Something went wrong!
|
|
return;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// TODO: we need to convert AtlasData so that codepoints are looked up by key
|
|
if (IsWhitespace(codepoint))
|
|
{
|
|
x += GetHorizontalAdvance(codepoint);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// TODO: draw the rest of the owl
|
|
}
|
|
}
|
|
|
|
// Call this after you have made all the Draw calls you want.
|
|
public void UploadBufferData(CommandBuffer commandBuffer)
|
|
{
|
|
if (CurrentVertexDataLengthInBytes > 0 && CurrentIndexDataLengthInBytes > 0)
|
|
{
|
|
commandBuffer.SetBufferData(VertexBuffer, (nint) VertexData, 0, (uint) CurrentVertexDataLengthInBytes);
|
|
commandBuffer.SetBufferData(IndexBuffer, (nint) IndexData, 0, (uint) CurrentIndexDataLengthInBytes);
|
|
}
|
|
}
|
|
|
|
private float GetHorizontalAdvance(uint codepoint)
|
|
{
|
|
Font.AtlasData.Glyphs
|
|
}
|
|
|
|
private float GetVerticalAlignOffset(VerticalAlignment verticalAlignment)
|
|
{
|
|
switch (verticalAlignment)
|
|
{
|
|
case VerticalAlignment.Baseline:
|
|
return 0;
|
|
|
|
case VerticalAlignment.Top:
|
|
return Font.AtlasData.Metrics.Ascender;
|
|
|
|
case VerticalAlignment.Middle:
|
|
return (Font.AtlasData.Metrics.Ascender + Font.AtlasData.Metrics.Descender) / 2f;
|
|
|
|
case VerticalAlignment.Bottom:
|
|
return Font.AtlasData.Metrics.Descender;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private static bool IsWhitespace(uint codepoint)
|
|
{
|
|
switch (codepoint)
|
|
{
|
|
case 0x0020:
|
|
case 0x00A0:
|
|
case 0x1680:
|
|
case 0x202F:
|
|
case 0x205F:
|
|
case 0x3000:
|
|
return true;
|
|
|
|
default:
|
|
if (codepoint > 0x2000 && codepoint <= 0x200A)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!IsDisposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
VertexBuffer.Dispose();
|
|
IndexBuffer.Dispose();
|
|
}
|
|
|
|
NativeMemory.Free(VertexData);
|
|
NativeMemory.Free(IndexData);
|
|
NativeMemory.Free(StringBytes);
|
|
|
|
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);
|
|
}
|
|
}
|