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, | ||||||
|  | 			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(); | ||||||
|  | 
 | ||||||
|  | 			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(); | ||||||
|  | 
 | ||||||
|  | 			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) | ||||||
| 		{ | 		{ | ||||||
| 	        var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); | 			Handle = handle; | ||||||
| 	        var fileByteBuffer = NativeMemory.Alloc((nuint) fileStream.Length); | 			Texture = texture; | ||||||
| 	        var fileByteSpan = new Span<byte>(fileByteBuffer, (int) fileStream.Length); |  | ||||||
| 	        fileStream.ReadExactly(fileByteSpan); |  | ||||||
| 	        fileStream.Close(); |  | ||||||
| 
 | 
 | ||||||
| 	        Handle = Wellspring.Wellspring_CreateFont((IntPtr) fileByteBuffer, (uint) fileByteSpan.Length); | 			StringBytesLength = 32; | ||||||
|  | 			StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 	        NativeMemory.Free(fileByteBuffer); | 		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; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -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