PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Menus/EffectMenuBase.cs

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