Stroker.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. #region MIT License
  2. /*Copyright (c) 2012 Robert Rouhani <robert.rouhani@gmail.com>
  3. SharpFont based on Tao.FreeType, Copyright (c) 2003-2007 Tao Framework Team
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. this software and associated documentation files (the "Software"), to deal in
  6. the Software without restriction, including without limitation the rights to
  7. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  8. of the Software, and to permit persons to whom the Software is furnished to do
  9. so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.*/
  19. #endregion
  20. using System;
  21. namespace SharpFont
  22. {
  23. /// <summary>
  24. /// Opaque handler to a path stroker object.
  25. /// </summary>
  26. public class Stroker : IDisposable
  27. {
  28. #region Fields
  29. private IntPtr reference;
  30. private bool disposed;
  31. private Library parentLibrary;
  32. #endregion
  33. #region Constructors
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="Stroker"/> class.
  36. /// </summary>
  37. /// <param name="library">FreeType library handle.</param>
  38. public Stroker(Library library)
  39. {
  40. IntPtr strokerRef;
  41. Error err = FT.FT_Stroker_New(library.Reference, out strokerRef);
  42. if (err != Error.Ok)
  43. throw new FreeTypeException(err);
  44. Reference = strokerRef;
  45. library.AddChildStroker(this);
  46. parentLibrary = library;
  47. }
  48. /// <summary>
  49. /// Finalizes an instance of the <see cref="Stroker"/> class.
  50. /// </summary>
  51. ~Stroker()
  52. {
  53. Dispose(false);
  54. }
  55. #endregion
  56. #region Properties
  57. /// <summary>
  58. /// Gets a value indicating whether the <see cref="Stroker"/> has been disposed.
  59. /// </summary>
  60. public bool IsDisposed
  61. {
  62. get
  63. {
  64. return disposed;
  65. }
  66. }
  67. internal IntPtr Reference
  68. {
  69. get
  70. {
  71. if (disposed)
  72. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  73. return reference;
  74. }
  75. set
  76. {
  77. if (disposed)
  78. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  79. reference = value;
  80. }
  81. }
  82. #endregion
  83. #region Methods
  84. /// <summary>
  85. /// Reset a stroker object's attributes.
  86. /// </summary>
  87. /// <remarks>
  88. /// The radius is expressed in the same units as the outline coordinates.
  89. /// </remarks>
  90. /// <param name="radius">The border radius.</param>
  91. /// <param name="lineCap">The line cap style.</param>
  92. /// <param name="lineJoin">The line join style.</param>
  93. /// <param name="miterLimit">
  94. /// The miter limit for the <see cref="StrokerLineJoin.MiterFixed"/> and
  95. /// <see cref="StrokerLineJoin.MiterVariable"/> line join styles, expressed as 16.16 fixed point value.
  96. /// </param>
  97. public void Set(int radius, StrokerLineCap lineCap, StrokerLineJoin lineJoin, int miterLimit)
  98. {
  99. if (disposed)
  100. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  101. FT.FT_Stroker_Set(Reference, radius, lineCap, lineJoin, miterLimit);
  102. }
  103. /// <summary>
  104. /// Reset a stroker object without changing its attributes. You should call this function before beginning a
  105. /// new series of calls to <see cref="BeginSubPath"/> or <see cref="EndSubPath"/>.
  106. /// </summary>
  107. public void Rewind()
  108. {
  109. if (disposed)
  110. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  111. FT.FT_Stroker_Rewind(Reference);
  112. }
  113. /// <summary>
  114. /// A convenience function used to parse a whole outline with the stroker. The resulting outline(s) can be
  115. /// retrieved later by functions like <see cref="GetCounts"/> and <see cref="Export"/>.
  116. /// </summary>
  117. /// <remarks><para>
  118. /// If ‘opened’ is 0 (the default), the outline is treated as a closed path, and the stroker generates two
  119. /// distinct ‘border’ outlines.
  120. /// </para><para>
  121. /// If ‘opened’ is 1, the outline is processed as an open path, and the stroker generates a single ‘stroke’
  122. /// outline.
  123. /// </para><para>
  124. /// This function calls <see cref="Rewind"/> automatically.
  125. /// </para></remarks>
  126. /// <param name="outline">The source outline.</param>
  127. /// <param name="opened">
  128. /// A boolean. If 1, the outline is treated as an open path instead of a closed one.
  129. /// </param>
  130. public void ParseOutline(Outline outline, bool opened)
  131. {
  132. if (disposed)
  133. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  134. if (outline == null)
  135. throw new ArgumentNullException("outline");
  136. Error err = FT.FT_Stroker_ParseOutline(Reference, outline.Reference, opened);
  137. if (err != Error.Ok)
  138. throw new FreeTypeException(err);
  139. }
  140. /// <summary>
  141. /// Start a new sub-path in the stroker.
  142. /// </summary>
  143. /// <remarks>
  144. /// This function is useful when you need to stroke a path that is not stored as an <see cref="Outline"/>
  145. /// object.
  146. /// </remarks>
  147. /// <param name="to">A pointer to the start vector.</param>
  148. /// <param name="open">A boolean. If 1, the sub-path is treated as an open one.</param>
  149. public void BeginSubPath(FTVector to, bool open)
  150. {
  151. if (disposed)
  152. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  153. Error err = FT.FT_Stroker_BeginSubPath(Reference, ref to, open);
  154. if (err != Error.Ok)
  155. throw new FreeTypeException(err);
  156. }
  157. /// <summary>
  158. /// Close the current sub-path in the stroker.
  159. /// </summary>
  160. /// <remarks>
  161. /// You should call this function after <see cref="BeginSubPath"/>. If the subpath was not ‘opened’, this
  162. /// function ‘draws’ a single line segment to the start position when needed.
  163. /// </remarks>
  164. public void EndSubPath()
  165. {
  166. if (disposed)
  167. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  168. Error err = FT.FT_Stroker_EndSubPath(Reference);
  169. if (err != Error.Ok)
  170. throw new FreeTypeException(err);
  171. }
  172. /// <summary>
  173. /// ‘Draw’ a single line segment in the stroker's current sub-path, from the last position.
  174. /// </summary>
  175. /// <remarks>
  176. /// You should call this function between <see cref="BeginSubPath"/> and <see cref="EndSubPath"/>.
  177. /// </remarks>
  178. /// <param name="to">A pointer to the destination point.</param>
  179. public void LineTo(FTVector to)
  180. {
  181. if (disposed)
  182. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  183. Error err = FT.FT_Stroker_LineTo(Reference, ref to);
  184. if (err != Error.Ok)
  185. throw new FreeTypeException(err);
  186. }
  187. /// <summary>
  188. /// ‘Draw’ a single quadratic Bézier in the stroker's current sub-path, from the last position.
  189. /// </summary>
  190. /// <remarks>
  191. /// You should call this function between <see cref="BeginSubPath"/> and <see cref="EndSubPath"/>.
  192. /// </remarks>
  193. /// <param name="control">A pointer to a Bézier control point.</param>
  194. /// <param name="to">A pointer to the destination point.</param>
  195. public void ConicTo(FTVector control, FTVector to)
  196. {
  197. if (disposed)
  198. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  199. Error err = FT.FT_Stroker_ConicTo(Reference, ref control, ref to);
  200. if (err != Error.Ok)
  201. throw new FreeTypeException(err);
  202. }
  203. /// <summary>
  204. /// ‘Draw’ a single cubic Bézier in the stroker's current sub-path, from the last position.
  205. /// </summary>
  206. /// <remarks>
  207. /// You should call this function between <see cref="BeginSubPath"/> and <see cref="EndSubPath"/>.
  208. /// </remarks>
  209. /// <param name="control1">A pointer to the first Bézier control point.</param>
  210. /// <param name="control2">A pointer to second Bézier control point.</param>
  211. /// <param name="to">A pointer to the destination point.</param>
  212. public void CubicTo(FTVector control1, FTVector control2, FTVector to)
  213. {
  214. if (disposed)
  215. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  216. Error err = FT.FT_Stroker_CubicTo(Reference, ref control1, ref control2, ref to);
  217. if (err != Error.Ok)
  218. throw new FreeTypeException(err);
  219. }
  220. /// <summary>
  221. /// Call this function once you have finished parsing your paths with the stroker. It returns the number of
  222. /// points and contours necessary to export one of the ‘border’ or ‘stroke’ outlines generated by the stroker.
  223. /// </summary>
  224. /// <remarks><para>
  225. /// When an outline, or a sub-path, is ‘closed’, the stroker generates two independent ‘border’ outlines, named
  226. /// ‘left’ and ‘right’.
  227. /// </para><para>
  228. /// When the outline, or a sub-path, is ‘opened’, the stroker merges the ‘border’ outlines with caps. The
  229. /// ‘left’ border receives all points, while the ‘right’ border becomes empty.
  230. /// </para><para>
  231. /// Use the function <see cref="GetCounts"/> instead if you want to retrieve the counts associated to both
  232. /// borders.
  233. /// </para></remarks>
  234. /// <param name="border">The border index.</param>
  235. /// <param name="pointsCount">The number of points.</param>
  236. /// <param name="contoursCount">The number of contours.</param>
  237. [CLSCompliant(false)]
  238. public void GetBorderCounts(StrokerBorder border, out uint pointsCount, out uint contoursCount)
  239. {
  240. if (disposed)
  241. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  242. Error err = FT.FT_Stroker_GetBorderCounts(Reference, border, out pointsCount, out contoursCount);
  243. if (err != Error.Ok)
  244. throw new FreeTypeException(err);
  245. }
  246. /// <summary><para>
  247. /// Call this function after <see cref="GetBorderCounts"/> to export the corresponding border to your own
  248. /// <see cref="Outline"/> structure.
  249. /// </para><para>
  250. /// Note that this function appends the border points and contours to your outline, but does not try to resize
  251. /// its arrays.
  252. /// </para></summary>
  253. /// <remarks><para>
  254. /// Always call this function after <see cref="GetBorderCounts"/> to get sure that there is enough room in your
  255. /// <see cref="Outline"/> object to receive all new data.
  256. /// </para><para>
  257. /// When an outline, or a sub-path, is ‘closed’, the stroker generates two independent ‘border’ outlines, named
  258. /// ‘left’ and ‘right’.
  259. /// </para><para>
  260. /// When the outline, or a sub-path, is ‘opened’, the stroker merges the ‘border’ outlines with caps. The
  261. /// ‘left’ border receives all points, while the ‘right’ border becomes empty.
  262. /// </para><para>
  263. /// Use the function <see cref="Export"/> instead if you want to retrieve all borders at once.
  264. /// </para></remarks>
  265. /// <param name="border">The border index.</param>
  266. /// <param name="outline">The target outline handle.</param>
  267. public void ExportBorder(StrokerBorder border, Outline outline)
  268. {
  269. if (disposed)
  270. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  271. if (outline == null)
  272. throw new ArgumentNullException("outline");
  273. FT.FT_Stroker_ExportBorder(Reference, border, outline.Reference);
  274. }
  275. /// <summary>
  276. /// Call this function once you have finished parsing your paths with the stroker. It returns the number of
  277. /// points and contours necessary to export all points/borders from the stroked outline/path.
  278. /// </summary>
  279. /// <param name="pointsCount">The number of points.</param>
  280. /// <param name="contoursCount">The number of contours.</param>
  281. [CLSCompliant(false)]
  282. public void GetCounts(out uint pointsCount, out uint contoursCount)
  283. {
  284. if (disposed)
  285. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  286. Error err = FT.FT_Stroker_GetCounts(Reference, out pointsCount, out contoursCount);
  287. if (err != Error.Ok)
  288. throw new FreeTypeException(err);
  289. }
  290. /// <summary><para>
  291. /// Call this function after <see cref="GetBorderCounts"/> to export all borders to your own
  292. /// <see cref="Outline"/> structure.
  293. /// </para><para>
  294. /// Note that this function appends the border points and contours to your outline, but does not try to resize
  295. /// its arrays.
  296. /// </para></summary>
  297. /// <param name="outline">The target outline handle.</param>
  298. public void Export(Outline outline)
  299. {
  300. if (disposed)
  301. throw new ObjectDisposedException("Stroker", "Cannot access a disposed object.");
  302. if (outline == null)
  303. throw new ArgumentNullException("outline");
  304. FT.FT_Stroker_Export(Reference, outline.Reference);
  305. }
  306. /// <summary>
  307. /// Disposes an instance of the <see cref="Stroker"/> class.
  308. /// </summary>
  309. public void Dispose()
  310. {
  311. Dispose(true);
  312. GC.SuppressFinalize(this);
  313. }
  314. private void Dispose(bool disposing)
  315. {
  316. if (!disposed)
  317. {
  318. disposed = true;
  319. FT.FT_Stroker_Done(reference);
  320. // removes itself from the parent Library, with a check to prevent this from happening when Library is
  321. // being disposed (Library disposes all it's children with a foreach loop, this causes an
  322. // InvalidOperationException for modifying a collection during enumeration)
  323. if (!parentLibrary.IsDisposed)
  324. parentLibrary.RemoveChildStroker(this);
  325. }
  326. }
  327. #endregion
  328. }
  329. }