2
0
mirror of https://github.com/raylib-cs/raylib-cs synced 2025-09-09 03:01:41 -04:00

Update string handling

This commit is contained in:
2021-12-05 00:27:22 +11:00
parent 77288efc11
commit c31808190d
3 changed files with 353 additions and 13 deletions

View File

@@ -1,5 +1,6 @@
using System;
using Xunit;
using Raylib_cs;
namespace Raylib_cs.Tests
{
@@ -39,5 +40,26 @@ namespace Raylib_cs.Tests
Assert.True(BlittableHelper.IsBlittable<VrStereoConfig>());
Assert.True(BlittableHelper.IsBlittable<RenderBatch>());
}
[Fact]
public void Debug()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
Raylib.InitWindow(screenWidth, screenHeight, "Aaåäö");
while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.RAYWHITE);
Raylib.EndDrawing();
}
Raylib.CloseWindow();
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Security;
using System.Xml;
namespace Raylib_cs
{
@@ -75,7 +76,15 @@ namespace Raylib_cs
/// <summary>Initialize window and OpenGL context</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void InitWindow(int width, int height, [MarshalAs(UnmanagedType.LPUTF8Str)] string title);
public static extern void InitWindow(int width, int height, byte* title);
public static void InitWindow(int width, int height, Utf8String title)
{
fixed (byte* p = title)
{
InitWindow(width, height, p);
}
}
/// <summary>Check if KEY_ESCAPE pressed or Close icon pressed</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
@@ -147,7 +156,15 @@ namespace Raylib_cs
/// <summary>Set title for window (only PLATFORM_DESKTOP)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetWindowTitle([MarshalAs(UnmanagedType.LPUTF8Str)] string title);
public static extern void SetWindowTitle(byte* title);
public static void SetWindowTitle(Utf8String title)
{
fixed (byte* p = title)
{
SetWindowTitle(p);
}
}
/// <summary>Set window position on screen (only PLATFORM_DESKTOP)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
@@ -239,7 +256,15 @@ namespace Raylib_cs
/// <summary>Set clipboard text content</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetClipboardText([MarshalAs(UnmanagedType.LPUTF8Str)] string text);
public static extern void SetClipboardText(byte* text);
public static void SetClipboardText(Utf8String text)
{
fixed (byte* p = text)
{
SetClipboardText(p);
}
}
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
@@ -1101,11 +1126,26 @@ namespace Raylib_cs
/// <summary>Create an image from text (default font)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Image ImageText([MarshalAs(UnmanagedType.LPUTF8Str)] string text, int fontSize, Color color);
public static extern Image ImageText(byte* text, int fontSize, Color color);
public static Image ImageText(Utf8String text, int fontSize, Color color)
{
fixed (byte* p = text)
{
return ImageText(p, fontSize, color);
}
}
/// <summary>Create an image from text (custom sprite font)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Image ImageTextEx(Font font, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, float fontSize, float spacing, Color tint);
public static extern Image ImageTextEx(Font font, byte* text, float fontSize, float spacing, Color tint);
public static Image ImageTextEx(Font font, Utf8String text, float fontSize, float spacing, Color tint)
{
fixed (byte* p = text)
{
return ImageTextEx(font, p, fontSize, spacing, tint);
}
}
/// <summary>Convert image to POT (power-of-two)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
@@ -1273,13 +1313,28 @@ namespace Raylib_cs
/// <summary>Draw text (using default font) within an image (destination)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void ImageDrawText(ref Image dst, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, int x, int y, int fontSize, Color color);
public static extern void ImageDrawText(ref Image dst, byte* text, int x, int y, int fontSize, Color color);
public static void ImageDrawText(ref Image dst, Utf8String text, int x, int y, int fontSize, Color color)
{
fixed (byte* p = text)
{
ImageDrawText(ref dst, p, x,y,fontSize,color);
}
}
/// <summary>Draw text (custom sprite font) within an image (destination)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void ImageDrawTextEx(ref Image dst, Font font, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, Vector2 position, float fontSize, float spacing, Color tint);
public static extern void ImageDrawTextEx(ref Image dst, Font font, byte* text, Vector2 position, float fontSize, float spacing, Color tint);
public static void ImageDrawTextEx(ref Image dst, Font font, Utf8String text, Vector2 position, float fontSize, float spacing, Color tint)
{
fixed (byte* p = text)
{
ImageDrawTextEx(ref dst, font, p, position, fontSize, spacing, tint);
}
}
// Texture loading functions
// NOTE: These functions require GPU access
@@ -1469,15 +1524,42 @@ namespace Raylib_cs
/// <summary>Draw text (using default font)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void DrawText([MarshalAs(UnmanagedType.LPUTF8Str)] string text, int posX, int posY, int fontSize, Color color);
public static extern void DrawText(byte* text, int posX, int posY, int fontSize, Color color);
public static void DrawText(Utf8String text, int posX, int posY, int fontSize, Color color)
{
fixed (byte* p = text)
{
DrawText(p, posX, posY, fontSize, color);
}
}
/// <summary>Draw text using font and additional parameters</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void DrawTextEx(Font font, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, Vector2 position, float fontSize, float spacing, Color tint);
public static extern void DrawTextEx(Font font, byte* text, Vector2 position, float fontSize, float spacing, Color tint);
public static void DrawTextEx(Font font, Utf8String text, Vector2 position, float fontSize,
float spacing, Color tint)
{
fixed (byte* p = text)
{
DrawTextEx(font, p, position, fontSize, spacing, tint);
}
}
/// <summary>Draw text using Font and pro parameters (rotation)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void DrawTextPro(Font font, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, Vector2 position, float fontSize, float spacing, Color tint);
public static extern void DrawTextPro(Font font, byte* text, Vector2 position, float fontSize, float spacing, Color tint);
public static void DrawTextPro(Font font, Utf8String text, Vector2 position, float fontSize,
float spacing, Color tint)
{
fixed (byte* p = text)
{
DrawTextEx(font, p, position, fontSize, spacing, tint);
}
}
/// <summary>Draw one character (codepoint)</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
@@ -1488,11 +1570,27 @@ namespace Raylib_cs
/// <summary>Measure string width for default font</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int MeasureText([MarshalAs(UnmanagedType.LPUTF8Str)] string text, int fontSize);
public static extern int MeasureText(byte* text, int fontSize);
public static int MeasureText(Utf8String text, int fontSize)
{
fixed (byte* p = text)
{
return MeasureText(p, fontSize);
}
}
/// <summary>Measure string size for Font</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern Vector2 MeasureTextEx(Font font, [MarshalAs(UnmanagedType.LPUTF8Str)] string text, float fontSize, float spacing);
public static extern Vector2 MeasureTextEx(Font font, byte* text, float fontSize, float spacing);
public static Vector2 MeasureTextEx(Font font, Utf8String text, float fontSize, float spacing)
{
fixed (byte* p = text)
{
return MeasureTextEx(font, p, fontSize, spacing);
}
}
/// <summary>Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found</summary>
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]

