ScanlineRasterizer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. //----------------------------------------------------------------------------
  2. // Anti-Grain Geometry - Version 2.4
  3. // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
  4. //
  5. // C# port by: Lars Brubaker
  6. // larsbrubaker@gmail.com
  7. // Copyright (C) 2007
  8. //
  9. // Permission to copy, use, modify, sell and distribute this software
  10. // is granted provided this copyright notice appears in all copies.
  11. // This software is provided "as is" without express or implied
  12. // warranty, and with no claim as to its suitability for any purpose.
  13. //
  14. //----------------------------------------------------------------------------
  15. //
  16. // The author gratefully acknowledges the support of David Turner,
  17. // Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
  18. // library - in producing this work. See http://www.freetype.org for details.
  19. //
  20. //----------------------------------------------------------------------------
  21. // Contact: mcseem@antigrain.com
  22. // mcseemagg@yahoo.com
  23. // http://www.antigrain.com
  24. //----------------------------------------------------------------------------
  25. //
  26. // Adaptation for 32-bit screen coordinates has been sponsored by
  27. // Liberty Technology Systems, Inc., visit http://lib-sys.com
  28. //
  29. // Liberty Technology Systems, Inc. is the provider of
  30. // PostScript and PDF technology for software developers.
  31. //
  32. //----------------------------------------------------------------------------
  33. using MatterHackers.Agg.VertexSource;
  34. using MatterHackers.VectorMath;
  35. using filling_rule_e = MatterHackers.Agg.Util.filling_rule_e;
  36. using poly_subpixel_scale_e = MatterHackers.Agg.Util.poly_subpixel_scale_e;
  37. namespace MatterHackers.Agg
  38. {
  39. //==================================================rasterizer_scanline_aa
  40. // Polygon rasterizer that is used to render filled polygons with
  41. // high-quality Anti-Aliasing. Internally, by default, the class uses
  42. // integer coordinates in format 24.8, i.e. 24 bits for integer part
  43. // and 8 bits for fractional - see poly_subpixel_shift. This class can be
  44. // used in the following way:
  45. //
  46. // 1. filling_rule(filling_rule_e ft) - optional.
  47. //
  48. // 2. gamma() - optional.
  49. //
  50. // 3. reset()
  51. //
  52. // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create
  53. // more than one contour, but each contour must consist of at least 3
  54. // vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3);
  55. // is the absolute minimum of vertices that define a triangle.
  56. // The algorithm does not check either the number of vertices nor
  57. // coincidence of their coordinates, but in the worst case it just
  58. // won't draw anything.
  59. // The order of the vertices (clockwise or counterclockwise)
  60. // is important when using the non-zero filling rule (fill_non_zero).
  61. // In this case the vertex order of all the contours must be the same
  62. // if you want your intersecting polygons to be without "holes".
  63. // You actually can use different vertices order. If the contours do not
  64. // intersect each other the order is not important anyway. If they do,
  65. // contours with the same vertex order will be rendered without "holes"
  66. // while the intersecting contours with different orders will have "holes".
  67. //
  68. // filling_rule() and gamma() can be called anytime before "sweeping".
  69. //------------------------------------------------------------------------
  70. public interface IRasterizer
  71. {
  72. int min_x();
  73. int min_y();
  74. int max_x();
  75. int max_y();
  76. void gamma(IGammaFunction gamma_function);
  77. bool sweep_scanline(IScanlineCache sl);
  78. void reset();
  79. void add_path(IVertexSource vs);
  80. bool rewind_scanlines();
  81. }
  82. public sealed class ScanlineRasterizer : IRasterizer
  83. {
  84. private RasterizerCellsAa m_outline;
  85. private VectorClipper m_VectorClipper;
  86. private int[] m_gamma = new int[(int)aa_scale_e.aa_scale];
  87. private Util.filling_rule_e m_filling_rule;
  88. private bool m_auto_close;
  89. private int m_start_x;
  90. private int m_start_y;
  91. private status m_status;
  92. private int m_scan_y;
  93. public enum status
  94. {
  95. status_initial,
  96. status_move_to,
  97. status_line_to,
  98. status_closed
  99. };
  100. public enum aa_scale_e
  101. {
  102. aa_shift = 8,
  103. aa_scale = 1 << aa_shift,
  104. aa_mask = aa_scale - 1,
  105. aa_scale2 = aa_scale * 2,
  106. aa_mask2 = aa_scale2 - 1
  107. };
  108. public ScanlineRasterizer()
  109. : this(new VectorClipper())
  110. {
  111. }
  112. //--------------------------------------------------------------------
  113. public ScanlineRasterizer(VectorClipper rasterizer_sl_clip)
  114. {
  115. m_outline = new RasterizerCellsAa();
  116. m_VectorClipper = rasterizer_sl_clip;
  117. m_filling_rule = filling_rule_e.fill_non_zero;
  118. m_auto_close = true;
  119. m_start_x = 0;
  120. m_start_y = 0;
  121. m_status = status.status_initial;
  122. for (int i = 0; i < (int)aa_scale_e.aa_scale; i++)
  123. {
  124. m_gamma[i] = i;
  125. }
  126. }
  127. /*
  128. //--------------------------------------------------------------------
  129. public rasterizer_scanline_aa(IClipper rasterizer_sl_clip, IGammaFunction gamma_function)
  130. {
  131. m_outline = new rasterizer_cells_aa();
  132. m_clipper = rasterizer_sl_clip;
  133. m_filling_rule = filling_rule_e.fill_non_zero;
  134. m_auto_close = true;
  135. m_start_x = 0;
  136. m_start_y = 0;
  137. m_status = status.status_initial;
  138. gamma(gamma_function);
  139. }*/
  140. //--------------------------------------------------------------------
  141. public void reset()
  142. {
  143. m_outline.reset();
  144. m_status = status.status_initial;
  145. }
  146. public void reset_clipping()
  147. {
  148. reset();
  149. m_VectorClipper.reset_clipping();
  150. }
  151. public RectangleDouble GetVectorClipBox()
  152. {
  153. return new RectangleDouble(
  154. m_VectorClipper.downscale(m_VectorClipper.clipBox.Left),
  155. m_VectorClipper.downscale(m_VectorClipper.clipBox.Bottom),
  156. m_VectorClipper.downscale(m_VectorClipper.clipBox.Right),
  157. m_VectorClipper.downscale(m_VectorClipper.clipBox.Top));
  158. }
  159. public void SetVectorClipBox(RectangleDouble clippingRect)
  160. {
  161. SetVectorClipBox(clippingRect.Left, clippingRect.Bottom, clippingRect.Right, clippingRect.Top);
  162. }
  163. public void SetVectorClipBox(double x1, double y1, double x2, double y2)
  164. {
  165. reset();
  166. m_VectorClipper.clip_box(m_VectorClipper.upscale(x1), m_VectorClipper.upscale(y1),
  167. m_VectorClipper.upscale(x2), m_VectorClipper.upscale(y2));
  168. }
  169. public void filling_rule(Util.filling_rule_e filling_rule)
  170. {
  171. m_filling_rule = filling_rule;
  172. }
  173. public void auto_close(bool flag)
  174. {
  175. m_auto_close = flag;
  176. }
  177. //--------------------------------------------------------------------
  178. public void gamma(IGammaFunction gamma_function)
  179. {
  180. for (int i = 0; i < (int)aa_scale_e.aa_scale; i++)
  181. {
  182. m_gamma[i] = (int)Util.uround(gamma_function.GetGamma((double)(i) / (int)aa_scale_e.aa_mask) * (int)aa_scale_e.aa_mask);
  183. }
  184. }
  185. /*
  186. //--------------------------------------------------------------------
  187. public int apply_gamma(int cover)
  188. {
  189. return (int)m_gamma[cover];
  190. }
  191. */
  192. //--------------------------------------------------------------------
  193. private void move_to(int x, int y)
  194. {
  195. if (m_outline.sorted()) reset();
  196. if (m_auto_close) close_polygon();
  197. m_VectorClipper.move_to(m_start_x = m_VectorClipper.downscale(x),
  198. m_start_y = m_VectorClipper.downscale(y));
  199. m_status = status.status_move_to;
  200. }
  201. //------------------------------------------------------------------------
  202. private void line_to(int x, int y)
  203. {
  204. m_VectorClipper.line_to(m_outline,
  205. m_VectorClipper.downscale(x),
  206. m_VectorClipper.downscale(y));
  207. m_status = status.status_line_to;
  208. }
  209. //------------------------------------------------------------------------
  210. public void move_to_d(double x, double y)
  211. {
  212. if (m_outline.sorted()) reset();
  213. if (m_auto_close) close_polygon();
  214. m_VectorClipper.move_to(m_start_x = m_VectorClipper.upscale(x),
  215. m_start_y = m_VectorClipper.upscale(y));
  216. m_status = status.status_move_to;
  217. }
  218. //------------------------------------------------------------------------
  219. public void line_to_d(double x, double y)
  220. {
  221. m_VectorClipper.line_to(m_outline,
  222. m_VectorClipper.upscale(x),
  223. m_VectorClipper.upscale(y));
  224. m_status = status.status_line_to;
  225. }
  226. public void close_polygon()
  227. {
  228. if (m_status == status.status_line_to)
  229. {
  230. m_VectorClipper.line_to(m_outline, m_start_x, m_start_y);
  231. m_status = status.status_closed;
  232. }
  233. }
  234. private void AddVertex(VertexData vertexData)
  235. {
  236. if (ShapePath.is_move_to(vertexData.command))
  237. {
  238. move_to_d(vertexData.position.X, vertexData.position.Y);
  239. }
  240. else
  241. {
  242. if (ShapePath.is_vertex(vertexData.command))
  243. {
  244. line_to_d(vertexData.position.X, vertexData.position.Y);
  245. }
  246. else
  247. {
  248. if (ShapePath.is_close(vertexData.command))
  249. {
  250. close_polygon();
  251. }
  252. }
  253. }
  254. }
  255. //------------------------------------------------------------------------
  256. private void edge(int x1, int y1, int x2, int y2)
  257. {
  258. if (m_outline.sorted()) reset();
  259. m_VectorClipper.move_to(m_VectorClipper.downscale(x1), m_VectorClipper.downscale(y1));
  260. m_VectorClipper.line_to(m_outline,
  261. m_VectorClipper.downscale(x2),
  262. m_VectorClipper.downscale(y2));
  263. m_status = status.status_move_to;
  264. }
  265. //------------------------------------------------------------------------
  266. private void edge_d(double x1, double y1, double x2, double y2)
  267. {
  268. if (m_outline.sorted()) reset();
  269. m_VectorClipper.move_to(m_VectorClipper.upscale(x1), m_VectorClipper.upscale(y1));
  270. m_VectorClipper.line_to(m_outline,
  271. m_VectorClipper.upscale(x2),
  272. m_VectorClipper.upscale(y2));
  273. m_status = status.status_move_to;
  274. }
  275. //-------------------------------------------------------------------
  276. public void add_path(IVertexSource vs)
  277. {
  278. double x = 0;
  279. double y = 0;
  280. ShapePath.FlagsAndCommand PathAndFlags;
  281. vs.rewind(0);
  282. if (m_outline.sorted())
  283. {
  284. reset();
  285. }
  286. while (!ShapePath.is_stop(PathAndFlags = vs.vertex(out x, out y)))
  287. {
  288. AddVertex(new VertexData(PathAndFlags, new Vector2(x, y)));
  289. }
  290. }
  291. //--------------------------------------------------------------------
  292. public int min_x()
  293. {
  294. return m_outline.min_x();
  295. }
  296. public int min_y()
  297. {
  298. return m_outline.min_y();
  299. }
  300. public int max_x()
  301. {
  302. return m_outline.max_x();
  303. }
  304. public int max_y()
  305. {
  306. return m_outline.max_y();
  307. }
  308. //--------------------------------------------------------------------
  309. private void sort()
  310. {
  311. if (m_auto_close) close_polygon();
  312. m_outline.sort_cells();
  313. }
  314. //------------------------------------------------------------------------
  315. public bool rewind_scanlines()
  316. {
  317. if (m_auto_close) close_polygon();
  318. m_outline.sort_cells();
  319. if (m_outline.total_cells() == 0)
  320. {
  321. return false;
  322. }
  323. m_scan_y = m_outline.min_y();
  324. return true;
  325. }
  326. //------------------------------------------------------------------------
  327. private bool navigate_scanline(int y)
  328. {
  329. if (m_auto_close) close_polygon();
  330. m_outline.sort_cells();
  331. if (m_outline.total_cells() == 0 ||
  332. y < m_outline.min_y() ||
  333. y > m_outline.max_y())
  334. {
  335. return false;
  336. }
  337. m_scan_y = y;
  338. return true;
  339. }
  340. //--------------------------------------------------------------------
  341. public int calculate_alpha(int area)
  342. {
  343. int cover = area >> ((int)poly_subpixel_scale_e.poly_subpixel_shift * 2 + 1 - (int)aa_scale_e.aa_shift);
  344. if (cover < 0)
  345. {
  346. cover = -cover;
  347. }
  348. if (m_filling_rule == filling_rule_e.fill_even_odd)
  349. {
  350. cover &= (int)aa_scale_e.aa_mask2;
  351. if (cover > (int)aa_scale_e.aa_scale)
  352. {
  353. cover = (int)aa_scale_e.aa_scale2 - cover;
  354. }
  355. }
  356. if (cover > (int)aa_scale_e.aa_mask)
  357. {
  358. cover = (int)aa_scale_e.aa_mask;
  359. }
  360. return (int)m_gamma[cover];
  361. }
  362. //--------------------------------------------------------------------
  363. public bool sweep_scanline(IScanlineCache scanlineCache)
  364. {
  365. for (; ; )
  366. {
  367. if (m_scan_y > m_outline.max_y())
  368. {
  369. return false;
  370. }
  371. scanlineCache.ResetSpans();
  372. int num_cells = (int)m_outline.scanline_num_cells(m_scan_y);
  373. PixelCellAa[] cells;
  374. int Offset;
  375. m_outline.scanline_cells(m_scan_y, out cells, out Offset);
  376. int cover = 0;
  377. while (num_cells != 0)
  378. {
  379. PixelCellAa cur_cell = cells[Offset];
  380. int x = cur_cell.x;
  381. int area = cur_cell.area;
  382. int alpha;
  383. cover += cur_cell.cover;
  384. //accumulate all cells with the same X
  385. while (--num_cells != 0)
  386. {
  387. Offset++;
  388. cur_cell = cells[Offset];
  389. if (cur_cell.x != x)
  390. {
  391. break;
  392. }
  393. area += cur_cell.area;
  394. cover += cur_cell.cover;
  395. }
  396. if (area != 0)
  397. {
  398. alpha = calculate_alpha((cover << ((int)poly_subpixel_scale_e.poly_subpixel_shift + 1)) - area);
  399. if (alpha != 0)
  400. {
  401. scanlineCache.add_cell(x, alpha);
  402. }
  403. x++;
  404. }
  405. if ((num_cells != 0) && (cur_cell.x > x))
  406. {
  407. alpha = calculate_alpha(cover << ((int)poly_subpixel_scale_e.poly_subpixel_shift + 1));
  408. if (alpha != 0)
  409. {
  410. scanlineCache.add_span(x, (cur_cell.x - x), alpha);
  411. }
  412. }
  413. }
  414. if (scanlineCache.num_spans() != 0) break;
  415. ++m_scan_y;
  416. }
  417. scanlineCache.finalize(m_scan_y);
  418. ++m_scan_y;
  419. return true;
  420. }
  421. //--------------------------------------------------------------------
  422. private bool hit_test(int tx, int ty)
  423. {
  424. if (!navigate_scanline(ty)) return false;
  425. //scanline_hit_test sl(tx);
  426. //sweep_scanline(sl);
  427. //return sl.hit();
  428. return true;
  429. }
  430. };
  431. }