diff --git a/Raylib-cs.Tests/RaylibTests.cs b/Raylib-cs.Tests/RaylibTests.cs index e3b6ce5..fa87ab3 100644 --- a/Raylib-cs.Tests/RaylibTests.cs +++ b/Raylib-cs.Tests/RaylibTests.cs @@ -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()); Assert.True(BlittableHelper.IsBlittable()); } + + [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(); + + } } } diff --git a/Raylib-cs/Raylib.cs b/Raylib-cs/Raylib.cs index ad2d5c0..c9abf60 100644 --- a/Raylib-cs/Raylib.cs +++ b/Raylib-cs/Raylib.cs @@ -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 /// Initialize window and OpenGL context [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); + } + } /// Check if KEY_ESCAPE pressed or Close icon pressed [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] @@ -147,7 +156,15 @@ namespace Raylib_cs /// Set title for window (only PLATFORM_DESKTOP) [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); + } + } /// Set window position on screen (only PLATFORM_DESKTOP) [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] @@ -239,7 +256,15 @@ namespace Raylib_cs /// Set clipboard text content [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 /// Create an image from text (default font) [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); + } + } /// Create an image from text (custom sprite font) [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); + } + } /// Convert image to POT (power-of-two) [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] @@ -1273,13 +1313,28 @@ namespace Raylib_cs /// Draw text (using default font) within an image (destination) [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); + } + } /// Draw text (custom sprite font) within an image (destination) [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 /// Draw text (using default font) [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); + } + } /// Draw text using font and additional parameters [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); + } + } /// Draw text using Font and pro parameters (rotation) [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); + } + + } /// Draw one character (codepoint) [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] @@ -1488,11 +1570,27 @@ namespace Raylib_cs /// Measure string width for default font [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); + } + } /// Measure string size for Font [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); + } + } /// Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] diff --git a/Raylib-cs/Utf8String.cs b/Raylib-cs/Utf8String.cs new file mode 100644 index 0000000..1b5a57c --- /dev/null +++ b/Raylib-cs/Utf8String.cs @@ -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 + +/// +/// A raw string representation suitable for passing into many core SQLite apis. These will normally be pointers to +/// utf8 encoded bytes, with a trailing \0 terminator. strings can be represented as +/// well as empty strings. +/// +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 sp; + + public ref readonly byte GetPinnableReference() + { + return ref sp.GetPinnableReference(); + } + + private Utf8String(ReadOnlySpan a) + { + // no check here. anything that calls this + // constructor must make assurances about the + // zero terminator. + sp = a; + } + + /// + /// Creates a new instance of which directly points at the memory pointed to by . The span must contain a valid encoded block of memory that + /// terminates with a \0 byte. The span passed in must include the \0 terminator. + /// + /// Both and empty strings can be created here. To create a string, + /// pass in an empty . To create an empty string, pass in a span with length 1, that + /// only contains a \0 + /// + /// + /// Thrown if span.Length > 0 and span[^1] is not \0. + /// + public static Utf8String FromSpan(ReadOnlySpan 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.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 FindZeroTerminator(byte* p) + { + var len = (int)GetLength(p); + return new ReadOnlySpan(p, len + 1); + } + + /// + /// Creates a new instance of which directly points at the memory pointed to by . The pointer must either be or point to a valid encoded block of memory that terminates with a \0 byte. + /// + public static unsafe Utf8String FromPtr(byte* p) + { + if (p == null) + { + return new Utf8String(ReadOnlySpan.Empty); + } + else + { + return new Utf8String(FindZeroTerminator(p)); + } + } + + // TODO maybe remove this and just use FromSpan? + + /// + /// Creates a new instance of which directly points at the memory pointed to by with length . The pointer must be to a valid + /// encoded block of memory that terminates with a \0 byte. The value refers to + /// the number of bytes in the utf8 encoded value not including the \0 byte terminator. + /// + /// can be , in which case is ignored + /// and a new instance is created that represents . Note that this + /// different from a pointer to a single \0 byte and a length of one. That would represent an empty string. + /// + /// + /// Thrown if is not and p[len] is not \0. + /// + public static unsafe Utf8String FromPtrLen(byte* p, int len) + { + if (p == null) + { + return new Utf8String(ReadOnlySpan.Empty); + } + else + { + // the given len does NOT include the zero terminator + var sp = new ReadOnlySpan(p, len + 1); + return FromSpan(sp); + } + } + + public static unsafe Utf8String FromIntPtr(IntPtr p) + { + if (p == IntPtr.Zero) + { + return new Utf8String(ReadOnlySpan.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); + } + } + } + + /// + /// Gets the encoded bytes for the provided . The array + /// will include a trailing \0 character. The length of the array will 's + /// +1 (for the trailing \0 byte). These bytes are + /// suitable to use with using the extension or or . Note that for the length provided should not include the + /// trailing \0 terminator. + /// + 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; + } +} \ No newline at end of file