220
Raylib-cs/Utf8String.cs Normal file
View File

@@ -0,0 +1,220 @@
using System;
using System.Text;
namespace Raylib_cs;
#region LICENSE
// ** -- TAKEN FROM SQLitePCL.raw -- ** \\
/* https://github.com/ericsink/SQLitePCL.raw */
/*
Copyright 2014-2021 SourceGear, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// CHANGES:
// - Update Method Names
// - Implicit Conversion From string
#endregion
/// <summary>
/// A raw string representation suitable for passing into many core SQLite apis. These will normally be pointers to
/// utf8 encoded bytes, with a trailing <c>\0</c> terminator. <see langword="null"/> strings can be represented as
/// well as empty strings.
/// </summary>
public readonly ref struct Utf8String
{
// this span will contain a zero terminator byte
// if sp.Length is 0, it represents a null string
// if sp.Length is 1, the only byte must be zero, and it is an empty string
readonly ReadOnlySpan<byte> sp;
public ref readonly byte GetPinnableReference()
{
return ref sp.GetPinnableReference();
}
private Utf8String(ReadOnlySpan<byte> a)
{
// no check here. anything that calls this
// constructor must make assurances about the
// zero terminator.
sp = a;
}
/// <summary>
/// Creates a new instance of <see cref="Utf8String"/> which directly points at the memory pointed to by <paramref
/// name="span"/>. The span must contain a valid <see cref="Encoding.UTF8"/> encoded block of memory that
/// terminates with a <c>\0</c> byte. The span passed in must include the <c>\0</c> terminator.
/// <para/>
/// Both <see langword="null"/> and empty strings can be created here. To create a <see langword="null"/> string,
/// pass in an empty <see cref="ReadOnlySpan{T}"/>. To create an empty string, pass in a span with length 1, that
/// only contains a <c>\0</c>
/// </summary>
/// <exception cref="ArgumentException">
/// Thrown if <c>span.Length > 0</c> and <c>span[^1]</c> is not <c>\0</c>.
/// </exception>
public static Utf8String FromSpan(ReadOnlySpan<byte> span)
{
if (
(span.Length > 0)
&& (span[^1] != 0)
)
{
throw new ArgumentException("zero terminator required");
}
return new Utf8String(span);
}
public static Utf8String FromString(string s)
{
if (s == null)
{
return new Utf8String(ReadOnlySpan<byte>.Empty);
}
else
{
return new Utf8String(s.ToUtf8String());
}
}
static unsafe long GetLength(byte* p)
{
var q = p;
while (*q != 0)
{
q++;
}
return q - p;
}
static unsafe ReadOnlySpan<byte> FindZeroTerminator(byte* p)
{
var len = (int)GetLength(p);
return new ReadOnlySpan<byte>(p, len + 1);
}
/// <summary>
/// Creates a new instance of <see cref="Utf8String"/> which directly points at the memory pointed to by <paramref
/// name="p"/>. The pointer must either be <see langword="null"/> or point to a valid <see
/// cref="Encoding.UTF8"/> encoded block of memory that terminates with a <c>\0</c> byte.
/// </summary>
public static unsafe Utf8String FromPtr(byte* p)
{
if (p == null)
{
return new Utf8String(ReadOnlySpan<byte>.Empty);
}
else
{
return new Utf8String(FindZeroTerminator(p));
}
}
// TODO maybe remove this and just use FromSpan?
/// <summary>
/// Creates a new instance of <see cref="Utf8String"/> which directly points at the memory pointed to by <paramref
/// name="p"/> with length <paramref name="len"/>. The pointer must be to a valid <see cref="Encoding.UTF8"/>
/// encoded block of memory that terminates with a <c>\0</c> byte. The <paramref name="len"/> value refers to
/// the number of bytes in the utf8 encoded value <em>not</em> including the <c>\0</c> byte terminator.
/// <para/>
/// <paramref name="p"/> can be <see langword="null"/>, in which case <paramref name="len"/> is ignored
/// and a new <see cref="Utf8String"/> instance is created that represents <see langword="null"/>. Note that this
/// different from a pointer to a single <c>\0</c> byte and a length of one. That would represent an empty <see
/// cref="Utf8String"/> string.
/// </summary>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="p"/> is not <see langword="null"/> and <c>p[len]</c> is not <c>\0</c>.
/// </exception>
public static unsafe Utf8String FromPtrLen(byte* p, int len)
{
if (p == null)
{
return new Utf8String(ReadOnlySpan<byte>.Empty);
}
else
{
// the given len does NOT include the zero terminator
var sp = new ReadOnlySpan<byte>(p, len + 1);
return FromSpan(sp);
}
}
public static unsafe Utf8String FromIntPtr(IntPtr p)
{
if (p == IntPtr.Zero)
{
return new Utf8String(ReadOnlySpan<byte>.Empty);
}
else
{
return new Utf8String(FindZeroTerminator((byte*)(p.ToPointer())));
}
}
public override string ToString()
{
if (sp.Length == 0)
{
return "NUL";
}
unsafe
{
fixed (byte* q = sp)
{
return Encoding.UTF8.GetString(q, sp.Length - 1);
}
}
}
/// <summary>
/// Gets the <see cref="Encoding.UTF8"/> encoded bytes for the provided <paramref name="value"/>. The array
/// will include a trailing <c>\0</c> character. The length of the array will <see cref="Encoding.UTF8"/>'s
/// <see cref="Encoding.GetByteCount(string)"/><c>+1</c> (for the trailing <c>\0</c> byte). These bytes are
/// suitable to use with <see cref="FromSpan"/> using the extension <see
/// cref="MemoryExtensions.AsSpan{T}(T[])"/> or <see cref="FromPtr(byte*)"/> or <see cref="FromPtrLen(byte*,
/// int)"/>. Note that for <see cref="FromPtrLen(byte*, int)"/> the length provided should not include the
/// trailing <c>\0</c> terminator.
/// </summary>
public static byte[] GetZeroTerminatedUTF8Bytes(string value)
{
return value.ToUtf8String();
}
public static implicit operator Utf8String(string s)
{
return FromString(s);
}
}
public static class Utf8StringUtils
{
public static byte[] ToUtf8String(this string sourceText)
{
if (sourceText == null)
{
return null;
}
var length = Encoding.UTF8.GetByteCount(sourceText);
var byteArray = new byte[length + 1];
var wrote = Encoding.UTF8.GetBytes(sourceText, 0, sourceText.Length, byteArray, 0);
byteArray[wrote] = 0;
return byteArray;
}
}