MoveTool.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. // Leave uncommented to always use bilinear rendering. Otherwise nearest neighbor
  2. // is used while interacting with the selection via the mouse, for better performance.
  3. //#define ALWAYSHIGHQUALITY
  4. using PaintDotNet.Measurement.Enum;
  5. using PaintDotNet.Measurement.HistoryMementos;
  6. using System;
  7. using System.ComponentModel;
  8. using System.Drawing;
  9. using System.Drawing.Drawing2D;
  10. using System.Runtime.Serialization;
  11. using System.Windows.Forms;
  12. namespace PaintDotNet.Measurement.Tools
  13. {
  14. public class MoveTool : MoveToolBase
  15. {
  16. public static string StaticName
  17. {
  18. get
  19. {
  20. return PdnResources.GetString("MoveTool.Name");
  21. }
  22. }
  23. // if this equals false, then Render() will always use NearestNeighbor, regardless of AppEnvironment.ResamplingAlgorithm
  24. private bool fullQuality = false;
  25. private BitmapLayer activeLayer;
  26. private RenderArgs renderArgs;
  27. private bool didPaste = false;
  28. private MoveToolContext ourContext
  29. {
  30. get
  31. {
  32. return (MoveToolContext)this.context;
  33. }
  34. }
  35. [Serializable]
  36. private sealed class MoveToolContext
  37. : MoveToolBase.Context
  38. {
  39. [NonSerialized]
  40. private MaskedSurface liftedPixels;
  41. [NonSerialized]
  42. public PersistedObject<MaskedSurface> poLiftedPixels;
  43. public Guid poLiftedPixelsGuid;
  44. public MaskedSurface LiftedPixels
  45. {
  46. get
  47. {
  48. if (this.liftedPixels == null)
  49. {
  50. if (this.poLiftedPixels != null)
  51. {
  52. this.liftedPixels = (MaskedSurface)poLiftedPixels.Object;
  53. }
  54. }
  55. return this.liftedPixels;
  56. }
  57. set
  58. {
  59. if (value == null)
  60. {
  61. this.poLiftedPixels = null;
  62. this.liftedPixels = null;
  63. }
  64. else
  65. {
  66. this.poLiftedPixels = new PersistedObject<MaskedSurface>(value, true);
  67. this.poLiftedPixelsGuid = PersistedObjectLocker.Add(this.poLiftedPixels);
  68. this.liftedPixels = null;
  69. }
  70. }
  71. }
  72. public override void GetObjectData(SerializationInfo info, StreamingContext context)
  73. {
  74. base.GetObjectData(info, context);
  75. info.AddValue("poLiftedPixelsGuid", this.poLiftedPixelsGuid);
  76. }
  77. public MoveToolContext(SerializationInfo info, StreamingContext context)
  78. : base(info, context)
  79. {
  80. this.poLiftedPixelsGuid = (Guid)info.GetValue("poLiftedPixelsGuid", typeof(Guid));
  81. this.poLiftedPixels = PersistedObjectLocker.Get<MaskedSurface>(this.poLiftedPixelsGuid);
  82. }
  83. public MoveToolContext(MoveToolContext cloneMe)
  84. : base(cloneMe)
  85. {
  86. this.poLiftedPixelsGuid = cloneMe.poLiftedPixelsGuid;
  87. this.poLiftedPixels = cloneMe.poLiftedPixels; // do not clone
  88. this.liftedPixels = cloneMe.liftedPixels; // do not clone
  89. }
  90. public MoveToolContext()
  91. {
  92. }
  93. protected override void Dispose(bool disposing)
  94. {
  95. if (disposing)
  96. {
  97. }
  98. base.Dispose(disposing);
  99. }
  100. public override object Clone()
  101. {
  102. return new MoveToolContext(this);
  103. }
  104. }
  105. private class ContextHistoryMemento
  106. : ToolHistoryMemento
  107. {
  108. private int layerIndex;
  109. private object liftedPixelsRef; // prevent this from being GC'd
  110. [Serializable]
  111. private class OurContextHistoryMementoData
  112. : HistoryMementoData
  113. {
  114. public MoveToolContext context;
  115. public OurContextHistoryMementoData(Context context)
  116. {
  117. this.context = (MoveToolContext)context.Clone();
  118. }
  119. }
  120. protected override HistoryMemento OnToolUndo()
  121. {
  122. MoveTool moveTool = DocumentWorkspace.GetTool() as MoveTool;
  123. if (moveTool == null)
  124. {
  125. throw new InvalidOperationException("Current Tool is not the MoveTool");
  126. }
  127. ContextHistoryMemento cha = new ContextHistoryMemento(DocumentWorkspace, moveTool.ourContext, this.Name, this.Image);
  128. OurContextHistoryMementoData ohad = (OurContextHistoryMementoData)this.Data;
  129. Context newContext = ohad.context;
  130. if (moveTool.ActiveLayerIndex != this.layerIndex)
  131. {
  132. bool oldDOLC = moveTool.deactivateOnLayerChange;
  133. moveTool.deactivateOnLayerChange = false;
  134. moveTool.ActiveLayerIndex = this.layerIndex;
  135. moveTool.deactivateOnLayerChange = oldDOLC;
  136. moveTool.activeLayer = (BitmapLayer)moveTool.ActiveLayer;
  137. moveTool.renderArgs = new RenderArgs(moveTool.activeLayer.Surface);
  138. moveTool.ClearSavedMemory();
  139. }
  140. moveTool.context.Dispose();
  141. moveTool.context = newContext;
  142. moveTool.DestroyNubs();
  143. if (moveTool.context.lifted)
  144. {
  145. moveTool.PositionNubs(moveTool.context.currentMode);
  146. }
  147. return cha;
  148. }
  149. public ContextHistoryMemento(IDocumentWorkspace documentWorkspace, MoveToolContext context, string name, ImageResource image)
  150. : base(documentWorkspace, name, image)
  151. {
  152. this.Data = new OurContextHistoryMementoData(context);
  153. this.layerIndex = this.DocumentWorkspace.GetActiveLayerIndex();
  154. this.liftedPixelsRef = context.poLiftedPixels;
  155. }
  156. }
  157. protected override void OnActivate()
  158. {
  159. AppEnvironment.ResamplingAlgorithmChanged += AppEnvironment_ResamplingAlgorithmChanged;
  160. this.moveToolCursor = new Cursor(PdnResources.GetResourceStream("Cursors.MoveToolCursor.cur"));
  161. this.Cursor = this.moveToolCursor;
  162. this.context.lifted = false;
  163. this.ourContext.LiftedPixels = null;
  164. this.context.offset = new Point(0, 0);
  165. this.context.liftedBounds = Selection.GetBoundsF();
  166. this.activeLayer = (BitmapLayer)ActiveLayer;
  167. if (this.renderArgs != null)
  168. {
  169. this.renderArgs.Dispose();
  170. this.renderArgs = null;
  171. }
  172. if (this.activeLayer == null)
  173. {
  174. this.renderArgs = null;
  175. }
  176. else
  177. {
  178. this.renderArgs = new RenderArgs(this.activeLayer.Surface);
  179. }
  180. this.tracking = false;
  181. PositionNubs(this.context.currentMode);
  182. #if ALWAYSHIGHQUALITY
  183. this.fullQuality = true;
  184. #endif
  185. base.OnActivate();
  186. }
  187. protected override void OnDeactivate()
  188. {
  189. AppEnvironment.ResamplingAlgorithmChanged -= AppEnvironment_ResamplingAlgorithmChanged;
  190. if (this.moveToolCursor != null)
  191. {
  192. this.moveToolCursor.Dispose();
  193. this.moveToolCursor = null;
  194. }
  195. if (context.lifted)
  196. {
  197. Drop();
  198. }
  199. this.activeLayer = null;
  200. if (this.renderArgs != null)
  201. {
  202. this.renderArgs.Dispose();
  203. this.renderArgs = null;
  204. }
  205. this.tracking = false;
  206. DestroyNubs();
  207. base.OnDeactivate();
  208. }
  209. private void AppEnvironment_ResamplingAlgorithmChanged(object sender, EventArgs e)
  210. {
  211. if (this.ourContext.LiftedPixels != null)
  212. {
  213. bool oldHQ = this.fullQuality;
  214. this.fullQuality = true;
  215. PreRender();
  216. Render(this.context.offset, true);
  217. Update();
  218. this.fullQuality = oldHQ;
  219. }
  220. }
  221. protected override void Drop()
  222. {
  223. RestoreSavedRegion();
  224. PdnRegion regionCopy = Selection.CreateRegion();
  225. using (PdnRegion simplifiedRegion = Utility.SimplifyAndInflateRegion(regionCopy,
  226. Utility.DefaultSimplificationFactor, 2))
  227. {
  228. HistoryMemento bitmapAction2 = new BitmapHistoryMemento(Name, Image, DocumentWorkspace,
  229. ActiveLayerIndex, simplifiedRegion);
  230. bool oldHQ = this.fullQuality;
  231. this.fullQuality = true;
  232. Render(this.context.offset, true);
  233. this.fullQuality = oldHQ;
  234. this.currentHistoryMementos.Add(bitmapAction2);
  235. activeLayer.Invalidate(simplifiedRegion);
  236. Update();
  237. }
  238. regionCopy.Dispose();
  239. regionCopy = null;
  240. ContextHistoryMemento cha = new ContextHistoryMemento(this.DocumentWorkspace, this.ourContext, this.Name, this.Image);
  241. this.currentHistoryMementos.Add(cha);
  242. string name;
  243. ImageResource image;
  244. if (didPaste)
  245. {
  246. name = EnumLocalizer.EnumValueToLocalizedName(typeof(CommonAction), CommonAction.Paste);
  247. image = PdnResources.GetImageResource("Icons.MenuEditPasteIcon.png");
  248. }
  249. else
  250. {
  251. name = this.Name;
  252. image = this.Image;
  253. }
  254. didPaste = false;
  255. SelectionHistoryMemento sha = new SelectionHistoryMemento(this.Name, this.Image, this.DocumentWorkspace);
  256. this.currentHistoryMementos.Add(sha);
  257. this.context.Dispose();
  258. this.context = new MoveToolContext();
  259. this.FlushHistoryMementos(PdnResources.GetString("MoveTool.HistoryMemento.DropPixels"));
  260. }
  261. protected override void OnSelectionChanging()
  262. {
  263. base.OnSelectionChanging();
  264. if (!dontDrop)
  265. {
  266. if (context.lifted)
  267. {
  268. Drop();
  269. }
  270. if (tracking)
  271. {
  272. tracking = false;
  273. }
  274. }
  275. }
  276. protected override void OnSelectionChanged()
  277. {
  278. if (!context.lifted)
  279. {
  280. DestroyNubs();
  281. PositionNubs(this.context.currentMode);
  282. }
  283. base.OnSelectionChanged();
  284. }
  285. /// <summary>
  286. /// Provided as a special entry point so that Paste can work well.
  287. /// </summary>
  288. /// <param name="surface">What you want to paste.</param>
  289. /// <param name="offset">Where you want to paste it.</param>
  290. public void PasteMouseDown(SurfaceForClipboard sfc, Point offset)
  291. {
  292. if (this.context.lifted)
  293. {
  294. Drop();
  295. }
  296. MaskedSurface pixels = sfc.MaskedSurface;
  297. PdnGraphicsPath pastePath = pixels.CreatePath();
  298. PdnRegion pasteRegion = new PdnRegion(pastePath);
  299. PdnRegion simplifiedPasteRegion = Utility.SimplifyAndInflateRegion(pasteRegion);
  300. HistoryMemento bitmapAction = new BitmapHistoryMemento(Name, Image,
  301. DocumentWorkspace, ActiveLayerIndex, simplifiedPasteRegion); // SLOW (110ms)
  302. this.currentHistoryMementos.Add(bitmapAction);
  303. PushContextHistoryMemento();
  304. this.context.seriesGuid = Guid.NewGuid();
  305. this.context.currentMode = Mode.Translate;
  306. this.context.startEdge = Edge.None;
  307. this.context.startAngle = 0.0f;
  308. this.ourContext.LiftedPixels = pixels;
  309. this.context.lifted = true;
  310. this.context.liftTransform = new Matrix();
  311. this.context.liftTransform.Reset();
  312. this.context.deltaTransform = new Matrix();
  313. this.context.deltaTransform.Reset();
  314. this.context.offset = new Point(0, 0);
  315. bool oldDD = this.dontDrop;
  316. this.dontDrop = true;
  317. SelectionHistoryMemento sha = new SelectionHistoryMemento(null, null, DocumentWorkspace);
  318. this.currentHistoryMementos.Add(sha);
  319. Selection.PerformChanging();
  320. Selection.Reset();
  321. Selection.SetContinuation(pastePath, CombineMode.Replace, true);
  322. pastePath = null;
  323. Selection.CommitContinuation();
  324. Selection.PerformChanged();
  325. PushContextHistoryMemento();
  326. this.context.liftedBounds = Selection.GetBoundsF(false);
  327. this.context.startBounds = this.context.liftedBounds;
  328. this.context.baseTransform = new Matrix();
  329. this.context.baseTransform.Reset();
  330. this.tracking = true;
  331. this.dontDrop = oldDD;
  332. this.didPaste = true;
  333. this.tracking = true;
  334. DestroyNubs();
  335. PositionNubs(this.context.currentMode);
  336. // we use the value 70,000 to simulate mouse input because that's guaranteed to be out of bounds of where
  337. // the mouse can actually be -- PDN is limited to 65536 x 65536 images by design
  338. MouseEventArgs mea1 = new MouseEventArgs(MouseButtons.Left, 0, 70000, 70000, 0);
  339. MouseEventArgs mea2 = new MouseEventArgs(MouseButtons.Left, 0, 70000 + offset.X, 70000 + offset.Y, 0);
  340. this.context.startMouseXY = new Point(70000, 70000);
  341. OnMouseDown(mea1);
  342. OnMouseMove(mea2); // SLOW (200ms)
  343. OnMouseUp(mea2);
  344. }
  345. protected override void OnLift(MouseEventArgs e)
  346. {
  347. PdnGraphicsPath liftPath = Selection.CreatePath();
  348. PdnRegion liftRegion = Selection.CreateRegion();
  349. this.ourContext.LiftedPixels = new MaskedSurface(activeLayer.Surface, liftPath);
  350. HistoryMemento bitmapAction = new BitmapHistoryMemento(
  351. Name,
  352. Image,
  353. DocumentWorkspace,
  354. ActiveLayerIndex,
  355. this.ourContext.poLiftedPixelsGuid);
  356. this.currentHistoryMementos.Add(bitmapAction);
  357. // If the user is holding down the control key, we want to *copy* the pixels
  358. // and not "lift and erase"
  359. if ((ModifierKeys & Keys.Control) == Keys.None)
  360. {
  361. ColorBgra fill = AppEnvironment.SecondaryColor();
  362. fill.A = 0;
  363. UnaryPixelOp op = new UnaryPixelOps.Constant(fill);
  364. op.Apply(this.renderArgs.Surface, liftRegion);
  365. }
  366. liftRegion.Dispose();
  367. liftRegion = null;
  368. liftPath.Dispose();
  369. liftPath = null;
  370. }
  371. protected override void PushContextHistoryMemento()
  372. {
  373. ContextHistoryMemento cha = new ContextHistoryMemento(this.DocumentWorkspace, this.ourContext, null, null);
  374. this.currentHistoryMementos.Add(cha);
  375. }
  376. protected override void Render(Point newOffset, bool useNewOffset)
  377. {
  378. Render(newOffset, useNewOffset, true);
  379. }
  380. protected void Render(Point newOffset, bool useNewOffset, bool saveRegion)
  381. {
  382. Rectangle saveBounds = Selection.GetBounds();
  383. PdnRegion selectedRegion = Selection.CreateRegion();
  384. PdnRegion simplifiedRegion = Utility.SimplifyAndInflateRegion(selectedRegion);
  385. if (saveRegion)
  386. {
  387. SaveRegion(simplifiedRegion, saveBounds);
  388. }
  389. WaitCursorChanger wcc = null;
  390. if (this.fullQuality && AppEnvironment.ResamplingAlgorithm() == ResamplingAlgorithm.Bilinear)
  391. {
  392. wcc = new WaitCursorChanger(DocumentWorkspace.GetThis());
  393. }
  394. this.ourContext.LiftedPixels.Draw(
  395. this.renderArgs.Surface,
  396. this.context.deltaTransform,
  397. this.fullQuality ?
  398. AppEnvironment.ResamplingAlgorithm() :
  399. ResamplingAlgorithm.NearestNeighbor);
  400. if (wcc != null)
  401. {
  402. wcc.Dispose();
  403. wcc = null;
  404. }
  405. activeLayer.Invalidate(simplifiedRegion);
  406. PositionNubs(this.context.currentMode);
  407. simplifiedRegion.Dispose();
  408. selectedRegion.Dispose();
  409. }
  410. protected override void PreRender()
  411. {
  412. RestoreSavedRegion();
  413. }
  414. protected override void OnMouseUp(MouseEventArgs e)
  415. {
  416. base.OnMouseUp(e);
  417. if (!tracking)
  418. {
  419. return;
  420. }
  421. this.fullQuality = true;
  422. OnMouseMove(e);
  423. #if !ALWAYSHIGHQUALITY
  424. this.fullQuality = false;
  425. #endif
  426. this.rotateNub.Visible = false;
  427. tracking = false;
  428. PositionNubs(this.context.currentMode);
  429. string resourceName;
  430. switch (this.context.currentMode)
  431. {
  432. default:
  433. throw new InvalidEnumArgumentException();
  434. case Mode.Rotate:
  435. resourceName = "MoveTool.HistoryMemento.Rotate";
  436. break;
  437. case Mode.Scale:
  438. resourceName = "MoveTool.HistoryMemento.Scale";
  439. break;
  440. case Mode.Translate:
  441. resourceName = "MoveTool.HistoryMemento.Translate";
  442. break;
  443. }
  444. this.context.startAngle += this.angleDelta;
  445. if (this.context.liftTransform == null)
  446. {
  447. this.context.liftTransform = new Matrix();
  448. }
  449. this.context.liftTransform.Reset();
  450. this.context.liftTransform.Multiply(this.context.deltaTransform, MatrixOrder.Append);
  451. string actionName = PdnResources.GetString(resourceName);
  452. FlushHistoryMementos(actionName);
  453. }
  454. private void FlushHistoryMementos(string name)
  455. {
  456. if (this.currentHistoryMementos.Count > 0)
  457. {
  458. CompoundHistoryMemento cha = new CompoundHistoryMemento(null, null,
  459. this.currentHistoryMementos.ToArray());
  460. string haName;
  461. ImageResource image;
  462. if (this.didPaste)
  463. {
  464. haName = PdnResources.GetString("CommonAction.Paste");
  465. image = PdnResources.GetImageResource("Icons.MenuEditPasteIcon.png");
  466. this.didPaste = false;
  467. }
  468. else
  469. {
  470. if (name == null)
  471. {
  472. haName = this.Name;
  473. }
  474. else
  475. {
  476. haName = name;
  477. }
  478. image = this.Image;
  479. }
  480. CompoundToolHistoryMemento ctha = new CompoundToolHistoryMemento(cha, this.DocumentWorkspace, haName, image);
  481. ctha.SeriesGuid = context.seriesGuid;
  482. //HistoryStack.PushNewMemento(ctha);
  483. this.currentHistoryMementos.Clear();
  484. }
  485. }
  486. public MoveTool(IDocumentWorkspace documentWorkspace)
  487. : base(documentWorkspace,
  488. PdnResources.GetImageResource("Icons.MoveToolIcon.png"),
  489. MoveTool.StaticName,
  490. PdnResources.GetString("MoveTool.HelpText"), // "Click and drag to move a selected region",
  491. 'm',
  492. false,
  493. ToolBarConfigItems.Resampling)
  494. {
  495. this.context = new MoveToolContext();
  496. this.enableOutline = false;
  497. }
  498. protected override void Dispose(bool disposing)
  499. {
  500. base.Dispose(disposing);
  501. if (disposing)
  502. {
  503. DestroyNubs();
  504. if (this.renderArgs != null)
  505. {
  506. this.renderArgs.Dispose();
  507. this.renderArgs = null;
  508. }
  509. if (this.context != null)
  510. {
  511. this.context.Dispose();
  512. this.context = null;
  513. }
  514. }
  515. }
  516. protected override void OnExecutingHistoryMemento(ExecutingHistoryMementoEventArgs e)
  517. {
  518. this.dontDrop = true;
  519. RestoreSavedRegion();
  520. ClearSavedMemory();
  521. if (e.MayAlterSuspendTool)
  522. {
  523. e.SuspendTool = false;
  524. }
  525. }
  526. protected override void OnExecutedHistoryMemento(ExecutedHistoryMementoEventArgs e)
  527. {
  528. if (context.lifted)
  529. {
  530. bool oldHQ = this.fullQuality;
  531. this.fullQuality = false;
  532. Render(context.offset, true);
  533. ClearSavedMemory();
  534. this.fullQuality = oldHQ;
  535. }
  536. else
  537. {
  538. DestroyNubs();
  539. PositionNubs(this.context.currentMode);
  540. }
  541. this.dontDrop = false;
  542. }
  543. protected override void OnFinishedHistoryStepGroup()
  544. {
  545. if (context.lifted)
  546. {
  547. bool oldHQ = this.fullQuality;
  548. this.fullQuality = true;
  549. Render(context.offset, true, false);
  550. this.fullQuality = oldHQ;
  551. }
  552. base.OnFinishedHistoryStepGroup();
  553. }
  554. }
  555. }