SVGGraphicsFill.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. using UnityEngine;
  2. using UnityEngine.Profiling;
  3. using System;
  4. // TODO: Find a way to break this the hell up.
  5. // TODO: Normalize conventions away from Java-style SetX to properties.
  6. public class SVGGraphicsFill : ISVGPathDraw {
  7. private const sbyte FILL_FLAG = -1;
  8. private readonly int[,] _neighbor = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
  9. private readonly SVGGraphics graphics;
  10. private readonly SVGBasicDraw basicDraw;
  11. private sbyte flagStep;
  12. private sbyte[,] flag;
  13. private int width, height;
  14. private int subW, subH;
  15. private int inZoneL, inZoneT;
  16. private Vector2 boundTopLeft, boundBottomRight;
  17. public SVGGraphicsFill(SVGGraphics _graphics) {
  18. graphics = _graphics;
  19. flagStep = 0;
  20. width = 0;
  21. height = 0;
  22. subW = subH = 0;
  23. //Basic Draw
  24. basicDraw = new SVGBasicDraw { SetPixelMethod = SetPixelForFlag };
  25. }
  26. public Vector2 Size {
  27. set {
  28. width = (int)value.x;
  29. height = (int)value.y;
  30. subW = width;
  31. subH = height;
  32. flag = new sbyte[(int)width + 1, (int)height + 1];
  33. }
  34. }
  35. public void SetColor(Color color) {
  36. graphics.SetColor(color);
  37. }
  38. private void SetPixelForFlag(int x, int y) {
  39. if(isInZone(x, y))
  40. flag[x, y] = flagStep;
  41. }
  42. private bool isInZone(int x, int y) {
  43. return ((x >= inZoneL && x < subW + inZoneL) && (y >= inZoneT && y < subH + inZoneT));
  44. }
  45. private void ExpandBounds(Vector2 point) {
  46. if(point.x < boundTopLeft.x)
  47. boundTopLeft.x = point.x;
  48. if(point.y < boundTopLeft.y)
  49. boundTopLeft.y = point.y;
  50. if(point.x > boundBottomRight.x)
  51. boundBottomRight.x = point.x;
  52. if(point.y > boundBottomRight.y)
  53. boundBottomRight.y = point.y;
  54. }
  55. private void ExpandBounds(Vector2 point, float dx, float dy) {
  56. if(point.x - dy < boundTopLeft.x)
  57. boundTopLeft.x = point.x - dx;
  58. if(point.y - dx < boundTopLeft.y)
  59. boundTopLeft.y = point.y - dy;
  60. if(point.x + dx > boundBottomRight.x)
  61. boundBottomRight.x = point.x + dx;
  62. if(point.y + dy > boundBottomRight.y)
  63. boundBottomRight.y = point.y + dy;
  64. }
  65. private void ExpandBounds(Vector2[] points) {
  66. int _length = points.Length;
  67. for(int i = 0; i < _length; i++)
  68. ExpandBounds(points[i]);
  69. }
  70. private static readonly LiteStack<IntVector2> _stack = new LiteStack<IntVector2>();
  71. private struct IntVector2 {
  72. public int x;
  73. public int y;
  74. }
  75. private void Fill(int x, int y) {
  76. // Debug.LogFormat("Fill called: w:{0}, h:{1}, subW:{2}, subH:{3}, inZoneL:{4}, inZoneT:{5}, x:{6}, y:{7}", width, height, subW, subH, inZoneL, inZoneT, x, y);
  77. Profiler.BeginSample("SVGGraphicsFill.Fill");
  78. if(!isInZone(x, y) || flag[x, y] != 0) {
  79. Profiler.EndSample();
  80. return;
  81. }
  82. flag[x, y] = FILL_FLAG;
  83. _stack.Clear();
  84. int anticipatedCapacityNeed = ((width + height) / 2) * 5;
  85. if(_stack.Capacity < anticipatedCapacityNeed)
  86. _stack.Capacity = anticipatedCapacityNeed;
  87. IntVector2 temp = new IntVector2 { x = x, y = y };
  88. _stack.Push(temp);
  89. int nbIterations = 0;
  90. while(_stack.Count > 0) {
  91. ++nbIterations;
  92. temp = _stack.Pop();
  93. for(int t = 0; t < 4; ++t) {
  94. int tx = temp.x + _neighbor[t, 0];
  95. int ty = temp.y + _neighbor[t, 1];
  96. if(isInZone(tx, ty) && flag[tx, ty] == 0) {
  97. flag[tx, ty] = FILL_FLAG;
  98. _stack.Push(new IntVector2 { x = tx, y = ty });
  99. }
  100. }
  101. }
  102. // Debug.LogFormat("NbIter:{0}", nbIterations);
  103. Profiler.EndSample();
  104. }
  105. public void ResetSubBuffer() {
  106. boundTopLeft = new Vector2(+10000f, +10000f);
  107. boundBottomRight = new Vector2(-10000f, -10000f);
  108. subW = width;
  109. subH = height;
  110. inZoneL = 0;
  111. inZoneT = 0;
  112. for(int i = 0; i < width; i++)
  113. for(int j = 0; j < height; j++)
  114. flag[i, j] = 0;
  115. flagStep = 1;
  116. }
  117. private void PreEndSubBuffer() {
  118. if(boundTopLeft.x < 0f)
  119. boundTopLeft.x = 0f;
  120. if(boundTopLeft.y < 0f)
  121. boundTopLeft.y = 0f;
  122. if(boundBottomRight.x >= width)
  123. boundBottomRight.x = width - 1f;
  124. if(boundBottomRight.y >= height)
  125. boundBottomRight.y = height - 1f;
  126. subW = Math.Abs((int)boundTopLeft.x - (int)boundBottomRight.x) + 3;
  127. subH = Math.Abs((int)boundTopLeft.y - (int)boundBottomRight.y) + 3;
  128. inZoneL = (int)boundTopLeft.x - 1;
  129. inZoneT = (int)boundTopLeft.y - 1;
  130. inZoneL = (inZoneL < 0) ? 0 : inZoneL;
  131. inZoneT = (inZoneT < 0) ? 0 : inZoneT;
  132. inZoneL = (inZoneL >= width) ? (width - 1) : inZoneL;
  133. inZoneT = (inZoneT >= height) ? (height - 1) : inZoneT;
  134. subW = (subW + inZoneL > width) ? (width - inZoneL) : subW;
  135. subH = (subH + inZoneT > height) ? (height - inZoneT) : subH;
  136. Fill(inZoneL, inZoneT);
  137. // TODO: This seems buggy:
  138. if((inZoneL == 0) && (inZoneT == 0))
  139. Fill(subW - 1, subH - 1);
  140. }
  141. private void FillInZone() {
  142. for(int i = inZoneL; i < subW + inZoneL; i++)
  143. for(int j = inZoneT; j < subH + inZoneT; j++)
  144. if(flag[i, j] != FILL_FLAG)
  145. graphics.SetPixel(i, j);
  146. }
  147. public void EndSubBuffer() {
  148. PreEndSubBuffer();
  149. Fill(inZoneL, inZoneT);
  150. FillInZone();
  151. }
  152. public void EndSubBuffer(Vector2[] points) {
  153. PreEndSubBuffer();
  154. for(int i = 0; i < points.GetLength(0); i++)
  155. Fill((int)points[i].x, (int)points[i].y);
  156. FillInZone();
  157. }
  158. public void EndSubBuffer(Vector2 point) {
  159. PreEndSubBuffer();
  160. Fill((int)point.x, (int)point.y);
  161. FillInZone();
  162. }
  163. public void EndSubBuffer(SVGColor? strokePathColor) {
  164. PreEndSubBuffer();
  165. FillInZone();
  166. graphics.SetColor(strokePathColor.Value.color);
  167. FillInZone();
  168. }
  169. public void EndSubBuffer(SVGLinearGradientBrush linearGradientBrush) {
  170. PreEndSubBuffer();
  171. for(int i = inZoneL; i < subW + inZoneL; i++) {
  172. for(int j = inZoneT; j < subH + inZoneT; j++) {
  173. if(flag[i, j] == 0) {
  174. Color _color = linearGradientBrush.GetColor(i, j);
  175. graphics.SetColor(_color);
  176. graphics.SetPixel(i, j);
  177. }
  178. }
  179. }
  180. }
  181. public void EndSubBuffer(SVGLinearGradientBrush linearGradientBrush, SVGColor? strokePathColor) {
  182. PreEndSubBuffer();
  183. for(int i = inZoneL; i < subW + inZoneL; i++) {
  184. for(int j = inZoneT; j < subH + inZoneT; j++) {
  185. if(flag[i, j] != FILL_FLAG) {
  186. Color _color = linearGradientBrush.GetColor(i, j);
  187. graphics.SetColor(_color);
  188. graphics.SetPixel(i, j);
  189. }
  190. }
  191. }
  192. graphics.SetColor(strokePathColor.Value.color);
  193. FillInZone();
  194. }
  195. public void EndSubBuffer(SVGRadialGradientBrush radialGradientBrush) {
  196. PreEndSubBuffer();
  197. for(int i = inZoneL; i < subW + inZoneL; i++) {
  198. for(int j = inZoneT; j < subH + inZoneT; j++) {
  199. if(flag[i, j] == 0) {
  200. Color _color = radialGradientBrush.GetColor(i, j);
  201. graphics.SetColor(_color);
  202. graphics.SetPixel(i, j);
  203. }
  204. }
  205. }
  206. }
  207. public void EndSubBuffer(SVGRadialGradientBrush radialGradientBrush, SVGColor? strokePathColor) {
  208. PreEndSubBuffer();
  209. for(int i = inZoneL; i < subW + inZoneL; ++i) {
  210. for(int j = inZoneT; j < subH + inZoneT; ++j) {
  211. if(flag[i, j] != FILL_FLAG) {
  212. Color _color = radialGradientBrush.GetColor(i, j);
  213. graphics.SetColor(_color);
  214. graphics.SetPixel(i, j);
  215. }
  216. }
  217. }
  218. graphics.SetColor(strokePathColor.Value.color);
  219. FillInZone();
  220. }
  221. private void PreRect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
  222. ResetSubBuffer();
  223. ExpandBounds(p1);
  224. ExpandBounds(p2);
  225. ExpandBounds(p3);
  226. ExpandBounds(p4);
  227. basicDraw.MoveTo(p1);
  228. basicDraw.LineTo(p2);
  229. basicDraw.LineTo(p3);
  230. basicDraw.LineTo(p4);
  231. basicDraw.LineTo(p1);
  232. }
  233. public void Rect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
  234. PreRect(p1, p2, p3, p4);
  235. EndSubBuffer();
  236. }
  237. public void Rect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, SVGColor? strokeColor) {
  238. PreRect(p1, p2, p3, p4);
  239. EndSubBuffer(strokeColor);
  240. }
  241. public void Rect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, SVGColor fillColor, SVGColor? strokeColor) {
  242. SetColor(fillColor.color);
  243. PreRect(p1, p2, p3, p4);
  244. EndSubBuffer(strokeColor);
  245. }
  246. private void PreRoundedRect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 p5, Vector2 p6, Vector2 p7,
  247. Vector2 p8, float r1, float r2, float angle) {
  248. float dxy = ((r1 > r2) ? (int)r1 : (int)r2);
  249. ResetSubBuffer();
  250. ExpandBounds(p1, dxy, dxy);
  251. ExpandBounds(p2, dxy, dxy);
  252. ExpandBounds(p3, dxy, dxy);
  253. ExpandBounds(p4, dxy, dxy);
  254. ExpandBounds(p5, dxy, dxy);
  255. ExpandBounds(p6, dxy, dxy);
  256. ExpandBounds(p7, dxy, dxy);
  257. ExpandBounds(p8, dxy, dxy);
  258. basicDraw.MoveTo(p1);
  259. basicDraw.LineTo(p2);
  260. basicDraw.ArcTo(r1, r2, angle, false, true, p3);
  261. basicDraw.MoveTo(p3);
  262. basicDraw.LineTo(p4);
  263. basicDraw.ArcTo(r1, r2, angle, false, true, p5);
  264. basicDraw.MoveTo(p5);
  265. basicDraw.LineTo(p6);
  266. basicDraw.ArcTo(r1, r2, angle, false, true, p7);
  267. basicDraw.MoveTo(p7);
  268. basicDraw.LineTo(p8);
  269. basicDraw.ArcTo(r1, r2, angle, false, true, p1);
  270. }
  271. public void RoundedRect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 p5, Vector2 p6, Vector2 p7, Vector2 p8,
  272. float r1, float r2,
  273. float angle) {
  274. PreRoundedRect(p1, p2, p3, p4, p5, p6, p7, p8, r1, r2,
  275. angle);
  276. EndSubBuffer();
  277. }
  278. public void RoundedRect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 p5, Vector2 p6, Vector2 p7, Vector2 p8,
  279. float r1, float r2,
  280. float angle, SVGColor? strokeColor) {
  281. PreRoundedRect(p1, p2, p3, p4, p5, p6, p7, p8, r1, r2,
  282. angle);
  283. EndSubBuffer(strokeColor);
  284. }
  285. public void RoundedRect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 p5, Vector2 p6, Vector2 p7, Vector2 p8,
  286. float r1, float r2,
  287. float angle, SVGColor fillColor, SVGColor? strokeColor) {
  288. SetColor(fillColor.color);
  289. PreRoundedRect(p1, p2, p3, p4, p5, p6, p7, p8, r1, r2,
  290. angle);
  291. EndSubBuffer(strokeColor);
  292. }
  293. private void PreCircle(Vector2 p, float r) {
  294. ResetSubBuffer();
  295. ExpandBounds(p, (int)r + 2, (int)r + 2);
  296. basicDraw.Circle(p, r);
  297. }
  298. public void Circle(Vector2 p, float r) {
  299. PreCircle(p, r);
  300. EndSubBuffer();
  301. }
  302. public void Circle(Vector2 p, float r, SVGColor? strokeColor) {
  303. PreCircle(p, r);
  304. EndSubBuffer(strokeColor);
  305. }
  306. public void Circle(Vector2 p, float r, SVGColor fillColor, SVGColor? strokeColor) {
  307. SetColor(fillColor.color);
  308. PreCircle(p, r);
  309. EndSubBuffer();
  310. }
  311. private void PreEllipse(Vector2 p, float rx, float ry, float angle) {
  312. int d = (rx > ry) ? (int)rx : (int)ry;
  313. ExpandBounds(p, d, d);
  314. basicDraw.Ellipse(p, (int)rx, (int)ry, angle);
  315. }
  316. public void Ellipse(Vector2 p, float rx, float ry, float angle) {
  317. PreEllipse(p, rx, ry, angle);
  318. EndSubBuffer();
  319. }
  320. public void Ellipse(Vector2 p, float rx, float ry, float angle, SVGColor? strokeColor) {
  321. PreEllipse(p, rx, ry, angle);
  322. EndSubBuffer(strokeColor);
  323. }
  324. public void Ellipse(Vector2 p, float rx, float ry, float angle, SVGColor fillColor, SVGColor? strokeColor) {
  325. SetColor(fillColor.color);
  326. PreEllipse(p, rx, ry, angle);
  327. EndSubBuffer(strokeColor);
  328. }
  329. private void PrePolygon(Vector2[] points) {
  330. if((points != null) && (points.GetLength(0) > 0)) {
  331. ResetSubBuffer();
  332. ExpandBounds(points);
  333. basicDraw.MoveTo(points[0]);
  334. int _length = points.GetLength(0);
  335. for(int i = 1; i < _length; i++)
  336. basicDraw.LineTo(points[i]);
  337. basicDraw.LineTo(points[0]);
  338. }
  339. }
  340. public void Polygon(Vector2[] points) {
  341. PrePolygon(points);
  342. EndSubBuffer();
  343. }
  344. public void Polygon(Vector2[] points, SVGColor? strokeColor) {
  345. PrePolygon(points);
  346. EndSubBuffer(strokeColor);
  347. }
  348. public void Polygon(Vector2[] points, SVGColor fillColor, SVGColor? strokeColor) {
  349. SetColor(fillColor.color);
  350. PrePolygon(points);
  351. EndSubBuffer(strokeColor);
  352. }
  353. public void FillPath(SVGGraphicsPath graphicsPath) {
  354. ResetSubBuffer();
  355. graphicsPath.RenderPath(this, true);
  356. EndSubBuffer();
  357. }
  358. public void FillPath(SVGGraphicsPath graphicsPath, Vector2[] points) {
  359. ResetSubBuffer();
  360. graphicsPath.RenderPath(this, true);
  361. EndSubBuffer(points);
  362. }
  363. public void FillPath(SVGGraphicsPath graphicsPath, Vector2 point) {
  364. ResetSubBuffer();
  365. graphicsPath.RenderPath(this, true);
  366. EndSubBuffer(point);
  367. }
  368. public void FillPath(SVGColor fillColor, SVGGraphicsPath graphicsPath) {
  369. SetColor(fillColor.color);
  370. FillPath(graphicsPath);
  371. }
  372. public void FillPath(SVGColor fillColor, SVGColor? strokePathColor, SVGGraphicsPath graphicsPath) {
  373. ResetSubBuffer();
  374. graphicsPath.RenderPath(this, true);
  375. if(strokePathColor != null)
  376. EndSubBuffer(strokePathColor);
  377. else
  378. EndSubBuffer();
  379. }
  380. public void FillPath(SVGLinearGradientBrush linearGradientBrush, SVGGraphicsPath graphicsPath) {
  381. ResetSubBuffer();
  382. graphicsPath.RenderPath(this, true);
  383. EndSubBuffer(linearGradientBrush);
  384. }
  385. public void FillPath(SVGLinearGradientBrush linearGradientBrush, SVGColor? strokePathColor,
  386. SVGGraphicsPath graphicsPath) {
  387. ResetSubBuffer();
  388. graphicsPath.RenderPath(this, true);
  389. if(strokePathColor != null)
  390. EndSubBuffer(linearGradientBrush, strokePathColor);
  391. else
  392. EndSubBuffer(linearGradientBrush);
  393. }
  394. public void FillPath(SVGRadialGradientBrush radialGradientBrush, SVGGraphicsPath graphicsPath) {
  395. ResetSubBuffer();
  396. graphicsPath.RenderPath(this, true);
  397. EndSubBuffer(radialGradientBrush);
  398. }
  399. public void FillPath(SVGRadialGradientBrush radialGradientBrush, SVGColor? strokePathColor,
  400. SVGGraphicsPath graphicsPath) {
  401. ResetSubBuffer();
  402. graphicsPath.RenderPath(this, true);
  403. if(strokePathColor != null)
  404. EndSubBuffer(radialGradientBrush, strokePathColor);
  405. else
  406. EndSubBuffer(radialGradientBrush);
  407. }
  408. public void CircleTo(Vector2 p, float r) {
  409. ExpandBounds(p, (int)r + 1, (int)r + 1);
  410. basicDraw.Circle(p, r);
  411. }
  412. public void EllipseTo(Vector2 p, float rx, float ry, float angle) {
  413. int d = (rx > ry) ? (int)rx + 2 : (int)ry + 2;
  414. ExpandBounds(p, d, d);
  415. basicDraw.Ellipse(p, (int)rx, (int)ry, angle);
  416. }
  417. public void LineTo(Vector2 p) {
  418. ExpandBounds(p);
  419. basicDraw.LineTo(p);
  420. }
  421. public void MoveTo(Vector2 p) {
  422. ExpandBounds(p);
  423. basicDraw.MoveTo(p);
  424. }
  425. public void ArcTo(float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, Vector2 p) {
  426. ExpandBounds(p, (r1 > r2) ? 2 * (int)r1 + 2 : 2 * (int)r2 + 2, (r1 > r2) ? 2 * (int)r1 + 2 : 2 * (int)r2 + 2);
  427. basicDraw.ArcTo(r1, r2, angle, largeArcFlag, sweepFlag, p);
  428. }
  429. public void CubicCurveTo(Vector2 p1, Vector2 p2, Vector2 p) {
  430. ExpandBounds(p1);
  431. ExpandBounds(p2);
  432. ExpandBounds(p);
  433. basicDraw.CubicCurveTo(p1, p2, p);
  434. }
  435. public void QuadraticCurveTo(Vector2 p1, Vector2 p) {
  436. ExpandBounds(p1);
  437. ExpandBounds(p);
  438. basicDraw.QuadraticCurveTo(p1, p);
  439. }
  440. }