FloodToolBase.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. using PaintDotNet.Measurement.Enum;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Windows.Forms;
  6. namespace PaintDotNet.Measurement.Tools
  7. {
  8. public abstract class FloodToolBase : Tool
  9. {
  10. private bool contiguous;
  11. private bool clipToSelection = true;
  12. protected bool ClipToSelection
  13. {
  14. get
  15. {
  16. return clipToSelection;
  17. }
  18. set
  19. {
  20. clipToSelection = value;
  21. }
  22. }
  23. public FloodToolBase(IDocumentWorkspace documentWorkspace, ImageResource toolBarImage, string name,
  24. string helpText, char hotKey, bool skipIfActiveOnHotKey, ToolBarConfigItems toolBarConfigItems)
  25. : base(documentWorkspace, toolBarImage, name, helpText, hotKey, skipIfActiveOnHotKey,
  26. ToolBarConfigItems.FloodMode | ToolBarConfigItems.Tolerance | toolBarConfigItems)
  27. {
  28. }
  29. private static bool CheckColor(ColorBgra a, ColorBgra b, int tolerance)
  30. {
  31. int sum = 0;
  32. int diff;
  33. diff = a.R - b.R;
  34. sum += (1 + diff * diff) * a.A / 256;
  35. diff = a.G - b.G;
  36. sum += (1 + diff * diff) * a.A / 256;
  37. diff = a.B - b.B;
  38. sum += (1 + diff * diff) * a.A / 256;
  39. diff = a.A - b.A;
  40. sum += diff * diff;
  41. return (sum <= tolerance * tolerance * 4);
  42. }
  43. public unsafe static void FillStencilByColor(Surface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance,
  44. out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection)
  45. {
  46. int top = int.MaxValue;
  47. int bottom = int.MinValue;
  48. int left = int.MaxValue;
  49. int right = int.MinValue;
  50. Rectangle[] scans;
  51. stencil.Clear(false);
  52. if (limitToSelection)
  53. {
  54. using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height)))
  55. {
  56. excluded.Xor(limitRegion);
  57. scans = excluded.GetRegionScansReadOnlyInt();
  58. }
  59. }
  60. else
  61. {
  62. scans = new Rectangle[0];
  63. }
  64. foreach (Rectangle rect in scans)
  65. {
  66. stencil.Set(rect, true);
  67. }
  68. for (int y = 0; y < surface.Height; ++y)
  69. {
  70. bool foundPixelInRow = false;
  71. ColorBgra* ptr = surface.GetRowAddressUnchecked(y);
  72. for (int x = 0; x < surface.Width; ++x)
  73. {
  74. if (CheckColor(cmp, *ptr, tolerance))
  75. {
  76. stencil.SetUnchecked(x, y, true);
  77. if (x < left)
  78. {
  79. left = x;
  80. }
  81. if (x > right)
  82. {
  83. right = x;
  84. }
  85. foundPixelInRow = true;
  86. }
  87. ++ptr;
  88. }
  89. if (foundPixelInRow)
  90. {
  91. if (y < top)
  92. {
  93. top = y;
  94. }
  95. if (y >= bottom)
  96. {
  97. bottom = y;
  98. }
  99. }
  100. }
  101. foreach (Rectangle rect in scans)
  102. {
  103. stencil.Set(rect, false);
  104. }
  105. boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);
  106. }
  107. public unsafe static void FillStencilFromPoint(Surface surface, IBitVector2D stencil, Point start,
  108. int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection)
  109. {
  110. ColorBgra cmp = surface[start];
  111. int top = int.MaxValue;
  112. int bottom = int.MinValue;
  113. int left = int.MaxValue;
  114. int right = int.MinValue;
  115. Rectangle[] scans;
  116. stencil.Clear(false);
  117. if (limitToSelection)
  118. {
  119. using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height)))
  120. {
  121. excluded.Xor(limitRegion);
  122. scans = excluded.GetRegionScansReadOnlyInt();
  123. }
  124. }
  125. else
  126. {
  127. scans = new Rectangle[0];
  128. }
  129. foreach (Rectangle rect in scans)
  130. {
  131. stencil.Set(rect, true);
  132. }
  133. Queue<Point> queue = new Queue<Point>(16);
  134. queue.Enqueue(start);
  135. while (queue.Count > 0)
  136. {
  137. Point pt = queue.Dequeue();
  138. ColorBgra* rowPtr = surface.GetRowAddressUnchecked(pt.Y);
  139. int localLeft = pt.X - 1;
  140. int localRight = pt.X;
  141. while (localLeft >= 0 &&
  142. !stencil.GetUnchecked(localLeft, pt.Y) &&
  143. CheckColor(cmp, rowPtr[localLeft], tolerance))
  144. {
  145. stencil.SetUnchecked(localLeft, pt.Y, true);
  146. --localLeft;
  147. }
  148. while (localRight < surface.Width &&
  149. !stencil.GetUnchecked(localRight, pt.Y) &&
  150. CheckColor(cmp, rowPtr[localRight], tolerance))
  151. {
  152. stencil.SetUnchecked(localRight, pt.Y, true);
  153. ++localRight;
  154. }
  155. ++localLeft;
  156. --localRight;
  157. if (pt.Y > 0)
  158. {
  159. int sleft = localLeft;
  160. int sright = localLeft;
  161. ColorBgra* rowPtrUp = surface.GetRowAddressUnchecked(pt.Y - 1);
  162. for (int sx = localLeft; sx <= localRight; ++sx)
  163. {
  164. if (!stencil.GetUnchecked(sx, pt.Y - 1) &&
  165. CheckColor(cmp, rowPtrUp[sx], tolerance))
  166. {
  167. ++sright;
  168. }
  169. else
  170. {
  171. if (sright - sleft > 0)
  172. {
  173. queue.Enqueue(new Point(sleft, pt.Y - 1));
  174. }
  175. ++sright;
  176. sleft = sright;
  177. }
  178. }
  179. if (sright - sleft > 0)
  180. {
  181. queue.Enqueue(new Point(sleft, pt.Y - 1));
  182. }
  183. }
  184. if (pt.Y < surface.Height - 1)
  185. {
  186. int sleft = localLeft;
  187. int sright = localLeft;
  188. ColorBgra* rowPtrDown = surface.GetRowAddressUnchecked(pt.Y + 1);
  189. for (int sx = localLeft; sx <= localRight; ++sx)
  190. {
  191. if (!stencil.GetUnchecked(sx, pt.Y + 1) &&
  192. CheckColor(cmp, rowPtrDown[sx], tolerance))
  193. {
  194. ++sright;
  195. }
  196. else
  197. {
  198. if (sright - sleft > 0)
  199. {
  200. queue.Enqueue(new Point(sleft, pt.Y + 1));
  201. }
  202. ++sright;
  203. sleft = sright;
  204. }
  205. }
  206. if (sright - sleft > 0)
  207. {
  208. queue.Enqueue(new Point(sleft, pt.Y + 1));
  209. }
  210. }
  211. if (localLeft < left)
  212. {
  213. left = localLeft;
  214. }
  215. if (localRight > right)
  216. {
  217. right = localRight;
  218. }
  219. if (pt.Y < top)
  220. {
  221. top = pt.Y;
  222. }
  223. if (pt.Y > bottom)
  224. {
  225. bottom = pt.Y;
  226. }
  227. }
  228. foreach (Rectangle rect in scans)
  229. {
  230. stencil.Set(rect, false);
  231. }
  232. boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);
  233. }
  234. protected abstract void OnFillRegionComputed(Point[][] polygonSet);
  235. protected override void OnMouseDown(MouseEventArgs e)
  236. {
  237. Point pos = new Point(e.X, e.Y);
  238. switch (AppEnvironment.FloodMode())
  239. {
  240. case FloodMode.Local:
  241. this.contiguous = true;
  242. break;
  243. case FloodMode.Global:
  244. this.contiguous = false;
  245. break;
  246. default:
  247. throw new InvalidEnumArgumentException();
  248. }
  249. if ((ModifierKeys & Keys.Shift) != 0)
  250. {
  251. this.contiguous = !this.contiguous;
  252. }
  253. if (Document.Bounds.Contains(pos))
  254. {
  255. base.OnMouseDown(e);
  256. PdnRegion currentRegion = Selection.CreateRegion();
  257. // See if the mouse click is valid
  258. if (!currentRegion.IsVisible(pos) && clipToSelection)
  259. {
  260. currentRegion.Dispose();
  261. currentRegion = null;
  262. return;
  263. }
  264. // Set the current surface, color picked and color to draw
  265. Surface surface = ((BitmapLayer)ActiveLayer).Surface;
  266. IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter(this.ScratchSurface);
  267. Rectangle boundingBox;
  268. int tolerance = (int)(AppEnvironment.Tolerance() * AppEnvironment.Tolerance() * 256);
  269. if (contiguous)
  270. {
  271. // FloodMode.Local
  272. FillStencilFromPoint(surface, stencilBuffer, pos, tolerance, out boundingBox, currentRegion, clipToSelection);
  273. }
  274. else
  275. {
  276. // FloodMode.Global
  277. FillStencilByColor(surface, stencilBuffer, surface[pos], tolerance, out boundingBox, currentRegion, clipToSelection);
  278. }
  279. Point[][] polygonSet = PdnGraphicsPath.PolygonSetFromStencil(stencilBuffer, boundingBox, 0, 0);
  280. OnFillRegionComputed(polygonSet);
  281. }
  282. base.OnMouseDown(e);
  283. }
  284. }
  285. }