Utility.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Some code had been borrowed from https://www.monogame.net/
  2. using System;
  3. using System.IO;
  4. using System.Runtime.InteropServices;
  5. using FontStashSharp.Interfaces;
  6. #if MONOGAME || FNA
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Graphics;
  9. #elif STRIDE
  10. using Stride.Core.Mathematics;
  11. using Stride.Graphics;
  12. using Texture2D = Stride.Graphics.Texture;
  13. #else
  14. using System.Numerics;
  15. using System.Drawing;
  16. using Matrix = System.Numerics.Matrix3x2;
  17. using Texture2D = Veldrid.Texture;
  18. using Color = Veldrid.RgbaFloat;
  19. #endif
  20. namespace FontStashSharp
  21. {
  22. internal static class Utility
  23. {
  24. public static readonly Point PointZero = new Point(0, 0);
  25. public static readonly Vector2 Vector2Zero = new Vector2(0, 0);
  26. public static readonly Vector2 DefaultScale = new Vector2(1.0f, 1.0f);
  27. public static readonly Vector2 DefaultOrigin = new Vector2(0.0f, 0.0f);
  28. [StructLayout(LayoutKind.Explicit)]
  29. private struct FloatToInt
  30. {
  31. [FieldOffset(0)] public float f;
  32. [FieldOffset(0)] public int i;
  33. }
  34. public static int FloatAsInt(this float f)
  35. {
  36. return new FloatToInt { f = f }.i;
  37. }
  38. /// <summary>
  39. /// Restricts a value to be within a specified range.
  40. /// </summary>
  41. /// <param name="value">The value to clamp.</param>
  42. /// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
  43. /// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
  44. /// <returns>The clamped value.</returns>
  45. public static int Clamp(int value, int min, int max)
  46. {
  47. value = (value > max) ? max : value;
  48. value = (value < min) ? min : value;
  49. return value;
  50. }
  51. /// <summary>
  52. /// Restricts a value to be within a specified range.
  53. /// </summary>
  54. /// <param name="value">The value to clamp.</param>
  55. /// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
  56. /// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
  57. /// <returns>The clamped value.</returns>
  58. public static float Clamp(float value, float min, float max)
  59. {
  60. // First we check to see if we're greater than the max
  61. value = (value > max) ? max : value;
  62. // Then we check to see if we're less than the min.
  63. value = (value < min) ? min : value;
  64. // There's no check to see if min > max.
  65. return value;
  66. }
  67. /// <summary>
  68. /// Linearly interpolates between two values.
  69. /// </summary>
  70. /// <param name="value1">Source value.</param>
  71. /// <param name="value2">Destination value.</param>
  72. /// <param name="amount">Value between 0 and 1 indicating the weight of value2.</param>
  73. /// <returns>Interpolated value.</returns>
  74. /// <remarks>This method performs the linear interpolation based on the following formula:
  75. /// <code>value1 + (value2 - value1) * amount</code>.
  76. /// Passing amount a value of 0 will cause value1 to be returned, a value of 1 will cause value2 to be returned.
  77. /// See <see cref="MathHelper.LerpPrecise"/> for a less efficient version with more precision around edge cases.
  78. /// </remarks>
  79. public static float Lerp(float value1, float value2, float amount)
  80. {
  81. return value1 + (value2 - value1) * amount;
  82. }
  83. /// <summary>
  84. /// Linearly interpolates between two values.
  85. /// This method is a less efficient, more precise version of <see cref="MathHelper.Lerp"/>.
  86. /// See remarks for more info.
  87. /// </summary>
  88. /// <param name="value1">Source value.</param>
  89. /// <param name="value2">Destination value.</param>
  90. /// <param name="amount">Value between 0 and 1 indicating the weight of value2.</param>
  91. /// <returns>Interpolated value.</returns>
  92. /// <remarks>This method performs the linear interpolation based on the following formula:
  93. /// <code>((1 - amount) * value1) + (value2 * amount)</code>.
  94. /// Passing amount a value of 0 will cause value1 to be returned, a value of 1 will cause value2 to be returned.
  95. /// This method does not have the floating point precision issue that <see cref="MathHelper.Lerp"/> has.
  96. /// i.e. If there is a big gap between value1 and value2 in magnitude (e.g. value1=10000000000000000, value2=1),
  97. /// right at the edge of the interpolation range (amount=1), <see cref="MathHelper.Lerp"/> will return 0 (whereas it should return 1).
  98. /// This also holds for value1=10^17, value2=10; value1=10^18,value2=10^2... so on.
  99. /// For an in depth explanation of the issue, see below references:
  100. /// Relevant Wikipedia Article: https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support
  101. /// Relevant StackOverflow Answer: http://stackoverflow.com/questions/4353525/floating-point-linear-interpolation#answer-23716956
  102. /// </remarks>
  103. public static float LerpPrecise(float value1, float value2, float amount)
  104. {
  105. return ((1 - amount) * value1) + (value2 * amount);
  106. }
  107. public static byte[] ToByteArray(this Stream stream)
  108. {
  109. byte[] bytes;
  110. // Rewind stream if it is at end
  111. if (stream.CanSeek && stream.Length == stream.Position)
  112. {
  113. stream.Seek(0, SeekOrigin.Begin);
  114. }
  115. // Copy it's data to memory
  116. using (var ms = new MemoryStream())
  117. {
  118. stream.CopyTo(ms);
  119. bytes = ms.ToArray();
  120. }
  121. return bytes;
  122. }
  123. public static Vector2 Transform(this Vector2 v, ref Matrix matrix)
  124. {
  125. #if MONOGAME || FNA
  126. Vector2 result;
  127. Vector2.Transform(ref v, ref matrix, out result);
  128. return result;
  129. #elif STRIDE
  130. Vector4 result;
  131. Vector2.Transform(ref v, ref matrix, out result);
  132. return new Vector2(result.X, result.Y);
  133. #else
  134. return Vector2.Transform(v, matrix);
  135. #endif
  136. }
  137. public static Vector3 TransformToVector3(this Vector2 v, ref Matrix matrix, float z)
  138. {
  139. var result = v.Transform(ref matrix);
  140. return new Vector3(result.X, result.Y, z);
  141. }
  142. public static int Length(this string s)
  143. {
  144. if (string.IsNullOrEmpty(s))
  145. {
  146. return 0;
  147. }
  148. return s.Length;
  149. }
  150. public static void BuildTransform(Vector2 position, Vector2 scale, float rotation, Vector2 origin, out Matrix transformation)
  151. {
  152. // This code had been borrowed from MonoGame's SpriteBatch.DrawString
  153. transformation = Matrix.Identity;
  154. float offsetX, offsetY;
  155. if (rotation == 0)
  156. {
  157. transformation.M11 = scale.X;
  158. transformation.M22 = scale.Y;
  159. offsetX = position.X - (origin.X * transformation.M11);
  160. offsetY = position.Y - (origin.Y * transformation.M22);
  161. }
  162. else
  163. {
  164. var cos = (float)Math.Cos(rotation);
  165. var sin = (float)Math.Sin(rotation);
  166. transformation.M11 = scale.X * cos;
  167. transformation.M12 = scale.X * sin;
  168. transformation.M21 = scale.Y * -sin;
  169. transformation.M22 = scale.Y * cos;
  170. offsetX = position.X - (origin.X * transformation.M11) - (origin.Y * transformation.M21);
  171. offsetY = position.Y - (origin.X * transformation.M12) - (origin.Y * transformation.M22);
  172. }
  173. #if MONOGAME || FNA || STRIDE
  174. transformation.M41 = offsetX;
  175. transformation.M42 = offsetY;
  176. #else
  177. transformation.M31 = offsetX;
  178. transformation.M32 = offsetY;
  179. #endif
  180. }
  181. public static void DrawQuad(this IFontStashRenderer2 renderer,
  182. Texture2D texture, Color color,
  183. Vector2 baseOffset, ref Matrix transformation, float layerDepth,
  184. Vector2 size, Rectangle textureRectangle,
  185. ref VertexPositionColorTexture topLeft, ref VertexPositionColorTexture topRight,
  186. ref VertexPositionColorTexture bottomLeft, ref VertexPositionColorTexture bottomRight)
  187. {
  188. #if MONOGAME || FNA || STRIDE
  189. var textureSize = new Point(texture.Width, texture.Height);
  190. #else
  191. var textureSize = renderer.TextureManager.GetTextureSize(texture);
  192. #endif
  193. topLeft.Position = baseOffset.TransformToVector3(ref transformation, layerDepth);
  194. topLeft.TextureCoordinate = new Vector2((float)textureRectangle.X / textureSize.Width,
  195. (float)textureRectangle.Y / textureSize.Height);
  196. topLeft.Color = color;
  197. topRight.Position = (baseOffset + new Vector2(size.X, 0)).TransformToVector3(ref transformation, layerDepth);
  198. topRight.TextureCoordinate = new Vector2((float)textureRectangle.Right / textureSize.Width,
  199. (float)textureRectangle.Y / textureSize.Height);
  200. topRight.Color = color;
  201. bottomLeft.Position = (baseOffset + new Vector2(0, size.Y)).TransformToVector3(ref transformation, layerDepth);
  202. bottomLeft.TextureCoordinate = new Vector2((float)textureRectangle.Left / textureSize.Width,
  203. (float)textureRectangle.Bottom / textureSize.Height);
  204. bottomLeft.Color = color;
  205. bottomRight.Position = (baseOffset + new Vector2(size.X, size.Y)).TransformToVector3(ref transformation, layerDepth);
  206. bottomRight.TextureCoordinate = new Vector2((float)textureRectangle.Right / textureSize.Width,
  207. (float)textureRectangle.Bottom / textureSize.Height);
  208. bottomRight.Color = color;
  209. renderer.DrawQuad(texture, ref topLeft, ref topRight, ref bottomLeft, ref bottomRight);
  210. }
  211. }
  212. }