CloneStampTool.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. using PaintDotNet.Measurement.HistoryMementos;
  2. using System;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.Windows.Forms;
  6. namespace PaintDotNet.Measurement.Tools
  7. {
  8. /// <summary>
  9. /// Ctrl left-click to select an origin, left click to place it
  10. /// </summary>
  11. public class CloneStampTool : Tool
  12. {
  13. private class StaticData
  14. {
  15. public Point takeFrom;
  16. public Point lastMoved;
  17. public bool updateSrcPreview;
  18. public WeakReference wr;
  19. }
  20. private new StaticData GetStaticData()
  21. {
  22. object staticData = base.GetStaticData();
  23. if (staticData == null)
  24. {
  25. staticData = new StaticData();
  26. base.SetStaticData(staticData);
  27. }
  28. return (StaticData)staticData;
  29. }
  30. private BitmapLayer takeFromLayer;
  31. private bool switchedTo = false;
  32. private Rectangle undoRegion = Rectangle.Empty;
  33. private PdnRegion savedRegion;
  34. private RenderArgs ra;
  35. private bool mouseUp = true;
  36. private Vector<Rectangle> historyRects;
  37. private bool antialiasing;
  38. private PdnRegion clipRegion;
  39. private BrushPreviewRenderer rendererDst;
  40. private BrushPreviewRenderer rendererSrc;
  41. // private bool added by MK for "clone source" cursor transition
  42. private bool mouseDownSettingCloneSource;
  43. private Cursor cursorMouseDown, cursorMouseUp, cursorMouseDownSetSource;
  44. private bool IsShiftDown()
  45. {
  46. return ModifierKeys == Keys.Shift;
  47. }
  48. private bool IsCtrlDown()
  49. {
  50. return ModifierKeys == Keys.Control;
  51. }
  52. /// <summary>
  53. /// Button down mouse left. Returns true if only the left mouse button is depressed.
  54. /// </summary>
  55. /// <param name="e"></param>
  56. /// <returns></returns>
  57. private bool IsMouseLeftDown(MouseEventArgs e)
  58. {
  59. return e.Button == MouseButtons.Left;
  60. }
  61. /// <summary>
  62. /// Button down mouse right. Returns true if only the right mouse is depressed.
  63. /// </summary>
  64. /// <param name="e"></param>
  65. /// <returns></returns>
  66. private bool IsMouseRightDown(MouseEventArgs e)
  67. {
  68. return e.Button == MouseButtons.Right;
  69. }
  70. protected override void OnMouseEnter()
  71. {
  72. this.rendererDst.Visible = true;
  73. base.OnMouseEnter();
  74. }
  75. protected override void OnMouseLeave()
  76. {
  77. this.rendererDst.Visible = false;
  78. base.OnMouseLeave();
  79. }
  80. public CloneStampTool(IDocumentWorkspace documentWorkspace)
  81. : base(documentWorkspace,
  82. PdnResources.GetImageResource("Icons.CloneStampToolIcon.png"),
  83. PdnResources.GetString("CloneStampTool.Name"),
  84. PdnResources.GetString("CloneStampTool.HelpText"),
  85. 'l',
  86. false,
  87. ToolBarConfigItems.Pen | ToolBarConfigItems.Antialiasing)
  88. {
  89. }
  90. protected override void OnPulse()
  91. {
  92. double time = (double)new SystemLayer.Timing().GetTickCount();
  93. double sin = Math.Sin(time / 300.0);
  94. int alpha = (int)Math.Ceiling((((sin + 1.0) / 2.0) * 224.0) + 31.0);
  95. this.rendererSrc.BrushAlpha = alpha;
  96. base.OnPulse();
  97. }
  98. protected override void OnActivate()
  99. {
  100. base.OnActivate();
  101. cursorMouseDown = new Cursor(PdnResources.GetResourceStream("Cursors.GenericToolCursorMouseDown.cur"));
  102. cursorMouseDownSetSource = new Cursor(PdnResources.GetResourceStream("Cursors.CloneStampToolCursorSetSource.cur"));
  103. cursorMouseUp = new Cursor(PdnResources.GetResourceStream("Cursors.CloneStampToolCursor.cur"));
  104. this.Cursor = cursorMouseUp;
  105. this.rendererDst = new BrushPreviewRenderer(this.RendererList);
  106. this.RendererList.Add(this.rendererDst, false);
  107. this.rendererSrc = new BrushPreviewRenderer(this.RendererList);
  108. this.rendererSrc.BrushLocation = GetStaticData().takeFrom;
  109. this.rendererSrc.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  110. this.rendererSrc.Visible = (GetStaticData().takeFrom != Point.Empty);
  111. this.RendererList.Add(this.rendererSrc, false);
  112. if (ActiveLayer != null)
  113. {
  114. switchedTo = true;
  115. historyRects = new Vector<Rectangle>();
  116. if (GetStaticData().wr != null && GetStaticData().wr.IsAlive)
  117. {
  118. takeFromLayer = (BitmapLayer)GetStaticData().wr.Target;
  119. }
  120. else
  121. {
  122. takeFromLayer = null;
  123. }
  124. }
  125. AppEnvironment.PenInfoChanged += new EventHandler(Environment_PenInfoChanged);
  126. }
  127. protected override void OnDeactivate()
  128. {
  129. if (!this.mouseUp)
  130. {
  131. StaticData sd = GetStaticData();
  132. Point lastXY = Point.Empty;
  133. if (sd != null)
  134. {
  135. lastXY = sd.lastMoved;
  136. }
  137. OnMouseUp(new MouseEventArgs(MouseButtons.Left, 0, lastXY.X, lastXY.Y, 0));
  138. }
  139. AppEnvironment.PenInfoChanged -= new EventHandler(Environment_PenInfoChanged);
  140. this.RendererList.Remove(this.rendererDst);
  141. this.rendererDst.Dispose();
  142. this.rendererDst = null;
  143. this.RendererList.Remove(this.rendererSrc);
  144. this.rendererSrc.Dispose();
  145. this.rendererSrc = null;
  146. if (cursorMouseDown != null)
  147. {
  148. cursorMouseDown.Dispose();
  149. cursorMouseDown = null;
  150. }
  151. if (cursorMouseUp != null)
  152. {
  153. cursorMouseUp.Dispose();
  154. cursorMouseUp = null;
  155. }
  156. if (cursorMouseDownSetSource != null)
  157. {
  158. cursorMouseDownSetSource.Dispose();
  159. cursorMouseDownSetSource = null;
  160. }
  161. base.OnDeactivate();
  162. }
  163. protected override void OnKeyDown(KeyEventArgs e)
  164. {
  165. if (IsCtrlDown() && mouseUp)
  166. {
  167. Cursor = cursorMouseDownSetSource;
  168. mouseDownSettingCloneSource = true;
  169. }
  170. base.OnKeyDown(e);
  171. }
  172. protected override void OnKeyUp(KeyEventArgs e)
  173. {
  174. // this isn't likely the best way to check to see if
  175. // the CTRL key has been let up. If it's not, version
  176. // 2.1 can address the discrepancy.
  177. if (!IsCtrlDown() && mouseDownSettingCloneSource)
  178. {
  179. Cursor = cursorMouseUp;
  180. mouseDownSettingCloneSource = false;
  181. }
  182. base.OnKeyUp(e);
  183. }
  184. protected override void OnMouseUp(MouseEventArgs e)
  185. {
  186. mouseUp = true;
  187. if (!mouseDownSettingCloneSource)
  188. {
  189. Cursor = cursorMouseUp;
  190. }
  191. if (IsMouseLeftDown(e))
  192. {
  193. this.rendererDst.Visible = true;
  194. if (savedRegion != null)
  195. {
  196. //RestoreRegion(this.savedRegion);
  197. ActiveLayer.Invalidate(this.savedRegion.GetBoundsInt());
  198. savedRegion.Dispose();
  199. savedRegion = null;
  200. Update();
  201. }
  202. if (GetStaticData().takeFrom == Point.Empty || GetStaticData().lastMoved == Point.Empty)
  203. {
  204. return;
  205. }
  206. if (historyRects.Count > 0)
  207. {
  208. PdnRegion saveMeRegion;
  209. Rectangle[] rectsRO;
  210. int rectsROLength;
  211. this.historyRects.GetArrayReadOnly(out rectsRO, out rectsROLength);
  212. saveMeRegion = Utility.RectanglesToRegion(rectsRO, 0, rectsROLength);
  213. PdnRegion simplifiedRegion = Utility.SimplifyAndInflateRegion(saveMeRegion);
  214. SaveRegion(simplifiedRegion, simplifiedRegion.GetBoundsInt());
  215. historyRects = new Vector<Rectangle>();
  216. HistoryMemento ha = new BitmapHistoryMemento(Name, Image, DocumentWorkspace, ActiveLayerIndex,
  217. simplifiedRegion, this.ScratchSurface);
  218. //HistoryStack.PushNewMemento(ha);
  219. this.ClearSavedMemory();
  220. }
  221. }
  222. }
  223. private unsafe void DrawACircle(PointF pt, Surface srfSrc, Surface srfDst, Point difference, Rectangle rect)
  224. {
  225. float bw = AppEnvironment.PenInfo().Width / 2;
  226. float envAlpha = AppEnvironment.GetPrimaryColorA() / 255.0f;
  227. rect.Intersect(new Rectangle(difference, srfSrc.Size));
  228. rect.Intersect(srfDst.Bounds);
  229. if (rect.Width == 0 || rect.Height == 0)
  230. {
  231. return;
  232. }
  233. // envAlpha = envAlpha^4
  234. envAlpha *= envAlpha;
  235. envAlpha *= envAlpha;
  236. for (int y = rect.Top; y < rect.Bottom; y++)
  237. {
  238. ColorBgra* srcRow = srfSrc.GetRowAddressUnchecked(y - difference.Y);
  239. ColorBgra* dstRow = srfDst.GetRowAddressUnchecked(y);
  240. for (int x = rect.Left; x < rect.Right; x++)
  241. {
  242. ColorBgra* srcPtr = unchecked(srcRow + x - difference.X);
  243. ColorBgra* dstPtr = unchecked(dstRow + x);
  244. float distFromRing = 0.5f + bw - Utility.Distance(pt, new PointF(x, y));
  245. if (distFromRing > 0)
  246. {
  247. float alpha = antialiasing ? Utility.Clamp(distFromRing * envAlpha, 0, 1) : 1;
  248. alpha *= srcPtr->A / 255.0f;
  249. dstPtr->A = (byte)(255 - (255 - dstPtr->A) * (1 - alpha));
  250. if (0 == (alpha + (1 - alpha) * dstPtr->A / 255))
  251. {
  252. dstPtr->Bgra = 0;
  253. }
  254. else
  255. {
  256. dstPtr->R = (byte)((srcPtr->R * alpha + dstPtr->R * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255));
  257. dstPtr->G = (byte)((srcPtr->G * alpha + dstPtr->G * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255));
  258. dstPtr->B = (byte)((srcPtr->B * alpha + dstPtr->B * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255));
  259. }
  260. }
  261. }
  262. }
  263. rect.Inflate(1, 1);
  264. Document.Invalidate(rect);
  265. }
  266. private void DrawCloneLine(Point currentMouse, Point lastMoved, Point lastTakeFrom, Surface surfaceSource, Surface surfaceDest)
  267. {
  268. Rectangle[] rectSelRegions;
  269. Rectangle rectBrushArea;
  270. int penWidth = (int)AppEnvironment.PenInfo().Width;
  271. int ceilingPenWidth = (int)Math.Ceiling((double)penWidth);
  272. if (mouseUp || switchedTo)
  273. {
  274. lastMoved = currentMouse;
  275. lastTakeFrom = GetStaticData().takeFrom;
  276. mouseUp = false;
  277. switchedTo = false;
  278. }
  279. Point difference = new Point(currentMouse.X - GetStaticData().takeFrom.X, currentMouse.Y - GetStaticData().takeFrom.Y);
  280. Point direction = new Point(currentMouse.X - lastMoved.X, currentMouse.Y - lastMoved.Y);
  281. float length = Utility.Magnitude(direction);
  282. float bw = 1 + AppEnvironment.PenInfo().Width / 2;
  283. rectSelRegions = this.clipRegion.GetRegionScansReadOnlyInt();
  284. Rectangle rect = Utility.PointsToRectangle(lastMoved, currentMouse);
  285. rect.Inflate(penWidth / 2 + 1, penWidth / 2 + 1);
  286. rect.Intersect(new Rectangle(difference, surfaceSource.Size));
  287. rect.Intersect(surfaceDest.Bounds);
  288. if (rect.Width == 0 || rect.Height == 0)
  289. {
  290. return;
  291. }
  292. SaveRegion(null, rect);
  293. historyRects.Add(rect);
  294. // Follow the line to draw the clone... line
  295. float fInc;
  296. try
  297. {
  298. fInc = (float)Math.Sqrt(bw) / length;
  299. }
  300. catch (DivideByZeroException)
  301. {
  302. // See bug #1796
  303. return;
  304. }
  305. for (float f = 0; f < 1; f += fInc)
  306. {
  307. // Do intersects with each of the rectangles in a selection
  308. foreach (Rectangle rectSel in rectSelRegions)
  309. {
  310. PointF p = new PointF(currentMouse.X * (1 - f) + f * lastMoved.X,
  311. currentMouse.Y * (1 - f) + f * lastMoved.Y);
  312. rectBrushArea = new Rectangle((int)(p.X - bw), (int)(p.Y - bw), (int)(bw * 2 + 1), (int)(bw * 2 + 1));
  313. Rectangle rectBrushArea2 = new Rectangle(
  314. rectBrushArea.X - difference.X,
  315. rectBrushArea.Y - difference.Y,
  316. rectBrushArea.Width,
  317. rectBrushArea.Height);
  318. if (rectBrushArea.IntersectsWith(rectSel))
  319. {
  320. rectBrushArea.Intersect(rectSel);
  321. SaveRegion(null, rectBrushArea);
  322. SaveRegion(null, rectBrushArea2);
  323. DrawACircle(p, surfaceSource, surfaceDest, difference, rectBrushArea);
  324. }
  325. }
  326. }
  327. }
  328. protected override void OnMouseMove(MouseEventArgs e)
  329. {
  330. base.OnMouseMove(e);
  331. this.rendererDst.BrushLocation = new Point(e.X, e.Y);
  332. this.rendererDst.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  333. if (!(ActiveLayer is BitmapLayer) || (takeFromLayer == null))
  334. {
  335. return;
  336. }
  337. if (GetStaticData().updateSrcPreview)
  338. {
  339. Point currentMouse = new Point(e.X, e.Y);
  340. Point difference = new Point(currentMouse.X - GetStaticData().lastMoved.X, currentMouse.Y - GetStaticData().lastMoved.Y);
  341. this.rendererSrc.BrushLocation = new Point(GetStaticData().takeFrom.X + difference.X, GetStaticData().takeFrom.Y + difference.Y); ;
  342. this.rendererSrc.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  343. }
  344. if (IsMouseLeftDown(e) &&
  345. (GetStaticData().takeFrom != Point.Empty) &&
  346. !IsCtrlDown())
  347. {
  348. Point currentMouse = new Point(e.X, e.Y);
  349. Point lastTakeFrom = Point.Empty;
  350. lastTakeFrom = GetStaticData().takeFrom;
  351. if (GetStaticData().lastMoved != Point.Empty)
  352. {
  353. Point difference = new Point(currentMouse.X - GetStaticData().lastMoved.X, currentMouse.Y - GetStaticData().lastMoved.Y);
  354. GetStaticData().takeFrom = new Point(GetStaticData().takeFrom.X + difference.X, GetStaticData().takeFrom.Y + difference.Y);
  355. }
  356. else
  357. {
  358. GetStaticData().lastMoved = currentMouse;
  359. }
  360. int penWidth = (int)AppEnvironment.PenInfo().Width;
  361. Rectangle rect;
  362. if (penWidth != 1)
  363. {
  364. rect = new Rectangle(new Point(GetStaticData().takeFrom.X - penWidth / 2, GetStaticData().takeFrom.Y - penWidth / 2), new Size(penWidth + 1, penWidth + 1));
  365. }
  366. else
  367. {
  368. rect = new Rectangle(new Point(GetStaticData().takeFrom.X - penWidth, GetStaticData().takeFrom.Y - penWidth), new Size(1 + (2 * penWidth), 1 + (2 * penWidth)));
  369. }
  370. Rectangle boundRect = new Rectangle(GetStaticData().takeFrom, new Size(1, 1));
  371. // If the takeFrom area escapes the boundary
  372. if (!ActiveLayer.Bounds.Contains(boundRect))
  373. {
  374. GetStaticData().lastMoved = currentMouse;
  375. lastTakeFrom = GetStaticData().takeFrom;
  376. }
  377. if (this.savedRegion != null)
  378. {
  379. ActiveLayer.Invalidate(savedRegion.GetBoundsInt());
  380. this.savedRegion.Dispose();
  381. this.savedRegion = null;
  382. }
  383. rect.Intersect(takeFromLayer.Surface.Bounds);
  384. if (rect.Width == 0 || rect.Height == 0)
  385. {
  386. return;
  387. }
  388. this.savedRegion = new PdnRegion(rect);
  389. SaveRegion(this.savedRegion, rect);
  390. // Draw that clone line
  391. Surface takeFromSurface;
  392. if (object.ReferenceEquals(takeFromLayer, ActiveLayer))
  393. {
  394. takeFromSurface = this.ScratchSurface;
  395. }
  396. else
  397. {
  398. takeFromSurface = takeFromLayer.Surface;
  399. }
  400. if (this.clipRegion == null)
  401. {
  402. this.clipRegion = Selection.CreateRegion();
  403. }
  404. DrawCloneLine(currentMouse, GetStaticData().lastMoved, lastTakeFrom,
  405. takeFromSurface, ((BitmapLayer)ActiveLayer).Surface);
  406. this.rendererSrc.BrushLocation = GetStaticData().takeFrom;
  407. ActiveLayer.Invalidate(rect);
  408. Update();
  409. GetStaticData().lastMoved = currentMouse;
  410. }
  411. }
  412. protected override void OnSelectionChanged()
  413. {
  414. if (this.clipRegion != null)
  415. {
  416. this.clipRegion.Dispose();
  417. this.clipRegion = null;
  418. }
  419. base.OnSelectionChanged();
  420. }
  421. protected override void OnMouseDown(MouseEventArgs e)
  422. {
  423. base.OnMouseDown(e);
  424. if (!(ActiveLayer is BitmapLayer))
  425. {
  426. return;
  427. }
  428. Cursor = cursorMouseDown;
  429. if (IsMouseLeftDown(e))
  430. {
  431. this.rendererDst.Visible = false;
  432. if (IsCtrlDown())
  433. {
  434. GetStaticData().takeFrom = new Point(e.X, e.Y);
  435. this.rendererSrc.BrushLocation = new Point(e.X, e.Y);
  436. this.rendererSrc.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  437. this.rendererSrc.Visible = true;
  438. GetStaticData().updateSrcPreview = false;
  439. GetStaticData().wr = new WeakReference(((BitmapLayer)ActiveLayer));
  440. takeFromLayer = (BitmapLayer)(GetStaticData().wr.Target);
  441. GetStaticData().lastMoved = Point.Empty;
  442. ra = new RenderArgs(((BitmapLayer)ActiveLayer).Surface);
  443. }
  444. else
  445. {
  446. GetStaticData().updateSrcPreview = true;
  447. // Determine if there is something to work if, if there isn't return
  448. if (GetStaticData().takeFrom == Point.Empty)
  449. {
  450. }
  451. else if (!GetStaticData().wr.IsAlive || takeFromLayer == null)
  452. {
  453. GetStaticData().takeFrom = Point.Empty;
  454. GetStaticData().lastMoved = Point.Empty;
  455. }
  456. // Make sure the layer is still there!
  457. else if (takeFromLayer != null && !Document.Layers.Contains(takeFromLayer))
  458. {
  459. GetStaticData().takeFrom = Point.Empty;
  460. GetStaticData().lastMoved = Point.Empty;
  461. }
  462. else
  463. {
  464. this.antialiasing = AppEnvironment.AntiAliasing();
  465. this.ra = new RenderArgs(((BitmapLayer)ActiveLayer).Surface);
  466. this.ra.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  467. OnMouseMove(e);
  468. }
  469. }
  470. }
  471. }
  472. private void Environment_PenInfoChanged(object sender, EventArgs e)
  473. {
  474. this.rendererSrc.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  475. this.rendererDst.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  476. }
  477. }
  478. }