restructure for wellspring API update
parent
8af0d3399b
commit
77948dbeda
|
@ -5,29 +5,107 @@ using WellspringCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font
|
namespace MoonWorks.Graphics.Font
|
||||||
{
|
{
|
||||||
public class Font : IDisposable
|
public unsafe class Font : IDisposable
|
||||||
{
|
{
|
||||||
public IntPtr Handle { get; }
|
public Texture Texture { get; }
|
||||||
|
|
||||||
|
internal IntPtr Handle { get; }
|
||||||
|
|
||||||
|
private byte* StringBytes;
|
||||||
|
private int StringBytesLength;
|
||||||
|
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
public unsafe Font(string path)
|
public unsafe static Font Load(
|
||||||
{
|
GraphicsDevice graphicsDevice,
|
||||||
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
CommandBuffer commandBuffer,
|
||||||
var fileByteBuffer = NativeMemory.Alloc((nuint) fileStream.Length);
|
string fontPath
|
||||||
var fileByteSpan = new Span<byte>(fileByteBuffer, (int) fileStream.Length);
|
) {
|
||||||
fileStream.ReadExactly(fileByteSpan);
|
var fontFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read);
|
||||||
fileStream.Close();
|
var fontFileByteBuffer = NativeMemory.Alloc((nuint) fontFileStream.Length);
|
||||||
|
var fontFileByteSpan = new Span<byte>(fontFileByteBuffer, (int) fontFileStream.Length);
|
||||||
|
fontFileStream.ReadExactly(fontFileByteSpan);
|
||||||
|
fontFileStream.Close();
|
||||||
|
|
||||||
Handle = Wellspring.Wellspring_CreateFont((IntPtr) fileByteBuffer, (uint) fileByteSpan.Length);
|
var atlasFileStream = new FileStream(Path.ChangeExtension(fontPath, ".json"), FileMode.Open, FileAccess.Read);
|
||||||
|
var atlasFileByteBuffer = NativeMemory.Alloc((nuint) atlasFileStream.Length);
|
||||||
|
var atlasFileByteSpan = new Span<byte>(atlasFileByteBuffer, (int) atlasFileStream.Length);
|
||||||
|
atlasFileStream.ReadExactly(atlasFileByteSpan);
|
||||||
|
atlasFileStream.Close();
|
||||||
|
|
||||||
NativeMemory.Free(fileByteBuffer);
|
var handle = Wellspring.Wellspring_CreateFont(
|
||||||
}
|
(IntPtr) fontFileByteBuffer,
|
||||||
|
(uint) fontFileByteSpan.Length,
|
||||||
|
(IntPtr) atlasFileByteBuffer,
|
||||||
|
(uint) atlasFileByteSpan.Length
|
||||||
|
);
|
||||||
|
|
||||||
|
var texture = Texture.FromImageFile(graphicsDevice, commandBuffer, Path.ChangeExtension(fontPath, ".png"));
|
||||||
|
|
||||||
|
NativeMemory.Free(fontFileByteBuffer);
|
||||||
|
NativeMemory.Free(atlasFileByteBuffer);
|
||||||
|
|
||||||
|
return new Font(handle, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Font(IntPtr handle, Texture texture)
|
||||||
|
{
|
||||||
|
Handle = handle;
|
||||||
|
Texture = texture;
|
||||||
|
|
||||||
|
StringBytesLength = 32;
|
||||||
|
StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool TextBounds(
|
||||||
|
string text,
|
||||||
|
float x,
|
||||||
|
float y,
|
||||||
|
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,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
(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 virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!IsDisposed)
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Texture.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Wellspring.Wellspring_DestroyFont(Handle);
|
Wellspring.Wellspring_DestroyFont(Handle);
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
|
@ -35,8 +113,8 @@ namespace MoonWorks.Graphics.Font
|
||||||
|
|
||||||
~Font()
|
~Font()
|
||||||
{
|
{
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
Dispose(disposing: false);
|
Dispose(disposing: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font
|
|
||||||
{
|
|
||||||
/* UTF-8 Decoder */
|
|
||||||
|
|
||||||
/* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
|
||||||
* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static class Decoder
|
|
||||||
{
|
|
||||||
static byte[] utf8d = new byte[] {
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
|
||||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
|
||||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
|
||||||
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
|
||||||
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
|
||||||
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
|
||||||
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
|
||||||
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
|
||||||
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
|
|
||||||
};
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static unsafe uint Decode(uint* state, uint* codep, uint dbyte) {
|
|
||||||
uint type = utf8d[dbyte];
|
|
||||||
|
|
||||||
*codep = (uint) ((*state != 0) ?
|
|
||||||
(dbyte & 0x3fu) | (*codep << 6) :
|
|
||||||
(0xff >> (int) type) & (dbyte));
|
|
||||||
|
|
||||||
*state = utf8d[256 + *state*16 + type];
|
|
||||||
return *state;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font.MSDF;
|
|
||||||
|
|
||||||
public class Font : IDisposable
|
|
||||||
{
|
|
||||||
public Texture Texture { get; }
|
|
||||||
internal AtlasData AtlasData;
|
|
||||||
|
|
||||||
static JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
IncludeFields = true
|
|
||||||
};
|
|
||||||
static AtlasDataContext Context = new AtlasDataContext(SerializerOptions);
|
|
||||||
|
|
||||||
private bool IsDisposed;
|
|
||||||
|
|
||||||
public static Font Load(
|
|
||||||
GraphicsDevice graphicsDevice,
|
|
||||||
CommandBuffer commandBuffer,
|
|
||||||
string jsonPath
|
|
||||||
) {
|
|
||||||
var atlasData = (AtlasData) JsonSerializer.Deserialize(File.ReadAllText(jsonPath), typeof(AtlasData), Context);
|
|
||||||
var imagePath = Path.ChangeExtension(jsonPath, ".png");
|
|
||||||
var texture = Texture.FromImageFile(graphicsDevice, commandBuffer, imagePath);
|
|
||||||
|
|
||||||
return new Font(texture, atlasData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Font(Texture texture, AtlasData atlasData)
|
|
||||||
{
|
|
||||||
Texture = texture;
|
|
||||||
AtlasData = atlasData;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!IsDisposed)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Texture.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
IsDisposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using MoonWorks.Math.Float;
|
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font.MSDF;
|
|
||||||
|
|
||||||
[JsonSerializable(typeof(AtlasData))]
|
|
||||||
internal partial class AtlasDataContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads from an atlas generated by msdf-atlas-gen
|
|
||||||
public struct AtlasData
|
|
||||||
{
|
|
||||||
public Atlas Atlas;
|
|
||||||
public Metrics Metrics;
|
|
||||||
public Glyph[] Glyphs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Atlas
|
|
||||||
{
|
|
||||||
public FieldType Type;
|
|
||||||
public int DistanceRange;
|
|
||||||
public int Size;
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public Origin YOrigin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Metrics
|
|
||||||
{
|
|
||||||
public int EmSize;
|
|
||||||
public float LineHeight;
|
|
||||||
public float Ascender;
|
|
||||||
public float Descender;
|
|
||||||
public float UnderlineY;
|
|
||||||
public float UnderlineThickness;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Glyph
|
|
||||||
{
|
|
||||||
public uint Unicode;
|
|
||||||
public float Advance;
|
|
||||||
public Bounds PlaneBounds;
|
|
||||||
public Bounds AtlasBounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Bounds
|
|
||||||
{
|
|
||||||
public float Left;
|
|
||||||
public float Bottom;
|
|
||||||
public float Right;
|
|
||||||
public float Top;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum FieldType
|
|
||||||
{
|
|
||||||
Hardmask,
|
|
||||||
Softmask,
|
|
||||||
SDF,
|
|
||||||
PSDF,
|
|
||||||
MSDF,
|
|
||||||
MTSDF
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Origin
|
|
||||||
{
|
|
||||||
Top,
|
|
||||||
Bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FontVertex : IVertexType
|
|
||||||
{
|
|
||||||
public Vector3 Position;
|
|
||||||
public Vector2 TexCoord;
|
|
||||||
public Color Color;
|
|
||||||
|
|
||||||
private static readonly VertexElementFormat[] vertexElementFormats = new VertexElementFormat[]
|
|
||||||
{
|
|
||||||
VertexElementFormat.Vector3,
|
|
||||||
VertexElementFormat.Vector2,
|
|
||||||
VertexElementFormat.Color
|
|
||||||
};
|
|
||||||
|
|
||||||
public static VertexElementFormat[] Formats => vertexElementFormats;
|
|
||||||
}
|
|
|
@ -1,188 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using WellspringCS;
|
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font
|
|
||||||
{
|
|
||||||
public class Packer : IDisposable
|
|
||||||
{
|
|
||||||
public IntPtr Handle { get; }
|
|
||||||
public Texture Texture { get; }
|
|
||||||
|
|
||||||
public Font Font { get; }
|
|
||||||
|
|
||||||
private byte[] StringBytes;
|
|
||||||
|
|
||||||
private bool IsDisposed;
|
|
||||||
|
|
||||||
public unsafe Packer(GraphicsDevice graphicsDevice, Font font, float fontSize, uint textureWidth, uint textureHeight, uint padding = 1)
|
|
||||||
{
|
|
||||||
Font = font;
|
|
||||||
Handle = Wellspring.Wellspring_CreatePacker(Font.Handle, fontSize, textureWidth, textureHeight, 0, padding);
|
|
||||||
Texture = Texture.CreateTexture2D(graphicsDevice, textureWidth, textureHeight, TextureFormat.R8, TextureUsageFlags.Sampler);
|
|
||||||
StringBytes = new byte[128];
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe bool PackFontRanges(params FontRange[] fontRanges)
|
|
||||||
{
|
|
||||||
fixed (FontRange *pFontRanges = &fontRanges[0])
|
|
||||||
{
|
|
||||||
var nativeSize = fontRanges.Length * Marshal.SizeOf<Wellspring.FontRange>();
|
|
||||||
var result = Wellspring.Wellspring_PackFontRanges(Handle, (IntPtr) pFontRanges, (uint) fontRanges.Length);
|
|
||||||
return result > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetTextureData(CommandBuffer commandBuffer)
|
|
||||||
{
|
|
||||||
var pixelDataPointer = Wellspring.Wellspring_GetPixelDataPointer(Handle);
|
|
||||||
commandBuffer.SetTextureData(Texture, pixelDataPointer, Texture.Width * Texture.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void TextBounds(
|
|
||||||
string text,
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
HorizontalAlignment horizontalAlignment,
|
|
||||||
VerticalAlignment verticalAlignment,
|
|
||||||
out Wellspring.Rectangle rectangle
|
|
||||||
) {
|
|
||||||
var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
|
|
||||||
|
|
||||||
if (StringBytes.Length < byteCount)
|
|
||||||
{
|
|
||||||
System.Array.Resize(ref StringBytes, byteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (char* chars = text)
|
|
||||||
fixed (byte* bytes = StringBytes)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, bytes, byteCount);
|
|
||||||
Wellspring.Wellspring_TextBounds(
|
|
||||||
Handle,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
(Wellspring.HorizontalAlignment) horizontalAlignment,
|
|
||||||
(Wellspring.VerticalAlignment) verticalAlignment,
|
|
||||||
(IntPtr) bytes,
|
|
||||||
(uint) byteCount,
|
|
||||||
out rectangle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!IsDisposed)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Texture.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Wellspring.Wellspring_DestroyPacker(Handle);
|
|
||||||
|
|
||||||
IsDisposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~Packer()
|
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,15 +3,6 @@ using MoonWorks.Math.Float;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font
|
namespace MoonWorks.Graphics.Font
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct FontRange
|
|
||||||
{
|
|
||||||
public uint FirstCodepoint;
|
|
||||||
public uint NumChars;
|
|
||||||
public byte OversampleH;
|
|
||||||
public byte OversampleV;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct Vertex
|
public struct Vertex
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,35 +1,49 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using WellspringCS;
|
using WellspringCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics.Font
|
namespace MoonWorks.Graphics.Font
|
||||||
{
|
{
|
||||||
public class TextBatch
|
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; }
|
private GraphicsDevice GraphicsDevice { get; }
|
||||||
public IntPtr Handle { get; }
|
public IntPtr Handle { get; }
|
||||||
|
|
||||||
public Buffer VertexBuffer { get; protected set; } = null;
|
public Buffer VertexBuffer { get; protected set; } = null;
|
||||||
public Buffer IndexBuffer { get; protected set; } = null;
|
public Buffer IndexBuffer { get; protected set; } = null;
|
||||||
public Texture Texture { get; protected set; }
|
|
||||||
public uint PrimitiveCount { get; protected set; }
|
public uint PrimitiveCount { get; protected set; }
|
||||||
|
|
||||||
private byte[] StringBytes;
|
private Font CurrentFont;
|
||||||
|
|
||||||
|
private byte* StringBytes;
|
||||||
|
private int StringBytesLength;
|
||||||
|
|
||||||
|
private bool IsDisposed;
|
||||||
|
|
||||||
public TextBatch(GraphicsDevice graphicsDevice)
|
public TextBatch(GraphicsDevice graphicsDevice)
|
||||||
{
|
{
|
||||||
GraphicsDevice = graphicsDevice;
|
GraphicsDevice = graphicsDevice;
|
||||||
Handle = Wellspring.Wellspring_CreateTextBatch();
|
Handle = Wellspring.Wellspring_CreateTextBatch();
|
||||||
StringBytes = new byte[128];
|
|
||||||
|
StringBytesLength = 128;
|
||||||
|
StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength);
|
||||||
|
|
||||||
|
VertexBuffer = Buffer.Create<Vertex>(GraphicsDevice, BufferUsageFlags.Vertex, MAX_VERTICES);
|
||||||
|
IndexBuffer = Buffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, MAX_INDICES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Packer packer)
|
public void Start(Font font)
|
||||||
{
|
{
|
||||||
Wellspring.Wellspring_StartTextBatch(Handle, packer.Handle);
|
Wellspring.Wellspring_StartTextBatch(Handle, font.Handle);
|
||||||
Texture = packer.Texture;
|
CurrentFont = font;
|
||||||
PrimitiveCount = 0;
|
PrimitiveCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Draw(
|
public unsafe bool Add(
|
||||||
string text,
|
string text,
|
||||||
float x,
|
float x,
|
||||||
float y,
|
float y,
|
||||||
|
@ -40,15 +54,14 @@ namespace MoonWorks.Graphics.Font
|
||||||
) {
|
) {
|
||||||
var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
|
var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
|
||||||
|
|
||||||
if (StringBytes.Length < byteCount)
|
if (StringBytesLength < byteCount)
|
||||||
{
|
{
|
||||||
System.Array.Resize(ref StringBytes, byteCount);
|
StringBytes = (byte*) NativeMemory.Realloc(StringBytes, (nuint) byteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (char* chars = text)
|
fixed (char* chars = text)
|
||||||
fixed (byte* bytes = StringBytes)
|
|
||||||
{
|
{
|
||||||
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, bytes, byteCount);
|
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, StringBytes, byteCount);
|
||||||
|
|
||||||
var result = Wellspring.Wellspring_Draw(
|
var result = Wellspring.Wellspring_Draw(
|
||||||
Handle,
|
Handle,
|
||||||
|
@ -58,15 +71,18 @@ namespace MoonWorks.Graphics.Font
|
||||||
new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
|
new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
|
||||||
(Wellspring.HorizontalAlignment) horizontalAlignment,
|
(Wellspring.HorizontalAlignment) horizontalAlignment,
|
||||||
(Wellspring.VerticalAlignment) verticalAlignment,
|
(Wellspring.VerticalAlignment) verticalAlignment,
|
||||||
(IntPtr) bytes,
|
(IntPtr) StringBytes,
|
||||||
(uint) byteCount
|
(uint) byteCount
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
throw new System.ArgumentException("Could not decode string!");
|
Logger.LogWarn("Could not decode string: " + text);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this after you have made all the Draw calls you want.
|
// Call this after you have made all the Draw calls you want.
|
||||||
|
@ -81,26 +97,6 @@ namespace MoonWorks.Graphics.Font
|
||||||
out uint indexDataLengthInBytes
|
out uint indexDataLengthInBytes
|
||||||
);
|
);
|
||||||
|
|
||||||
if (VertexBuffer == null)
|
|
||||||
{
|
|
||||||
VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
|
|
||||||
}
|
|
||||||
else if (VertexBuffer.Size < vertexDataLengthInBytes)
|
|
||||||
{
|
|
||||||
VertexBuffer.Dispose();
|
|
||||||
VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IndexBuffer == null)
|
|
||||||
{
|
|
||||||
IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
|
|
||||||
}
|
|
||||||
else if (IndexBuffer.Size < indexDataLengthInBytes)
|
|
||||||
{
|
|
||||||
IndexBuffer.Dispose();
|
|
||||||
IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
|
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
|
||||||
{
|
{
|
||||||
commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
|
commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
|
||||||
|
@ -109,5 +105,34 @@ namespace MoonWorks.Graphics.Font
|
||||||
|
|
||||||
PrimitiveCount = vertexCount / 2; // FIXME: is this jank?
|
PrimitiveCount = vertexCount / 2; // FIXME: is this jank?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
VertexBuffer.Dispose();
|
||||||
|
IndexBuffer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue