RecoloringTool.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. using PaintDotNet.Measurement.HistoryMementos;
  2. using System;
  3. using System.Collections;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.Windows.Forms;
  7. namespace PaintDotNet.Measurement.Tools
  8. {
  9. public class RecolorTool : Tool
  10. {
  11. private bool mouseDown;
  12. private Point lastMouseXY;
  13. private MouseButtons mouseButton;
  14. private RenderArgs renderArgs;
  15. private BitmapLayer bitmapLayer;
  16. private ArrayList savedSurfaces;
  17. private BrushPreviewRenderer previewRenderer;
  18. private float penWidth;
  19. private int ceilingPenWidth;
  20. private int halfPenWidth;
  21. private ColorBgra colorToReplace;
  22. private ColorBgra colorReplacing;
  23. private Cursor cursorMouseDown;
  24. private Cursor cursorMouseUp;
  25. private Cursor cursorMouseDownPickColor;
  26. private Cursor cursorMouseDownAdjustColor;
  27. // private ColorBgra replacementDiff;
  28. private static ColorBgra colorToleranceBasis = ColorBgra.FromBgra(0x20, 0x20, 0x20, 0x00);
  29. private PdnRegion clipRegion;
  30. private UserBlendOps.NormalBlendOp blendOp = new UserBlendOps.NormalBlendOp();
  31. private bool hasDrawn;
  32. private Keys modifierDown;
  33. // AA stuff
  34. private BitVector2D isPointAlreadyAA;
  35. private Surface aaPoints;
  36. public ColorBgra AAPoints(int x, int y)
  37. {
  38. return aaPoints[x, y];
  39. }
  40. public void AAPointsAdd(int x, int y, ColorBgra color)
  41. {
  42. aaPoints[x, y] = color;
  43. isPointAlreadyAA[x, y] = true;
  44. }
  45. public void AAPointsRemove(int x, int y)
  46. {
  47. isPointAlreadyAA[x, y] = false;
  48. }
  49. private bool IsPointAlreadyAntiAliased(int x, int y)
  50. {
  51. return isPointAlreadyAA[x, y];
  52. }
  53. private bool IsPointAlreadyAntiAliased(Point pt)
  54. {
  55. return IsPointAlreadyAntiAliased(pt.X, pt.Y);
  56. }
  57. // RenderArgs specifically for a brush mask
  58. private RenderArgs brushRenderArgs;
  59. private int myTolerance;
  60. private bool IsColorInTolerance(ColorBgra colorA, ColorBgra colorB)
  61. {
  62. return Utility.ColorDifference(colorA, colorB) <= myTolerance;
  63. }
  64. private void RestrictTolerance()
  65. {
  66. int difference = Utility.ColorDifference(colorReplacing, colorToReplace);
  67. if (myTolerance > difference)
  68. {
  69. myTolerance = difference;
  70. }
  71. }
  72. protected override void OnMouseEnter()
  73. {
  74. this.previewRenderer.Visible = true;
  75. base.OnMouseEnter();
  76. }
  77. protected override void OnMouseLeave()
  78. {
  79. this.previewRenderer.Visible = false;
  80. base.OnMouseLeave();
  81. }
  82. protected override void OnActivate()
  83. {
  84. base.OnActivate();
  85. // initialize any state information you need
  86. cursorMouseUp = new Cursor(PdnResources.GetResourceStream("Cursors.RecoloringToolCursor.cur"));
  87. cursorMouseDown = new Cursor(PdnResources.GetResourceStream("Cursors.GenericToolCursorMouseDown.cur"));
  88. cursorMouseDownPickColor = new Cursor(PdnResources.GetResourceStream("Cursors.RecoloringToolCursorPickColor.cur"));
  89. cursorMouseDownAdjustColor = new Cursor(PdnResources.GetResourceStream("Cursors.RecoloringToolCursorAdjustColor.cur"));
  90. this.previewRenderer = new BrushPreviewRenderer(this.RendererList);
  91. this.RendererList.Add(this.previewRenderer, false);
  92. Cursor = cursorMouseUp;
  93. mouseDown = false;
  94. // fetch colors from workspace palette
  95. this.colorToReplace = this.AppEnvironment.PrimaryColor();
  96. this.colorReplacing = this.AppEnvironment.SecondaryColor();
  97. this.aaPoints = this.ScratchSurface;
  98. this.isPointAlreadyAA = new BitVector2D(aaPoints.Width, aaPoints.Height);
  99. if (savedSurfaces != null)
  100. {
  101. foreach (PlacedSurface ps in savedSurfaces)
  102. {
  103. ps.Dispose();
  104. }
  105. }
  106. savedSurfaces = new ArrayList();
  107. if (ActiveLayer != null)
  108. {
  109. bitmapLayer = (BitmapLayer)ActiveLayer;
  110. renderArgs = new RenderArgs(bitmapLayer.Surface);
  111. }
  112. else
  113. {
  114. bitmapLayer = null;
  115. renderArgs = null;
  116. }
  117. }
  118. protected override void OnDeactivate()
  119. {
  120. base.OnDeactivate();
  121. if (mouseDown)
  122. {
  123. OnMouseUp(new MouseEventArgs(mouseButton, 0, lastMouseXY.X, lastMouseXY.Y, 0));
  124. }
  125. this.RendererList.Remove(this.previewRenderer);
  126. this.previewRenderer.Dispose();
  127. this.previewRenderer = null;
  128. if (savedSurfaces != null)
  129. {
  130. if (savedSurfaces != null)
  131. {
  132. foreach (PlacedSurface ps in savedSurfaces)
  133. {
  134. ps.Dispose();
  135. }
  136. }
  137. savedSurfaces.Clear();
  138. savedSurfaces = null;
  139. }
  140. renderArgs.Dispose(); ;
  141. renderArgs = null;
  142. aaPoints = null;
  143. renderArgs = null;
  144. bitmapLayer = null;
  145. if (clipRegion != null)
  146. {
  147. clipRegion.Dispose();
  148. clipRegion = null;
  149. }
  150. if (cursorMouseUp != null)
  151. {
  152. cursorMouseUp.Dispose();
  153. cursorMouseUp = null;
  154. }
  155. if (cursorMouseDown != null)
  156. {
  157. cursorMouseDown.Dispose();
  158. cursorMouseDown = null;
  159. }
  160. if (cursorMouseDownPickColor != null)
  161. {
  162. cursorMouseDownPickColor.Dispose();
  163. cursorMouseDownPickColor = null;
  164. }
  165. if (cursorMouseDownAdjustColor != null)
  166. {
  167. cursorMouseDownAdjustColor.Dispose();
  168. cursorMouseDownAdjustColor = null;
  169. }
  170. }
  171. private ColorBgra LiftColor(int x, int y)
  172. {
  173. return ((BitmapLayer)ActiveLayer).Surface[x, y];
  174. }
  175. /// <summary>
  176. /// Picks up the color under the mouse and assigns to the forecolor (or backcolor).
  177. /// If assigning to the forecolor, the backcolor will be adjusted respective to the
  178. /// difference of the old forecolor versus the new forecolor.
  179. /// </summary>
  180. /// <param name="e"></param>
  181. private void AdjustDrawingColor(MouseEventArgs e)
  182. {
  183. ColorBgra oldColor;
  184. if (BtnDownMouseLeft(e))
  185. {
  186. oldColor = this.AppEnvironment.PrimaryColor();
  187. PickColor(e);
  188. this.AppEnvironment.SetSecondaryColor(AdjustColorDifference(oldColor,
  189. this.AppEnvironment.PrimaryColor(), this.AppEnvironment.SecondaryColor()));
  190. }
  191. if (BtnDownMouseRight(e))
  192. {
  193. oldColor = this.AppEnvironment.SecondaryColor();
  194. PickColor(e);
  195. this.AppEnvironment.SetPrimaryColor(AdjustColorDifference(oldColor,
  196. this.AppEnvironment.SecondaryColor(), this.AppEnvironment.PrimaryColor()));
  197. }
  198. }
  199. private byte AdjustColorByte(byte oldByte, byte newByte, byte basisByte)
  200. {
  201. if (oldByte > newByte)
  202. {
  203. return Utility.ClampToByte(basisByte - (oldByte - newByte));
  204. }
  205. else
  206. {
  207. return Utility.ClampToByte(basisByte + (newByte - oldByte));
  208. }
  209. }
  210. /// <summary>
  211. /// Returns a ColorBgra shift by the difference between oldcolor and newcolor but using
  212. /// basisColor as the basis.
  213. /// </summary>
  214. /// <param name="oldcolor"></param>
  215. /// <param name="newcolor"></param>
  216. /// <param name="shiftColor"></param>
  217. /// <returns></returns>
  218. private ColorBgra AdjustColorDifference(ColorBgra oldColor, ColorBgra newColor, ColorBgra basisColor)
  219. {
  220. ColorBgra returnColor;
  221. // eliminate testing for the "equal to" case
  222. returnColor = basisColor;
  223. returnColor.B = AdjustColorByte(oldColor.B, newColor.B, basisColor.B);
  224. returnColor.G = AdjustColorByte(oldColor.G, newColor.G, basisColor.G);
  225. returnColor.R = AdjustColorByte(oldColor.R, newColor.R, basisColor.R);
  226. return returnColor;
  227. }
  228. private void PickColor(MouseEventArgs e)
  229. {
  230. if (!DocumentWorkspace.GetDocument().Bounds.Contains(e.X, e.Y))
  231. {
  232. return;
  233. }
  234. // if we managed to get here without any mouse buttons down
  235. // we return promptly.
  236. if (BtnDownMouseLeft(e) || BtnDownMouseRight(e))
  237. {
  238. // since the above statement exits if one or the other
  239. if (BtnDownMouseLeft(e))
  240. {
  241. colorReplacing = LiftColor(e.X, e.Y);
  242. colorReplacing.A = (byte)this.AppEnvironment.GetPrimaryColorA();
  243. this.AppEnvironment.SetPrimaryColor(colorReplacing);
  244. }
  245. else
  246. {
  247. colorToReplace = LiftColor(e.X, e.Y);
  248. colorToReplace.A = (byte)this.AppEnvironment.GetSecondaryColorA();
  249. this.AppEnvironment.SetSecondaryColor(colorToReplace);
  250. }
  251. }
  252. else
  253. {
  254. return;
  255. }
  256. // before assigned the newly lifted color, we preserve the
  257. // alpha from the user's selected color.
  258. }
  259. private RenderArgs RenderCircleBrush()
  260. {
  261. // create pen mask surface
  262. Surface brushSurface = new Surface(ceilingPenWidth, ceilingPenWidth);
  263. brushSurface.Clear((ColorBgra)0);
  264. RenderArgs brush = new RenderArgs(brushSurface);
  265. if (AppEnvironment.AntiAliasing())
  266. {
  267. brush.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  268. }
  269. else
  270. {
  271. brush.Graphics.SmoothingMode = SmoothingMode.None;
  272. }
  273. if (AppEnvironment.AntiAliasing())
  274. {
  275. if (penWidth > 2)
  276. {
  277. penWidth = penWidth - 1.0f;
  278. }
  279. else
  280. {
  281. penWidth = penWidth / 2;
  282. }
  283. }
  284. else
  285. {
  286. if (penWidth <= 1.0f)
  287. {
  288. brush.Surface[1, 1] = ColorBgra.Black;
  289. }
  290. else
  291. {
  292. penWidth = (float)Math.Round(penWidth + 1.0f);
  293. }
  294. }
  295. using (Brush testBrush = new SolidBrush(System.Drawing.Color.Black))
  296. {
  297. brush.Graphics.FillEllipse(testBrush, 0.0f, 0.0f, penWidth, penWidth);
  298. }
  299. return brush;
  300. }
  301. private unsafe void DrawOverPoints(Point start, Point finish, ColorBgra colorToReplaceWith, ColorBgra colorBeingReplaced)
  302. {
  303. ColorBgra colorAdjusted = ColorBgra.FromColor(Color.Empty);
  304. byte dstAlpha;
  305. ColorBgra colorLifted;
  306. Rectangle[] rectSelRegions;
  307. Rectangle rectBrushArea;
  308. Rectangle rectBrushRelativeOffset = new Rectangle(0, 0, 0, 0);
  309. // special condition for a canvas with no active selection
  310. // create an array of rectangles with a single rectangle
  311. // specifying the size of the canvas
  312. if (Selection.IsEmpty)
  313. {
  314. rectSelRegions = new Rectangle[] { DocumentWorkspace.GetDocument().Bounds };
  315. }
  316. else
  317. {
  318. rectSelRegions = clipRegion.GetRegionScansReadOnlyInt();
  319. }
  320. // code ripped off from clone stamp tool
  321. Point direction = new Point(finish.X - start.X, finish.Y - start.Y);
  322. float length = Utility.Magnitude(direction);
  323. float bw = AppEnvironment.PenInfo().Width / 2;
  324. float fInc;
  325. if (length == 0.0f)
  326. {
  327. fInc = float.PositiveInfinity;
  328. }
  329. else
  330. {
  331. fInc = (float)Math.Sqrt(bw) / length;
  332. }
  333. // iterate through all points in the linear stroke
  334. for (float f = 0; f < 1; f += fInc)
  335. {
  336. PointF q = new PointF(finish.X * (1 - f) + f * start.X,
  337. finish.Y * (1 - f) + f * start.Y);
  338. Point p = Point.Round(q);
  339. // iterate through all rectangles
  340. foreach (Rectangle rectSel in rectSelRegions)
  341. {
  342. // set the perimeter values for the rectBrushRegion rectangle
  343. // so the area can be intersected with the active
  344. // selection individual recSelRegion rectangle.
  345. rectBrushArea = new Rectangle(p.X - halfPenWidth, p.Y - halfPenWidth, ceilingPenWidth, ceilingPenWidth);
  346. // test the intersection...
  347. // the perimeter values of rectBrushRegion (above)
  348. // may calculate negative but
  349. // *should* always be clipped to acceptable values by
  350. // by the following intersection.
  351. if (rectBrushArea.IntersectsWith(rectSel))
  352. {
  353. // a valid intersection was found.
  354. // prune the brush rectangle to fit the intersection.
  355. rectBrushArea.Intersect(rectSel);
  356. for (int y = rectBrushArea.Top; y < rectBrushArea.Bottom; y++)
  357. {
  358. // create a new rectangle for an offset relative to the
  359. // the brush mask
  360. rectBrushRelativeOffset.X = Math.Max(rectSel.X - (p.X - halfPenWidth), 0);
  361. rectBrushRelativeOffset.Y = Math.Max(rectSel.Y - (p.Y - halfPenWidth), 0);
  362. rectBrushRelativeOffset.Size = rectBrushArea.Size;
  363. ColorBgra* srcBgra;
  364. ColorBgra* dstBgra;
  365. try
  366. {
  367. // get the source address of the first pixel from the brush mask.
  368. srcBgra = (ColorBgra*)brushRenderArgs.Surface.GetPointAddress(rectBrushRelativeOffset.Left,
  369. rectBrushRelativeOffset.Y + (y - rectBrushArea.Y));
  370. // get the address of the pixel we want to change on the canvas.
  371. dstBgra = (ColorBgra*)renderArgs.Surface.GetPointAddress(rectBrushArea.Left, y);
  372. }
  373. catch
  374. {
  375. return;
  376. }
  377. for (int x = rectBrushArea.Left; x < rectBrushArea.Right; x++)
  378. {
  379. if (srcBgra->A != 0)
  380. {
  381. colorLifted = *dstBgra;
  382. // hasDrawn is set if a pixel endures color replacement so that
  383. // the placed surface will be left alone, otherwise, the placed
  384. // surface will be discarded
  385. // adjust the channel color up and down based on the difference calculated
  386. // from the source. These values are clamped to a byte. It's possible
  387. // that the new color is too dark or too bright to take the whole range
  388. bool boolCIT = this.IsColorInTolerance(colorLifted, colorBeingReplaced);
  389. bool boolPAAA = false;
  390. if (AppEnvironment.AntiAliasing())
  391. {
  392. boolPAAA = this.IsPointAlreadyAntiAliased(x, y);
  393. }
  394. if (boolCIT || boolPAAA)
  395. {
  396. if (boolPAAA)
  397. {
  398. colorAdjusted = (ColorBgra)AAPoints(x, y);
  399. if (penWidth < 2.0f)
  400. {
  401. colorAdjusted.B = Utility.ClampToByte(colorToReplaceWith.B + (colorAdjusted.B - colorBeingReplaced.B));
  402. colorAdjusted.G = Utility.ClampToByte(colorToReplaceWith.G + (colorAdjusted.G - colorBeingReplaced.G));
  403. colorAdjusted.R = Utility.ClampToByte(colorToReplaceWith.R + (colorAdjusted.R - colorBeingReplaced.R));
  404. colorAdjusted.A = Utility.ClampToByte(colorToReplaceWith.A + (colorAdjusted.A - colorBeingReplaced.A));
  405. }
  406. }
  407. else
  408. {
  409. colorAdjusted.B = Utility.ClampToByte(colorLifted.B + (colorToReplaceWith.B - colorBeingReplaced.B));
  410. colorAdjusted.G = Utility.ClampToByte(colorLifted.G + (colorToReplaceWith.G - colorBeingReplaced.G));
  411. colorAdjusted.R = Utility.ClampToByte(colorLifted.R + (colorToReplaceWith.R - colorBeingReplaced.R));
  412. colorAdjusted.A = Utility.ClampToByte(colorLifted.A + (colorToReplaceWith.A - colorBeingReplaced.A));
  413. }
  414. if ((srcBgra->A != 255) && AppEnvironment.AntiAliasing())
  415. {
  416. colorAdjusted.A = srcBgra->A;
  417. dstAlpha = dstBgra->A;
  418. *dstBgra = blendOp.Apply(*dstBgra, colorAdjusted);
  419. dstBgra->A = dstAlpha;
  420. if (!this.IsPointAlreadyAntiAliased(x, y))
  421. {
  422. AAPointsAdd(x, y, colorAdjusted);
  423. }
  424. }
  425. else
  426. {
  427. colorAdjusted.A = (*dstBgra).A;
  428. *dstBgra = colorAdjusted;
  429. if (boolPAAA)
  430. {
  431. AAPointsRemove(x, y);
  432. }
  433. }
  434. hasDrawn = true;
  435. }
  436. }
  437. ++srcBgra;
  438. ++dstBgra;
  439. }
  440. }
  441. }
  442. }
  443. }
  444. }
  445. private bool KeyDownShiftOnly()
  446. {
  447. return ModifierKeys == Keys.Shift;
  448. }
  449. private bool KeyDownControlOnly()
  450. {
  451. return ModifierKeys == Keys.Control;
  452. }
  453. protected override void OnKeyDown(KeyEventArgs e)
  454. {
  455. if ((modifierDown == Keys.Control) ||
  456. (modifierDown == Keys.Shift))
  457. {
  458. return;
  459. }
  460. else
  461. {
  462. if (!mouseDown)
  463. {
  464. if (KeyDownControlOnly())
  465. {
  466. Cursor = cursorMouseDownPickColor;
  467. }
  468. else if (KeyDownShiftOnly())
  469. {
  470. Cursor = cursorMouseDownAdjustColor;
  471. }
  472. else
  473. {
  474. base.OnKeyDown(e);
  475. }
  476. }
  477. }
  478. }
  479. protected override void OnKeyUp(KeyEventArgs e)
  480. {
  481. if (!KeyDownControlOnly() && !KeyDownShiftOnly())
  482. {
  483. if (!mouseDown)
  484. {
  485. modifierDown = 0;
  486. Cursor = cursorMouseUp;
  487. }
  488. }
  489. base.OnKeyUp(e);
  490. }
  491. /// <summary>
  492. /// Button down mouse left. Returns true if only the left mouse button is depressed.
  493. /// </summary>
  494. /// <param name="e"></param>
  495. /// <returns></returns>
  496. private bool BtnDownMouseLeft(MouseEventArgs e)
  497. {
  498. return (e.Button == MouseButtons.Left);
  499. }
  500. /// <summary>
  501. /// Button down mouse right. Returns true if only the right mouse is depressed.
  502. /// </summary>
  503. /// <param name="e"></param>
  504. /// <returns></returns>
  505. private bool BtnDownMouseRight(MouseEventArgs e)
  506. {
  507. return (e.Button == MouseButtons.Right);
  508. }
  509. protected override void OnMouseDown(MouseEventArgs e)
  510. {
  511. base.OnMouseDown(e);
  512. if (mouseDown)
  513. {
  514. return;
  515. }
  516. if (BtnDownMouseLeft(e) || BtnDownMouseRight(e))
  517. {
  518. this.previewRenderer.Visible = false;
  519. mouseDown = true;
  520. Cursor = cursorMouseDown;
  521. if ((!KeyDownControlOnly()) && (!KeyDownShiftOnly()))
  522. {
  523. mouseButton = e.Button;
  524. lastMouseXY.X = e.X;
  525. lastMouseXY.Y = e.Y;
  526. // parses and establishes the active selection area
  527. if (clipRegion != null)
  528. {
  529. clipRegion.Dispose();
  530. clipRegion = null;
  531. }
  532. clipRegion = Selection.CreateRegion();
  533. renderArgs.Graphics.SetClip(clipRegion.GetRegionReadOnly(), CombineMode.Replace);
  534. // find the replacement color and the color to replace
  535. colorReplacing = AppEnvironment.PrimaryColor();
  536. colorToReplace = AppEnvironment.SecondaryColor();
  537. penWidth = AppEnvironment.PenInfo().Width;
  538. // get the pen width find the ceiling integer of half of the pen width
  539. ceilingPenWidth = (int)Math.Max(Math.Ceiling(penWidth), 3);
  540. // used only for cursor positioning
  541. halfPenWidth = (int)Math.Ceiling(penWidth / 2.0f);
  542. // set hasDrawn to false since nothing has been drawn
  543. hasDrawn = false;
  544. // render the circle via GDI+ so the AA techniques can precisely
  545. // mimic GDI+.
  546. this.brushRenderArgs = RenderCircleBrush();
  547. // establish tolerance
  548. myTolerance = (int)(AppEnvironment.Tolerance() * 256);
  549. // restrict tolerance so no overlap is permitted
  550. RestrictTolerance();
  551. OnMouseMove(e);
  552. }
  553. else
  554. {
  555. modifierDown = ModifierKeys;
  556. OnMouseMove(e);
  557. }
  558. }
  559. }
  560. protected override void OnMouseUp(MouseEventArgs e)
  561. {
  562. base.OnMouseUp(e);
  563. if (!KeyDownShiftOnly() && !KeyDownControlOnly())
  564. {
  565. Cursor = cursorMouseUp;
  566. }
  567. if (mouseDown)
  568. {
  569. this.previewRenderer.Visible = true;
  570. OnMouseMove(e);
  571. if (savedSurfaces.Count > 0)
  572. {
  573. PdnRegion saveMeRegion = new PdnRegion();
  574. saveMeRegion.MakeEmpty();
  575. foreach (PlacedSurface pi1 in savedSurfaces)
  576. {
  577. saveMeRegion.Union(pi1.Bounds);
  578. }
  579. PdnRegion simplifiedRegion = Utility.SimplifyAndInflateRegion(saveMeRegion);
  580. using (IrregularSurface weDrewThis = new IrregularSurface(renderArgs.Surface, simplifiedRegion))
  581. {
  582. for (int i = savedSurfaces.Count - 1; i >= 0; --i)
  583. {
  584. PlacedSurface ps = (PlacedSurface)savedSurfaces[i];
  585. ps.Draw(renderArgs.Surface);
  586. ps.Dispose();
  587. }
  588. savedSurfaces.Clear();
  589. if (hasDrawn)
  590. {
  591. HistoryMemento ha = new BitmapHistoryMemento(Name, Image, DocumentWorkspace,
  592. ActiveLayerIndex, simplifiedRegion);
  593. weDrewThis.Draw(bitmapLayer.Surface);
  594. //HistoryStack.PushNewMemento(ha);
  595. }
  596. }
  597. }
  598. mouseDown = false;
  599. modifierDown = 0;
  600. }
  601. if (brushRenderArgs != null)
  602. {
  603. if (brushRenderArgs.Surface != null)
  604. {
  605. brushRenderArgs.Surface.Dispose();
  606. }
  607. }
  608. }
  609. protected override void OnMouseMove(MouseEventArgs e)
  610. {
  611. base.OnMouseMove(e);
  612. this.previewRenderer.BrushLocation = new Point(e.X, e.Y);
  613. this.previewRenderer.BrushSize = AppEnvironment.PenInfo().Width / 2.0f;
  614. if (mouseDown)
  615. {
  616. if (BtnDownMouseLeft(e) || BtnDownMouseRight(e))
  617. {
  618. if (modifierDown == 0)
  619. {
  620. // if the primary and secondary colors are identical,
  621. // return...there's no point in committing any action
  622. if (colorReplacing == colorToReplace)
  623. {
  624. return;
  625. }
  626. // get our start and end coordinates, since we need
  627. // to trace along an action line -- the user will expect this behavior
  628. // if we don't, it'll look like a tin can riddled with bullet holes
  629. Point pointStartCorner = lastMouseXY; // start point
  630. Point pointEndCorner = new Point(e.X, e.Y); // end point
  631. // create the rectangle with the 'a' and 'b' points above
  632. Rectangle inspectionRect =
  633. Utility.PointsToRectangle(pointStartCorner, pointEndCorner);
  634. // inflate the region to address account for the pen width
  635. // then intersect with the Workspace to "clip" the boundary
  636. // the total area of the clipped rectangle includes the
  637. // width of the pen surrounding the points limited by either
  638. // the canvas perimeter or the selection outline
  639. inspectionRect.Inflate(1 + ceilingPenWidth / 2, 1 + ceilingPenWidth / 2);
  640. inspectionRect.Intersect(ActiveLayer.Bounds);
  641. // Enforce the selection area restrictions.
  642. // If within the selection area restrictions, build an image history
  643. bool gotWidth = inspectionRect.Width > 0;
  644. bool gotHeight = inspectionRect.Height > 0;
  645. bool isInClip = renderArgs.Graphics.IsVisible(inspectionRect);
  646. if ((gotWidth) && (gotHeight) && (isInClip))
  647. {
  648. PlacedSurface savedPS = new PlacedSurface(renderArgs.Surface, inspectionRect);
  649. savedSurfaces.Add(savedPS);
  650. renderArgs.Graphics.CompositingMode = CompositingMode.SourceOver;
  651. // check the mouse buttons and if we've made it this far, at least
  652. // one of the mouse buttons (left|right) was depressed
  653. if (BtnDownMouseLeft(e))
  654. {
  655. this.DrawOverPoints(pointStartCorner, pointEndCorner, colorReplacing, colorToReplace);
  656. }
  657. else
  658. {
  659. this.DrawOverPoints(pointStartCorner, pointEndCorner, colorToReplace, colorReplacing);
  660. }
  661. bitmapLayer.Invalidate(inspectionRect);
  662. Update();
  663. }
  664. // update the lastMouseXY so we know how to "connect the dots"
  665. lastMouseXY = pointEndCorner;
  666. }
  667. else
  668. {
  669. switch (modifierDown & (Keys.Control | Keys.Shift))
  670. {
  671. case Keys.Control:
  672. PickColor(e);
  673. break;
  674. case Keys.Shift:
  675. AdjustDrawingColor(e);
  676. break;
  677. default:
  678. break;
  679. }
  680. }
  681. }
  682. }
  683. }
  684. public RecolorTool(IDocumentWorkspace documentWorkspace)
  685. : base(documentWorkspace,
  686. PdnResources.GetImageResource("Icons.RecoloringToolIcon.png"),
  687. PdnResources.GetString("RecolorTool.Name"),
  688. PdnResources.GetString("RecolorTool.HelpText"),
  689. 'r',
  690. false,
  691. ToolBarConfigItems.Pen | ToolBarConfigItems.Antialiasing | ToolBarConfigItems.Tolerance)
  692. {
  693. }
  694. }
  695. }