PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Menus/EffectMenuBase.cs

https://bitbucket.org/tcz001/openpdn
C# | 1130 lines | 890 code | 203 blank | 37 comment | 98 complexity | 9ab625413570c564abe4fc795ed4f164 MD5 | raw file
Possible License(s): Unlicense
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using PaintDotNet.Actions;
  10. using PaintDotNet.Effects;
  11. using PaintDotNet.HistoryMementos;
  12. using PaintDotNet.SystemLayer;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Drawing;
  16. using System.IO;
  17. using System.Reflection;
  18. using System.Runtime.InteropServices;
  19. using System.Text;
  20. using System.Threading;
  21. using System.Windows.Forms;
  22. namespace PaintDotNet.Menus
  23. {
  24. internal abstract class EffectMenuBase
  25. : PdnMenuItem
  26. {
  27. private const int tilesPerCpu = 75;
  28. private int renderingThreadCount = Math.Max(2, SystemLayer.Processor.LogicalCpuCount);
  29. private const int effectRefreshInterval = 15;
  30. private PdnRegion[] progressRegions;
  31. private int progressRegionsStartIndex;
  32. private PdnMenuItem sentinel;
  33. private bool menuPopulated = false;
  34. private EffectsCollection effects;
  35. private Effect lastEffect = null;
  36. private EffectConfigToken lastEffectToken = null;
  37. private Dictionary<Type, EffectConfigToken> effectTokens = new Dictionary<Type, EffectConfigToken>();
  38. private System.Windows.Forms.Timer invalidateTimer;
  39. private System.ComponentModel.Container components = null;
  40. protected abstract bool EnableEffectShortcuts
  41. {
  42. get;
  43. }
  44. protected abstract bool EnableRepeatEffectMenuItem
  45. {
  46. get;
  47. }
  48. protected abstract bool FilterEffects(Effect effect);
  49. private bool IsBuiltInEffect(Effect effect)
  50. {
  51. if (effect == null)
  52. {
  53. return true;
  54. }
  55. Type effectType = effect.GetType();
  56. Type effectBaseType = typeof(Effect);
  57. // Built-in effects only live in PaintDotNet.Effects.dll
  58. if (effectType.Assembly == effectBaseType.Assembly)
  59. {
  60. return true;
  61. }
  62. else
  63. {
  64. return false;
  65. }
  66. }
  67. private void HandleEffectException(AppWorkspace appWorkspace, Effect effect, Exception ex)
  68. {
  69. try
  70. {
  71. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  72. AppWorkspace.Widgets.StatusBarProgress.EraseProgressStatusBar();
  73. }
  74. catch (Exception)
  75. {
  76. }
  77. // Figure out if it's a built-in effect, or a plug-in
  78. bool builtIn = IsBuiltInEffect(effect);
  79. if (builtIn)
  80. {
  81. // For built-in effects, tear down Paint.NET which will result in a crash log
  82. throw new ApplicationException("Effect threw an exception", ex);
  83. }
  84. else
  85. {
  86. Icon formIcon = Utility.ImageToIcon(PdnResources.GetImageResource("Icons.BugWarning.png").Reference);
  87. string formTitle = PdnResources.GetString("Effect.PluginErrorDialog.Title");
  88. Image taskImage = null;
  89. string introText = PdnResources.GetString("Effect.PluginErrorDialog.IntroText");
  90. TaskButton restartTB = new TaskButton(
  91. PdnResources.GetImageResource("Icons.RightArrowBlue.png").Reference,
  92. PdnResources.GetString("Effect.PluginErrorDialog.RestartTB.ActionText"),
  93. PdnResources.GetString("Effect.PluginErrorDialog.RestartTB.ExplanationText"));
  94. TaskButton doNotRestartTB = new TaskButton(
  95. PdnResources.GetImageResource("Icons.WarningIcon.png").Reference,
  96. PdnResources.GetString("Effect.PluginErrorDialog.DoNotRestartTB.ActionText"),
  97. PdnResources.GetString("Effect.PluginErrorDialog.DoNotRestartTB.ExplanationText"));
  98. string auxButtonText = PdnResources.GetString("Effect.PluginErrorDialog.AuxButton1.Text");
  99. EventHandler auxButtonClickHandler =
  100. delegate(object sender, EventArgs e)
  101. {
  102. using (PdnBaseForm textBoxForm = new PdnBaseForm())
  103. {
  104. textBoxForm.Name = "EffectCrash";
  105. TextBox exceptionBox = new TextBox();
  106. textBoxForm.Icon = Utility.ImageToIcon(PdnResources.GetImageResource("Icons.WarningIcon.png").Reference);
  107. textBoxForm.Text = PdnResources.GetString("Effect.PluginErrorDialog.Title");
  108. exceptionBox.Dock = DockStyle.Fill;
  109. exceptionBox.ReadOnly = true;
  110. exceptionBox.Multiline = true;
  111. string exceptionText = AppWorkspace.GetLocalizedEffectErrorMessage(effect.GetType().Assembly, effect.GetType(), ex);
  112. exceptionBox.Font = new Font(FontFamily.GenericMonospace, exceptionBox.Font.Size);
  113. exceptionBox.Text = exceptionText;
  114. exceptionBox.ScrollBars = ScrollBars.Vertical;
  115. textBoxForm.StartPosition = FormStartPosition.CenterParent;
  116. textBoxForm.ShowInTaskbar = false;
  117. textBoxForm.MinimizeBox = false;
  118. textBoxForm.Controls.Add(exceptionBox);
  119. textBoxForm.Width = UI.ScaleWidth(700);
  120. textBoxForm.ShowDialog();
  121. }
  122. };
  123. TaskButton clickedTB = TaskDialog.Show(
  124. appWorkspace,
  125. formIcon,
  126. formTitle,
  127. taskImage,
  128. true,
  129. introText,
  130. new TaskButton[] { restartTB, doNotRestartTB },
  131. restartTB,
  132. doNotRestartTB,
  133. TaskDialog.DefaultPixelWidth96Dpi * 2,
  134. auxButtonText,
  135. auxButtonClickHandler);
  136. if (clickedTB == restartTB)
  137. {
  138. // Next, apply restart logic
  139. CloseAllWorkspacesAction cawa = new CloseAllWorkspacesAction();
  140. cawa.PerformAction(appWorkspace);
  141. if (!cawa.Cancelled)
  142. {
  143. SystemLayer.Shell.RestartApplication();
  144. Startup.CloseApplication();
  145. }
  146. }
  147. }
  148. }
  149. public EffectMenuBase()
  150. {
  151. InitializeComponent();
  152. }
  153. private void InitializeComponent()
  154. {
  155. this.sentinel = new PdnMenuItem();
  156. //
  157. // sentinel
  158. //
  159. this.sentinel.Name = null;
  160. //
  161. // components
  162. //
  163. this.components = new System.ComponentModel.Container();
  164. //
  165. // invalidateTimer
  166. //
  167. this.invalidateTimer = new System.Windows.Forms.Timer(this.components);
  168. this.invalidateTimer.Enabled = false;
  169. this.invalidateTimer.Tick += InvalidateTimer_Tick;
  170. this.invalidateTimer.Interval = effectRefreshInterval;
  171. //
  172. // EffectMenuBase
  173. //
  174. this.DropDownItems.Add(sentinel);
  175. }
  176. protected override void Dispose(bool disposing)
  177. {
  178. if (disposing)
  179. {
  180. if (this.components != null)
  181. {
  182. this.components.Dispose();
  183. this.components = null;
  184. }
  185. }
  186. base.Dispose(disposing);
  187. }
  188. protected override void OnDropDownOpening(EventArgs e)
  189. {
  190. if (!this.menuPopulated)
  191. {
  192. PopulateMenu();
  193. }
  194. bool enabled = (AppWorkspace.ActiveDocumentWorkspace != null);
  195. foreach (ToolStripItem item in this.DropDownItems)
  196. {
  197. item.Enabled = enabled;
  198. }
  199. base.OnDropDownOpening(e);
  200. }
  201. public EffectsCollection Effects
  202. {
  203. get
  204. {
  205. if (this.effects == null)
  206. {
  207. this.effects = GatherEffects();
  208. }
  209. return this.effects;
  210. }
  211. }
  212. public void PopulateEffects()
  213. {
  214. PopulateMenu(false);
  215. }
  216. private void PopulateMenu(bool forceRepopulate)
  217. {
  218. if (forceRepopulate)
  219. {
  220. this.menuPopulated = false;
  221. }
  222. PopulateMenu();
  223. }
  224. private void PopulateMenu()
  225. {
  226. this.DropDownItems.Clear();
  227. if (EnableRepeatEffectMenuItem && this.lastEffect != null)
  228. {
  229. string repeatFormat = PdnResources.GetString("Effects.RepeatMenuItem.Format");
  230. string menuName = string.Format(repeatFormat, lastEffect.Name);
  231. PdnMenuItem pmi = new PdnMenuItem(menuName, this.lastEffect.Image, RepeatEffectMenuItem_Click);
  232. pmi.Name = "RepeatEffect(" + this.lastEffect.GetType().FullName + ")";
  233. pmi.ShortcutKeys = Keys.Control | Keys.F;
  234. this.DropDownItems.Add(pmi);
  235. ToolStripSeparator tss = new ToolStripSeparator();
  236. this.DropDownItems.Add(tss);
  237. }
  238. AddEffectsToMenu();
  239. Triple<Assembly, Type, Exception>[] errors = this.Effects.GetLoaderExceptions();
  240. for (int i = 0; i < errors.Length; ++i)
  241. {
  242. AppWorkspace.ReportEffectLoadError(errors[i]);
  243. }
  244. }
  245. protected virtual Keys GetEffectShortcutKeys(Effect effect)
  246. {
  247. return Keys.None;
  248. }
  249. private void AddEffectToMenu(Effect effect, bool withShortcut)
  250. {
  251. if (!FilterEffects(effect))
  252. {
  253. return;
  254. }
  255. string name = effect.Name;
  256. if (effect.CheckForEffectFlags(EffectFlags.Configurable))
  257. {
  258. string configurableFormat = PdnResources.GetString("Effects.Name.Format.Configurable");
  259. name = string.Format(configurableFormat, name);
  260. }
  261. PdnMenuItem mi = new PdnMenuItem(name, effect.Image, EffectMenuItem_Click);
  262. if (withShortcut)
  263. {
  264. mi.ShortcutKeys = GetEffectShortcutKeys(effect);
  265. }
  266. else
  267. {
  268. mi.ShortcutKeys = Keys.None;
  269. }
  270. mi.Tag = (object)effect.GetType();
  271. mi.Name = "Effect(" + effect.GetType().FullName + ")";
  272. PdnMenuItem addEffectHere = this;
  273. if (effect.SubMenuName != null)
  274. {
  275. PdnMenuItem subMenu = null;
  276. // search for this subMenu
  277. foreach (ToolStripItem sub in this.DropDownItems)
  278. {
  279. PdnMenuItem subpmi = sub as PdnMenuItem;
  280. if (subpmi != null)
  281. {
  282. if (subpmi.Text == effect.SubMenuName)
  283. {
  284. subMenu = subpmi;
  285. break;
  286. }
  287. }
  288. }
  289. if (subMenu == null)
  290. {
  291. subMenu = new PdnMenuItem(effect.SubMenuName, null, null);
  292. this.DropDownItems.Add(subMenu);
  293. }
  294. addEffectHere = subMenu;
  295. }
  296. addEffectHere.DropDownItems.Add(mi);
  297. }
  298. private void AddEffectsToMenu()
  299. {
  300. // Fill the menu with the effect names, and "..." if it is configurable
  301. EffectsCollection effectsCollection = this.Effects;
  302. Type[] effectTypes = effectsCollection.Effects;
  303. bool withShortcuts = EnableEffectShortcuts;
  304. List<Effect> newEffects = new List<Effect>();
  305. foreach (Type type in effectsCollection.Effects)
  306. {
  307. try
  308. {
  309. ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
  310. Effect effect = (Effect)ci.Invoke(null);
  311. if (FilterEffects(effect))
  312. {
  313. newEffects.Add(effect);
  314. }
  315. }
  316. catch (Exception ex)
  317. {
  318. // We don't want a DLL that can't be figured out to cause the app to crash
  319. //continue;
  320. AppWorkspace.ReportEffectLoadError(Triple.Create(type.Assembly, type, ex));
  321. }
  322. }
  323. newEffects.Sort(
  324. delegate(Effect lhs, Effect rhs)
  325. {
  326. return string.Compare(lhs.Name, rhs.Name, true);
  327. });
  328. List<string> subMenuNames = new List<string>();
  329. foreach (Effect effect in newEffects)
  330. {
  331. if (!string.IsNullOrEmpty(effect.SubMenuName))
  332. {
  333. subMenuNames.Add(effect.SubMenuName);
  334. }
  335. }
  336. subMenuNames.Sort(
  337. delegate(string lhs, string rhs)
  338. {
  339. return string.Compare(lhs, rhs, true);
  340. });
  341. string lastSubMenuName = null;
  342. foreach (string subMenuName in subMenuNames)
  343. {
  344. if (subMenuName == lastSubMenuName)
  345. {
  346. // skip duplicate names
  347. continue;
  348. }
  349. PdnMenuItem subMenu = new PdnMenuItem(subMenuName, null, null);
  350. DropDownItems.Add(subMenu);
  351. lastSubMenuName = subMenuName;
  352. }
  353. foreach (Effect effect in newEffects)
  354. {
  355. AddEffectToMenu(effect, withShortcuts);
  356. }
  357. }
  358. private static EffectsCollection GatherEffects()
  359. {
  360. List<Assembly> assemblies = new List<Assembly>();
  361. // PaintDotNet.Effects.dll
  362. assemblies.Add(Assembly.GetAssembly(typeof(Effect)));
  363. // TARGETDIR\Effects\*.dll
  364. string homeDir = PdnInfo.GetApplicationDir();
  365. string effectsDir = Path.Combine(homeDir, InvariantStrings.EffectsSubDir);
  366. bool dirExists;
  367. try
  368. {
  369. dirExists = Directory.Exists(effectsDir);
  370. }
  371. catch
  372. {
  373. dirExists = false;
  374. }
  375. if (dirExists)
  376. {
  377. string fileSpec = "*" + InvariantStrings.DllExtension;
  378. string[] filePaths = Directory.GetFiles(effectsDir, fileSpec);
  379. foreach (string filePath in filePaths)
  380. {
  381. Assembly pluginAssembly = null;
  382. try
  383. {
  384. pluginAssembly = Assembly.LoadFrom(filePath);
  385. assemblies.Add(pluginAssembly);
  386. }
  387. catch (Exception ex)
  388. {
  389. Tracing.Ping("Exception while loading " + filePath + ": " + ex.ToString());
  390. }
  391. }
  392. }
  393. EffectsCollection ec = new EffectsCollection(assemblies);
  394. return ec;
  395. }
  396. private void RepeatEffectMenuItem_Click(object sender, EventArgs e)
  397. {
  398. Exception exception = null;
  399. Effect effect = null;
  400. DocumentWorkspace activeDW = AppWorkspace.ActiveDocumentWorkspace;
  401. if (activeDW != null)
  402. {
  403. using (new PushNullToolMode(activeDW))
  404. {
  405. Surface copy = activeDW.BorrowScratchSurface(this.GetType() + ".RepeatEffectMenuItem_Click() utilizing scratch for rendering");
  406. try
  407. {
  408. using (new WaitCursorChanger(AppWorkspace))
  409. {
  410. copy.CopySurface(((BitmapLayer)activeDW.ActiveLayer).Surface);
  411. }
  412. PdnRegion selectedRegion = activeDW.Selection.CreateRegion();
  413. EffectEnvironmentParameters eep = new EffectEnvironmentParameters(
  414. AppWorkspace.AppEnvironment.PrimaryColor,
  415. AppWorkspace.AppEnvironment.SecondaryColor,
  416. AppWorkspace.AppEnvironment.PenInfo.Width,
  417. selectedRegion,
  418. copy);
  419. effect = (Effect)Activator.CreateInstance(this.lastEffect.GetType());
  420. effect.EnvironmentParameters = eep;
  421. EffectConfigToken token;
  422. if (this.lastEffectToken == null)
  423. {
  424. token = null;
  425. }
  426. else
  427. {
  428. token = (EffectConfigToken)this.lastEffectToken.Clone();
  429. }
  430. DoEffect(effect, token, selectedRegion, selectedRegion, copy, out exception);
  431. }
  432. finally
  433. {
  434. activeDW.ReturnScratchSurface(copy);
  435. }
  436. }
  437. }
  438. if (exception != null)
  439. {
  440. HandleEffectException(AppWorkspace, effect, exception);
  441. }
  442. }
  443. private void EffectMenuItem_Click(object sender, EventArgs e)
  444. {
  445. if (AppWorkspace.ActiveDocumentWorkspace == null)
  446. {
  447. return;
  448. }
  449. PdnMenuItem pmi = (PdnMenuItem)sender;
  450. Type effectType = (Type)pmi.Tag;
  451. RunEffect(effectType);
  452. }
  453. public void RunEffect(Type effectType)
  454. {
  455. bool oldDirtyValue = AppWorkspace.ActiveDocumentWorkspace.Document.Dirty;
  456. bool resetDirtyValue = false;
  457. AppWorkspace.Update(); // make sure the window is done 'closing'
  458. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  459. DocumentWorkspace activeDW = AppWorkspace.ActiveDocumentWorkspace;
  460. PdnRegion selectedRegion;
  461. if (activeDW.Selection.IsEmpty)
  462. {
  463. selectedRegion = new PdnRegion(activeDW.Document.Bounds);
  464. }
  465. else
  466. {
  467. selectedRegion = activeDW.Selection.CreateRegion();
  468. }
  469. Exception exception = null;
  470. Effect effect = null;
  471. BitmapLayer layer = (BitmapLayer)activeDW.ActiveLayer;
  472. using (new PushNullToolMode(activeDW))
  473. {
  474. try
  475. {
  476. effect = (Effect)Activator.CreateInstance(effectType);
  477. string name = effect.Name;
  478. EffectConfigToken newLastToken = null;
  479. if (!(effect.CheckForEffectFlags(EffectFlags.Configurable)))
  480. {
  481. Surface copy = activeDW.BorrowScratchSurface(this.GetType() + ".RunEffect() using scratch surface for non-configurable rendering");
  482. try
  483. {
  484. using (new WaitCursorChanger(AppWorkspace))
  485. {
  486. copy.CopySurface(layer.Surface);
  487. }
  488. EffectEnvironmentParameters eep = new EffectEnvironmentParameters(
  489. AppWorkspace.AppEnvironment.PrimaryColor,
  490. AppWorkspace.AppEnvironment.SecondaryColor,
  491. AppWorkspace.AppEnvironment.PenInfo.Width,
  492. selectedRegion,
  493. copy);
  494. effect.EnvironmentParameters = eep;
  495. DoEffect(effect, null, selectedRegion, selectedRegion, copy, out exception);
  496. }
  497. finally
  498. {
  499. activeDW.ReturnScratchSurface(copy);
  500. }
  501. }
  502. else
  503. {
  504. PdnRegion previewRegion = (PdnRegion)selectedRegion.Clone();
  505. previewRegion.Intersect(RectangleF.Inflate(activeDW.VisibleDocumentRectangleF, 1, 1));
  506. Surface originalSurface = activeDW.BorrowScratchSurface(this.GetType() + ".RunEffect() using scratch surface for rendering during configuration");
  507. try
  508. {
  509. using (new WaitCursorChanger(AppWorkspace))
  510. {
  511. originalSurface.CopySurface(layer.Surface);
  512. }
  513. EffectEnvironmentParameters eep = new EffectEnvironmentParameters(
  514. AppWorkspace.AppEnvironment.PrimaryColor,
  515. AppWorkspace.AppEnvironment.SecondaryColor,
  516. AppWorkspace.AppEnvironment.PenInfo.Width,
  517. selectedRegion,
  518. originalSurface);
  519. effect.EnvironmentParameters = eep;
  520. //
  521. IDisposable resumeTUFn = AppWorkspace.SuspendThumbnailUpdates();
  522. //
  523. using (EffectConfigDialog configDialog = effect.CreateConfigDialog())
  524. {
  525. configDialog.Opacity = 0.9;
  526. configDialog.Effect = effect;
  527. configDialog.EffectSourceSurface = originalSurface;
  528. configDialog.Selection = selectedRegion;
  529. BackgroundEffectRenderer ber = null;
  530. EventHandler eh =
  531. delegate(object sender, EventArgs e)
  532. {
  533. EffectConfigDialog ecf = (EffectConfigDialog)sender;
  534. if (ber != null)
  535. {
  536. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBarAsync();
  537. try
  538. {
  539. ber.Start();
  540. }
  541. catch (Exception ex)
  542. {
  543. exception = ex;
  544. ecf.Close();
  545. }
  546. }
  547. };
  548. configDialog.EffectTokenChanged += eh;
  549. if (this.effectTokens.ContainsKey(effectType))
  550. {
  551. EffectConfigToken oldToken = (EffectConfigToken)effectTokens[effectType].Clone();
  552. configDialog.EffectToken = oldToken;
  553. }
  554. ber = new BackgroundEffectRenderer(
  555. effect,
  556. configDialog.EffectToken,
  557. new RenderArgs(layer.Surface),
  558. new RenderArgs(originalSurface),
  559. previewRegion,
  560. tilesPerCpu * renderingThreadCount,
  561. renderingThreadCount);
  562. ber.RenderedTile += new RenderedTileEventHandler(RenderedTileHandler);
  563. ber.StartingRendering += new EventHandler(StartingRenderingHandler);
  564. ber.FinishedRendering += new EventHandler(FinishedRenderingHandler);
  565. invalidateTimer.Enabled = true;
  566. DialogResult dr;
  567. try
  568. {
  569. dr = Utility.ShowDialog(configDialog, AppWorkspace);
  570. }
  571. catch (Exception ex)
  572. {
  573. dr = DialogResult.None;
  574. exception = ex;
  575. }
  576. invalidateTimer.Enabled = false;
  577. this.InvalidateTimer_Tick(invalidateTimer, EventArgs.Empty);
  578. if (dr == DialogResult.OK)
  579. {
  580. this.effectTokens[effectType] = (EffectConfigToken)configDialog.EffectToken.Clone();
  581. }
  582. using (new WaitCursorChanger(AppWorkspace))
  583. {
  584. try
  585. {
  586. ber.Abort();
  587. ber.Join();
  588. }
  589. catch (Exception ex)
  590. {
  591. exception = ex;
  592. }
  593. ber.Dispose();
  594. ber = null;
  595. if (dr != DialogResult.OK)
  596. {
  597. ((BitmapLayer)activeDW.ActiveLayer).Surface.CopySurface(originalSurface);
  598. activeDW.ActiveLayer.Invalidate();
  599. }
  600. configDialog.EffectTokenChanged -= eh;
  601. configDialog.Hide();
  602. AppWorkspace.Update();
  603. previewRegion.Dispose();
  604. }
  605. //
  606. resumeTUFn.Dispose();
  607. resumeTUFn = null;
  608. //
  609. if (dr == DialogResult.OK)
  610. {
  611. PdnRegion remainingToRender = selectedRegion.Clone();
  612. PdnRegion alreadyRendered = PdnRegion.CreateEmpty();
  613. for (int i = 0; i < this.progressRegions.Length; ++i)
  614. {
  615. if (this.progressRegions[i] == null)
  616. {
  617. break;
  618. }
  619. else
  620. {
  621. remainingToRender.Exclude(this.progressRegions[i]);
  622. alreadyRendered.Union(this.progressRegions[i]);
  623. }
  624. }
  625. activeDW.ActiveLayer.Invalidate(alreadyRendered);
  626. newLastToken = (EffectConfigToken)configDialog.EffectToken.Clone();
  627. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  628. DoEffect(effect, newLastToken, selectedRegion, remainingToRender, originalSurface, out exception);
  629. }
  630. else // if (dr == DialogResult.Cancel)
  631. {
  632. using (new WaitCursorChanger(AppWorkspace))
  633. {
  634. activeDW.ActiveLayer.Invalidate();
  635. Utility.GCFullCollect();
  636. }
  637. resetDirtyValue = true;
  638. return;
  639. }
  640. }
  641. }
  642. catch (Exception ex)
  643. {
  644. exception = ex;
  645. }
  646. finally
  647. {
  648. activeDW.ReturnScratchSurface(originalSurface);
  649. }
  650. }
  651. // if it was from the Effects menu, save it as the "Repeat ...." item
  652. if (effect.Category == EffectCategory.Effect)
  653. {
  654. this.lastEffect = effect;
  655. if (newLastToken == null)
  656. {
  657. this.lastEffectToken = null;
  658. }
  659. else
  660. {
  661. this.lastEffectToken = (EffectConfigToken)newLastToken.Clone();
  662. }
  663. PopulateMenu(true);
  664. }
  665. }
  666. catch (Exception ex)
  667. {
  668. exception = ex;
  669. }
  670. finally
  671. {
  672. selectedRegion.Dispose();
  673. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  674. AppWorkspace.Widgets.StatusBarProgress.EraseProgressStatusBar();
  675. AppWorkspace.ActiveDocumentWorkspace.EnableOutlineAnimation = true;
  676. if (this.progressRegions != null)
  677. {
  678. for (int i = 0; i < this.progressRegions.Length; ++i)
  679. {
  680. if (this.progressRegions[i] != null)
  681. {
  682. this.progressRegions[i].Dispose();
  683. this.progressRegions[i] = null;
  684. }
  685. }
  686. }
  687. if (resetDirtyValue)
  688. {
  689. AppWorkspace.ActiveDocumentWorkspace.Document.Dirty = oldDirtyValue;
  690. }
  691. if (exception != null)
  692. {
  693. HandleEffectException(AppWorkspace, effect, exception);
  694. }
  695. }
  696. }
  697. }
  698. private void RenderedTileHandler(object sender, RenderedTileEventArgs e)
  699. {
  700. if (this.progressRegions[e.TileNumber] == null)
  701. {
  702. this.progressRegions[e.TileNumber] = e.RenderedRegion;
  703. }
  704. }
  705. private void InvalidateTimer_Tick(object sender, System.EventArgs e)
  706. {
  707. if (AppWorkspace.FindForm().WindowState == FormWindowState.Minimized)
  708. {
  709. return;
  710. }
  711. if (this.progressRegions == null)
  712. {
  713. return;
  714. }
  715. lock (this.progressRegions)
  716. {
  717. int min = this.progressRegionsStartIndex;
  718. int max;
  719. for (max = min; max < progressRegions.Length; ++max)
  720. {
  721. if (this.progressRegions[max] == null)
  722. {
  723. break;
  724. }
  725. }
  726. if (min != max)
  727. {
  728. using (PdnRegion updateRegion = PdnRegion.CreateEmpty())
  729. {
  730. for (int i = min; i < max; ++i)
  731. {
  732. updateRegion.Union(this.progressRegions[i]);
  733. }
  734. using (PdnRegion simplified = Utility.SimplifyAndInflateRegion(updateRegion))
  735. {
  736. AppWorkspace.ActiveDocumentWorkspace.ActiveLayer.Invalidate(simplified);
  737. }
  738. this.progressRegionsStartIndex = max;
  739. }
  740. }
  741. double progress = 100.0 * (double)max / (double)progressRegions.Length;
  742. AppWorkspace.Widgets.StatusBarProgress.SetProgressStatusBar(progress);
  743. }
  744. }
  745. private void FinishedRenderingHandler(object sender, EventArgs e)
  746. {
  747. if (AppWorkspace.InvokeRequired)
  748. {
  749. AppWorkspace.BeginInvoke(new EventHandler(FinishedRenderingHandler), new object[] { sender, e });
  750. }
  751. else
  752. {
  753. AppWorkspace.ActiveDocumentWorkspace.EnableOutlineAnimation = true;
  754. }
  755. }
  756. private void StartingRenderingHandler(object sender, EventArgs e)
  757. {
  758. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBarAsync();
  759. AppWorkspace.ActiveDocumentWorkspace.EnableOutlineAnimation = false;
  760. if (this.progressRegions == null)
  761. {
  762. this.progressRegions = new PdnRegion[tilesPerCpu * renderingThreadCount];
  763. }
  764. lock (this.progressRegions)
  765. {
  766. for (int i = 0; i < progressRegions.Length; ++i)
  767. {
  768. progressRegions[i] = null;
  769. }
  770. this.progressRegionsStartIndex = 0;
  771. }
  772. }
  773. private bool DoEffect(Effect effect, EffectConfigToken token, PdnRegion selectedRegion,
  774. PdnRegion regionToRender, Surface originalSurface, out Exception exception)
  775. {
  776. exception = null;
  777. bool oldDirtyValue = AppWorkspace.ActiveDocumentWorkspace.Document.Dirty;
  778. bool resetDirtyValue = false;
  779. bool returnVal = false;
  780. AppWorkspace.ActiveDocumentWorkspace.EnableOutlineAnimation = false;
  781. try
  782. {
  783. using (ProgressDialog aed = new ProgressDialog())
  784. {
  785. if (effect.Image != null)
  786. {
  787. aed.Icon = Utility.ImageToIcon(effect.Image, Utility.TransparentKey);
  788. }
  789. aed.Opacity = 0.9;
  790. aed.Value = 0;
  791. aed.Text = effect.Name;
  792. aed.Description = string.Format(PdnResources.GetString("Effects.ApplyingDialog.Description"), effect.Name);
  793. invalidateTimer.Enabled = true;
  794. using (new WaitCursorChanger(AppWorkspace))
  795. {
  796. HistoryMemento ha = null;
  797. DialogResult result = DialogResult.None;
  798. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  799. AppWorkspace.Widgets.LayerControl.SuspendLayerPreviewUpdates();
  800. try
  801. {
  802. ManualResetEvent saveEvent = new ManualResetEvent(false);
  803. BitmapHistoryMemento bha = null;
  804. // perf bug #1445: save this data in a background thread
  805. PdnRegion selectedRegionCopy = selectedRegion.Clone();
  806. PaintDotNet.Threading.ThreadPool.Global.QueueUserWorkItem(
  807. delegate(object context)
  808. {
  809. try
  810. {
  811. ImageResource image;
  812. if (effect.Image == null)
  813. {
  814. image = null;
  815. }
  816. else
  817. {
  818. image = ImageResource.FromImage(effect.Image);
  819. }
  820. bha = new BitmapHistoryMemento(effect.Name, image, this.AppWorkspace.ActiveDocumentWorkspace,
  821. this.AppWorkspace.ActiveDocumentWorkspace.ActiveLayerIndex, selectedRegionCopy, originalSurface);
  822. }
  823. finally
  824. {
  825. saveEvent.Set();
  826. selectedRegionCopy.Dispose();
  827. selectedRegionCopy = null;
  828. }
  829. });
  830. BackgroundEffectRenderer ber = new BackgroundEffectRenderer(
  831. effect,
  832. token,
  833. new RenderArgs(((BitmapLayer)AppWorkspace.ActiveDocumentWorkspace.ActiveLayer).Surface),
  834. new RenderArgs(originalSurface),
  835. regionToRender,
  836. tilesPerCpu * renderingThreadCount,
  837. renderingThreadCount);
  838. ber.RenderedTile += new RenderedTileEventHandler(aed.RenderedTileHandler);
  839. ber.RenderedTile += new RenderedTileEventHandler(RenderedTileHandler);
  840. ber.StartingRendering += new EventHandler(StartingRenderingHandler);
  841. ber.FinishedRendering += new EventHandler(aed.FinishedRenderingHandler);
  842. ber.FinishedRendering += new EventHandler(FinishedRenderingHandler);
  843. ber.Start();
  844. result = Utility.ShowDialog(aed, AppWorkspace);
  845. if (result == DialogResult.Cancel)
  846. {
  847. resetDirtyValue = true;
  848. using (new WaitCursorChanger(AppWorkspace))
  849. {
  850. try
  851. {
  852. ber.Abort();
  853. ber.Join();
  854. }
  855. catch (Exception ex)
  856. {
  857. exception = ex;
  858. }
  859. ((BitmapLayer)AppWorkspace.ActiveDocumentWorkspace.ActiveLayer).Surface.CopySurface(originalSurface);
  860. }
  861. }
  862. invalidateTimer.Enabled = false;
  863. try
  864. {
  865. ber.Join();
  866. }
  867. catch (Exception ex)
  868. {
  869. exception = ex;
  870. }
  871. ber.Dispose();
  872. saveEvent.WaitOne();
  873. saveEvent.Close();
  874. saveEvent = null;
  875. ha = bha;
  876. }
  877. catch (Exception)
  878. {
  879. using (new WaitCursorChanger(AppWorkspace))
  880. {
  881. ((BitmapLayer)AppWorkspace.ActiveDocumentWorkspace.ActiveLayer).Surface.CopySurface(originalSurface);
  882. ha = null;
  883. }
  884. }
  885. finally
  886. {
  887. AppWorkspace.Widgets.LayerControl.ResumeLayerPreviewUpdates();
  888. }
  889. using (PdnRegion simplifiedRenderRegion = Utility.SimplifyAndInflateRegion(selectedRegion))
  890. {
  891. using (new WaitCursorChanger(AppWorkspace))
  892. {
  893. AppWorkspace.ActiveDocumentWorkspace.ActiveLayer.Invalidate(simplifiedRenderRegion);
  894. }
  895. }
  896. using (new WaitCursorChanger(AppWorkspace))
  897. {
  898. if (result == DialogResult.OK)
  899. {
  900. if (ha != null)
  901. {
  902. AppWorkspace.ActiveDocumentWorkspace.History.PushNewMemento(ha);
  903. }
  904. AppWorkspace.Update();
  905. returnVal = true;
  906. }
  907. else
  908. {
  909. Utility.GCFullCollect();
  910. }
  911. }
  912. } // using
  913. } // using
  914. }
  915. finally
  916. {
  917. AppWorkspace.ActiveDocumentWorkspace.EnableOutlineAnimation = true;
  918. if (resetDirtyValue)
  919. {
  920. AppWorkspace.ActiveDocumentWorkspace.Document.Dirty = oldDirtyValue;
  921. }
  922. }
  923. AppWorkspace.Widgets.StatusBarProgress.EraseProgressStatusBarAsync();
  924. return returnVal;
  925. }
  926. }
  927. }