2022-04-13 22:10:23 +00:00
|
|
|
using System;
|
|
|
|
using System.IO;
|
2023-04-05 07:47:02 +00:00
|
|
|
using System.Runtime.InteropServices;
|
2022-04-13 22:10:23 +00:00
|
|
|
using WellspringCS;
|
|
|
|
|
|
|
|
namespace MoonWorks.Graphics.Font
|
|
|
|
{
|
2023-12-15 18:46:43 +00:00
|
|
|
public unsafe class Font : GraphicsResource
|
|
|
|
{
|
|
|
|
public Texture Texture { get; }
|
|
|
|
public float PixelsPerEm { get; }
|
|
|
|
public float DistanceRange { get; }
|
2022-04-13 22:10:23 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
internal IntPtr Handle { get; }
|
2022-04-13 22:10:23 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
private byte* StringBytes;
|
|
|
|
private int StringBytesLength;
|
2023-04-05 07:47:02 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns></returns>
|
|
|
|
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<byte>(fontFileByteBuffer, (int) fontFileStream.Length);
|
|
|
|
fontFileStream.ReadExactly(fontFileByteSpan);
|
|
|
|
fontFileStream.Close();
|
2023-04-05 07:47:02 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
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();
|
2022-04-13 22:10:23 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
var handle = Wellspring.Wellspring_CreateFont(
|
|
|
|
(IntPtr) fontFileByteBuffer,
|
|
|
|
(uint) fontFileByteSpan.Length,
|
|
|
|
(IntPtr) atlasFileByteBuffer,
|
|
|
|
(uint) atlasFileByteSpan.Length,
|
|
|
|
out float pixelsPerEm,
|
|
|
|
out float distanceRange
|
|
|
|
);
|
|
|
|
|
2024-02-23 08:06:04 +00:00
|
|
|
var imagePath = Path.ChangeExtension(fontPath, ".png");
|
|
|
|
ImageUtils.ImageInfoFromFile(imagePath, out var width, out var height, out var sizeInBytes);
|
2023-12-15 18:46:43 +00:00
|
|
|
|
2024-03-06 06:50:45 +00:00
|
|
|
var uploader = new ResourceUploader(graphicsDevice);
|
|
|
|
var texture = uploader.CreateTexture2DFromCompressed(imagePath);
|
|
|
|
uploader.Upload();
|
|
|
|
uploader.Dispose();
|
2024-02-23 08:06:04 +00:00
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
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)
|
2022-04-13 22:10:23 +00:00
|
|
|
{
|
2023-12-15 18:46:43 +00:00
|
|
|
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)
|
2022-04-13 22:10:23 +00:00
|
|
|
{
|
2023-12-15 18:46:43 +00:00
|
|
|
StringBytes = (byte*) NativeMemory.Realloc(StringBytes, (nuint) byteCount);
|
2022-04-13 22:10:23 +00:00
|
|
|
}
|
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
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;
|
2022-04-13 22:10:23 +00:00
|
|
|
}
|
|
|
|
|
2023-12-15 18:46:43 +00:00
|
|
|
protected override void Dispose(bool disposing)
|
2022-04-13 22:10:23 +00:00
|
|
|
{
|
2023-12-15 18:46:43 +00:00
|
|
|
if (!IsDisposed)
|
|
|
|
{
|
|
|
|
if (disposing)
|
|
|
|
{
|
|
|
|
Texture.Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
Wellspring.Wellspring_DestroyFont(Handle);
|
|
|
|
}
|
|
|
|
base.Dispose(disposing);
|
2022-04-13 22:10:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|