binding/SkiaSharp/Util.cs (170 lines of code) (raw):
#nullable disable
using System;
using System.Buffers;
using System.ComponentModel;
using System.Text;
#if NETSTANDARD1_3 || WINDOWS_UWP
using System.Reflection;
#endif
namespace SkiaSharp
{
internal unsafe static class Utils
{
internal const float NearlyZero = 1.0f / (1 << 12);
internal static int GetPreambleSize (SKData data)
{
_ = data ?? throw new ArgumentNullException (nameof (data));
var buffer = data.AsSpan ();
var len = buffer.Length;
if (len >= 2 && buffer[0] == 0xfe && buffer[1] == 0xff)
return 2;
else if (len >= 3 && buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
return 3;
else if (len >= 3 && buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
return 3;
else if (len >= 4 && buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
return 4;
else
return 0;
}
internal static Span<byte> AsSpan (this IntPtr ptr, int size) =>
new Span<byte> ((void*)ptr, size);
internal static ReadOnlySpan<byte> AsReadOnlySpan (this IntPtr ptr, int size) =>
new ReadOnlySpan<byte> ((void*)ptr, size);
internal static bool NearlyEqual (float a, float b, float tolerance) =>
Math.Abs (a - b) <= tolerance;
internal static byte[] GetBytes (this Encoding encoding, ReadOnlySpan<char> text)
{
if (text.Length == 0)
return new byte[0];
fixed (char* t = text) {
var byteCount = encoding.GetByteCount (t, text.Length);
if (byteCount == 0)
return new byte[0];
var bytes = new byte[byteCount];
fixed (byte* b = bytes) {
encoding.GetBytes (t, text.Length, b, byteCount);
}
return bytes;
}
}
public static RentedArray<T> RentArray<T> (int length, bool nullIfEmpty = false) =>
nullIfEmpty && length <= 0
? default
: new RentedArray<T> (length);
public static RentedArray<IntPtr> RentHandlesArray (SKObject[] objects, bool nullIfEmpty = false)
{
var handles = RentArray<IntPtr> (objects?.Length ?? 0, nullIfEmpty);
for (var i = 0; i < handles.Length; i++) {
handles[i] = objects[i]?.Handle ?? IntPtr.Zero;
}
return handles;
}
internal readonly ref struct RentedArray<T>
{
internal RentedArray (int length)
{
Array = ArrayPool<T>.Shared.Rent (length);
Span = new Span<T> (Array, 0, length);
}
public readonly T[] Array;
public readonly Span<T> Span;
public int Length => Span.Length;
public T this[int index] {
get => Span[index];
set => Span[index] = value;
}
[EditorBrowsable (EditorBrowsableState.Never)]
public ref T GetPinnableReference () =>
ref Span.GetPinnableReference ();
public void Dispose ()
{
if (Array != null)
ArrayPool<T>.Shared.Return (Array);
}
public static explicit operator T[] (RentedArray<T> scope) =>
scope.Array;
public static implicit operator Span<T> (RentedArray<T> scope) =>
scope.Span;
public static implicit operator ReadOnlySpan<T> (RentedArray<T> scope) =>
scope.Span;
}
#if NETSTANDARD1_3 || WINDOWS_UWP
internal static bool IsAssignableFrom (this Type type, Type c) =>
type.GetTypeInfo ().IsAssignableFrom (c.GetTypeInfo ());
#endif
}
public unsafe static class StringUtilities
{
internal const string NullTerminator = "\0";
// GetUnicodeStringLength
private static int GetUnicodeStringLength (SKTextEncoding encoding) =>
encoding switch {
SKTextEncoding.Utf8 => 1,
SKTextEncoding.Utf16 => 1,
SKTextEncoding.Utf32 => 2,
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported.")
};
// GetCharacterByteSize
internal static int GetCharacterByteSize (this SKTextEncoding encoding) =>
encoding switch {
SKTextEncoding.Utf8 => 1,
SKTextEncoding.Utf16 => 2,
SKTextEncoding.Utf32 => 4,
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported.")
};
// GetUnicodeCharacterCode
public static int GetUnicodeCharacterCode (string character, SKTextEncoding encoding)
{
if (character == null)
throw new ArgumentNullException (nameof (character));
if (GetUnicodeStringLength (encoding) != character.Length)
throw new ArgumentException (nameof (character), $"Only a single character can be specified.");
var bytes = GetEncodedText (character, encoding);
return BitConverter.ToInt32 (bytes, 0);
}
// GetEncodedText
public static byte[] GetEncodedText (string text, SKTextEncoding encoding) =>
GetEncodedText (text.AsSpan (), encoding);
internal static byte[] GetEncodedText (string text, SKTextEncoding encoding, bool addNull)
{
if (!string.IsNullOrEmpty (text) && addNull)
text += NullTerminator;
return GetEncodedText (text.AsSpan (), encoding);
}
public static byte[] GetEncodedText (ReadOnlySpan<char> text, SKTextEncoding encoding) =>
encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetBytes (text),
SKTextEncoding.Utf16 => Encoding.Unicode.GetBytes (text),
SKTextEncoding.Utf32 => Encoding.UTF32.GetBytes (text),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
// GetString
public static string GetString (IntPtr data, int dataLength, SKTextEncoding encoding) =>
GetString (data.AsReadOnlySpan (dataLength), 0, dataLength, encoding);
public static string GetString (byte[] data, SKTextEncoding encoding) =>
GetString (data, 0, data.Length, encoding);
public static string GetString (byte[] data, int index, int count, SKTextEncoding encoding)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
return encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (data, index, count),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (data, index, count),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (data, index, count),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
}
public static string GetString (ReadOnlySpan<byte> data, SKTextEncoding encoding) =>
GetString (data, 0, data.Length, encoding);
public static string GetString (ReadOnlySpan<byte> data, int index, int count, SKTextEncoding encoding)
{
data = data.Slice (index, count);
if (data.Length == 0)
return string.Empty;
fixed (byte* bp = data) {
return encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (bp, data.Length),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (bp, data.Length),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (bp, data.Length),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
}
}
}
}