/ThirdParty/OpenTK.1.0/Source/Compatibility/Graphics/Text/GlyphPacker.cs

https://github.com/virtualglobebook/OpenGlobe · C# · 270 lines · 149 code · 53 blank · 68 comment · 22 complexity · 64d0951864693496f404d6ed858c4881 MD5 · raw file

  1. #region License
  2. //
  3. // The Open Toolkit Library License
  4. //
  5. // Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted.
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights to
  10. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  11. // the Software, and to permit persons to whom the Software is furnished to do
  12. // so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in all
  15. // copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. // OTHER DEALINGS IN THE SOFTWARE.
  25. //
  26. #endregion
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using System.Drawing;
  31. namespace OpenTK.Graphics.Text
  32. {
  33. class GlyphPacker
  34. {
  35. Node root;
  36. #region --- Constructors ---
  37. public GlyphPacker(int width, int height)
  38. {
  39. if (width <= 0)
  40. throw new ArgumentOutOfRangeException("width", width, "Must be greater than zero.");
  41. if (height <= 0)
  42. throw new ArgumentOutOfRangeException("height", height, "Must be greater than zero.");
  43. root = new Node();
  44. root.Rectangle = new Rectangle(0, 0, width, width);
  45. }
  46. #endregion
  47. #region --- Public Methods ---
  48. #region public bool TryAdd(Rectangle boundingBox)
  49. /// <summary>
  50. /// Adds boundingBox to the GlyphPacker.
  51. /// </summary>
  52. /// <param name="boundingBox">The bounding box of the item to pack.</param>
  53. /// <param name="packedRectangle">The System.Drawing.Rectangle that contains the position of the packed item.</param>
  54. /// <returns>True, if the item was successfully packed; false if the item is too big for this packer..</returns>
  55. /// <exception cref="InvalidOperationException">Occurs if the item is larger than the available TexturePacker area</exception>
  56. /// <exception cref="TexturePackerFullException">Occurs if the item cannot fit in the remaining packer space.</exception>
  57. public bool TryAdd(Rectangle boundingBox, out Rectangle packedRectangle)
  58. {
  59. if (!root.Rectangle.Contains(boundingBox))
  60. {
  61. packedRectangle = new Rectangle();
  62. return false;
  63. }
  64. // Increase size so that the glyphs do not touch each other (to avoid rendering artifacts).
  65. boundingBox.Width += 2;
  66. boundingBox.Height += 2;
  67. Node node = root.Insert(boundingBox);
  68. // Tree is full and insertion failed:
  69. if (node == null)
  70. {
  71. packedRectangle = new Rectangle();
  72. return false;
  73. }
  74. packedRectangle = new Rectangle(node.Rectangle.X, node.Rectangle.Y, node.Rectangle.Width - 2, node.Rectangle.Height - 2);
  75. return true;
  76. }
  77. #endregion
  78. #region public Rectangle TryAdd(RectangleF boundingBox)
  79. /// <summary>
  80. /// Adds boundingBox to the GlyphPacker.
  81. /// </summary>
  82. /// <param name="boundingBox">The bounding box of the item to pack.</param>
  83. /// <param name="packedRectangle">The System.Drawing.RectangleF that contains the position of the packed item.</param>
  84. /// <returns>True, if the item was successfully packed; false if the item is too big for this packer..</returns>
  85. /// <exception cref="InvalidOperationException">Occurs if the item is larger than the available TexturePacker area</exception>
  86. /// <exception cref="TexturePackerFullException">Occurs if the item cannot fit in the remaining packer space.</exception>
  87. public bool TryAdd(RectangleF boundingBox, out RectangleF packedRectangle)
  88. {
  89. Rectangle bbox = new Rectangle(
  90. (int)boundingBox.X, (int)boundingBox.Y,
  91. (int)(boundingBox.Width + 0.5f), (int)(boundingBox.Height + 0.5f));
  92. return TryAdd(bbox, out packedRectangle);
  93. }
  94. #endregion
  95. #region public Rectangle Add(Rectangle boundingBox)
  96. /// <summary>
  97. /// Adds boundingBox to the GlyphPacker.
  98. /// </summary>
  99. /// <param name="boundingBox">The bounding box of the item to pack.</param>
  100. /// <returns>A System.Drawing.Rectangle containing the coordinates of the packed item.</returns>
  101. /// <exception cref="InvalidOperationException">Occurs if the item is larger than the available TexturePacker area</exception>
  102. /// <exception cref="TexturePackerFullException">Occurs if the item cannot fit in the remaining packer space.</exception>
  103. public Rectangle Add(Rectangle boundingBox)
  104. {
  105. if (!TryAdd(boundingBox, out boundingBox))
  106. throw new TexturePackerFullException();
  107. return boundingBox;
  108. }
  109. #endregion
  110. #region public Rectangle Add(RectangleF boundingBox)
  111. /// <summary>
  112. /// Rounds boundingBox to the largest integer and adds the resulting Rectangle to the GlyphPacker.
  113. /// </summary>
  114. /// <param name="boundingBox">The bounding box of the item to pack.</param>
  115. /// <returns>A System.Drawing.Rectangle containing the coordinates of the packed item.</returns>
  116. /// <exception cref="InvalidOperationException">Occurs if the item is larger than the available TexturePacker area</exception>
  117. /// <exception cref="ArgumentException">Occurs if the item already exists in the TexturePacker.</exception>
  118. public Rectangle Add(RectangleF boundingBox)
  119. {
  120. Rectangle bbox = new Rectangle(
  121. (int)boundingBox.X, (int)boundingBox.Y,
  122. (int)(boundingBox.Width + 0.5f), (int)(boundingBox.Height + 0.5f));
  123. return Add(bbox);
  124. }
  125. #endregion
  126. #region public void Clear()
  127. /// <summary>
  128. /// Discards all packed items.
  129. /// </summary>
  130. public void Clear()
  131. {
  132. root.Clear();
  133. }
  134. #endregion
  135. #endregion
  136. #region Node
  137. class Node
  138. {
  139. public Node()
  140. {
  141. }
  142. Node left, right;
  143. Rectangle rect;
  144. bool occupied;
  145. public Rectangle Rectangle { get { return rect; } set { rect = value; } }
  146. public Node Left { get { return left; } set { left = value; } }
  147. public Node Right { get { return right; } set { right = value; } }
  148. #region --- Constructor ---
  149. public bool Leaf
  150. {
  151. get { return left == null && right == null; }
  152. }
  153. #endregion
  154. #region Node Insert(Rectangle bbox)
  155. public Node Insert( Rectangle bbox)
  156. {
  157. if (!this.Leaf)
  158. {
  159. // Recurse towards left child, and if that fails, towards the right.
  160. Node new_node = left.Insert(bbox);
  161. return new_node ?? right.Insert(bbox);
  162. }
  163. else
  164. {
  165. // We have recursed to a leaf.
  166. // If it is not empty go back.
  167. if (occupied)
  168. return null;
  169. // If this leaf is too small go back.
  170. if (rect.Width < bbox.Width || rect.Height < bbox.Height)
  171. return null;
  172. // If this leaf is the right size, insert here.
  173. if (rect.Width == bbox.Width && rect.Height == bbox.Height)
  174. {
  175. occupied = true;
  176. return this;
  177. }
  178. // This leaf is too large, split it up. We'll decide which way to split
  179. // by checking the width and height difference between this rectangle and
  180. // out item's bounding box. If the width difference is larger, we'll split
  181. // horizontaly, else verticaly.
  182. left = new Node();
  183. right = new Node();
  184. int dw = this.rect.Width - bbox.Width + 1;
  185. int dh = this.rect.Height - bbox.Height + 1;
  186. if (dw > dh)
  187. {
  188. left.rect = new Rectangle(rect.Left, rect.Top, bbox.Width, rect.Height);
  189. right.rect = new Rectangle(rect.Left + bbox.Width, rect.Top, rect.Width - bbox.Width, rect.Height);
  190. }
  191. else
  192. {
  193. left.rect = new Rectangle(rect.Left, rect.Top, rect.Width, bbox.Height);
  194. right.rect = new Rectangle(rect.Left, rect.Top + bbox.Height, rect.Width, rect.Height - bbox.Height);
  195. }
  196. return left.Insert(bbox);
  197. }
  198. }
  199. #endregion
  200. #region public void Clear()
  201. public void Clear()
  202. {
  203. if (left != null)
  204. left.Clear();
  205. if (right != null)
  206. right.Clear();
  207. left = right = null;
  208. }
  209. #endregion
  210. }
  211. #endregion
  212. }
  213. class TexturePackerFullException : Exception
  214. {
  215. public TexturePackerFullException() : base("There is not enough space to add this item. Consider calling the Clear() method.") { }
  216. }
  217. }