diff --git a/ScriptLab/ConfigDialog.cs b/ScriptLab/ConfigDialog.cs index 4459010..60f7fc0 100644 --- a/ScriptLab/ConfigDialog.cs +++ b/ScriptLab/ConfigDialog.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using System.Windows.Forms; @@ -14,7 +15,7 @@ namespace pyrochild.effects.scriptlab { public partial class ConfigDialog : EffectConfigDialog { - Dictionary AvailableEffects = new Dictionary(); + Dictionary AvailableEffects = new Dictionary(); List AvailableEffectsIcons; List AvailableEffectsNames; @@ -37,66 +38,39 @@ public ConfigDialog() btnDonate.Image = new Bitmap(typeof(ScriptLab), "images.money.png"); btnChangeColor.Image = new Bitmap(typeof(ScriptLab), "images.colorwheel.png"); Text = ScriptLab.StaticDialogName; + } - List RawEffects = CommonUtil.GatherEffects(); - - RawEffects.Sort((t1, t2) => - { - try - { - string e1 = ((Effect)(t1.GetConstructor(Type.EmptyTypes).Invoke(new object[0]))).Name; - string e2 = ((Effect)(t2.GetConstructor(Type.EmptyTypes).Invoke(new object[0]))).Name; - if (e1.Contains("&")) - { - e1 = e1.Remove(e1.IndexOf("&"), 1); - } - if (e2.Contains("&")) - { - e2 = e2.Remove(e2.IndexOf("&"), 1); - } + protected override void OnLoad(EventArgs e) + { + string slName = this.Effect.Name; - return e1.CompareTo(e2); - } - catch - { - return 0; - } - }); + List RawEffects = this.Services + .GetService().EffectInfos + .Where(x => x.Name != slName) + .OrderBy(x => x.Name) + .ToList(); AvailableEffectsNames = new List(); AvailableEffectsIcons = new List(); for (int i = 0; i < RawEffects.Count; i++) { - try + if ((RawEffects[i].Category != EffectCategory.DoNotDisplay //effects that don't want to be shown + || RawEffects[i].Type.Name == nameof(RotateZoomEffect)) //unless it's the rotatezoomeffect which hides itself from the ffects menu so it can be put in Layers + && !AvailableEffects.ContainsKey(RawEffects[i].Type.FullName + ":" + RawEffects[i].Name)) { - Effect effect = (Effect)(RawEffects[i].GetConstructor(Type.EmptyTypes).Invoke(new object[0])); - - if ((effect.Category != EffectCategory.DoNotDisplay //effects that don't want to be shown - || effect is RotateZoomEffect) //unless it's the rotatezoomeffect which hides itself from the ffects menu so it can be put in Layers - && !AvailableEffects.ContainsKey(RawEffects[i].FullName + ":" + effect.Name)) - { - AvailableEffects.Add(RawEffects[i].FullName + ":" + effect.Name, RawEffects[i]); - AvailableEffectsNames.Add(effect.Name); - AvailableEffectsIcons.Add(effect.Image); - SearchResultEffects.Add(RawEffects[i].FullName + ":" + effect.Name); - SearchResultIndices.Add(i, i); - } - else - { - RawEffects.RemoveAt(i); - --i; - } - } - catch - { - RawEffects.RemoveAt(i); - --i; + AvailableEffects.Add(RawEffects[i].Type.FullName + ":" + RawEffects[i].Name, RawEffects[i]); + AvailableEffectsNames.Add(RawEffects[i].Name); + AvailableEffectsIcons.Add(RawEffects[i].Image); + SearchResultEffects.Add(RawEffects[i].Type.FullName + ":" + RawEffects[i].Name); + SearchResultIndices.Add(i, i); } } lbAvailable.Items.AddRange(AvailableEffectsNames.ToArray()); lbAvailable.Invalidate(); + + base.OnLoad(e); } void lbScript_DrawItem(object sender, DrawItemEventArgs e) @@ -111,7 +85,7 @@ void lbScript_DrawItem(object sender, DrawItemEventArgs e) Image icon = step.Icon; string text = step.Name; - if (step.EffectType == null) //effect wasn't found + if (!step.EffectAvailable) //effect wasn't found { e.Graphics.DrawRectangle(Pens.Red, Rectangle.Inflate(e.Bounds, -1, -1)); e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(127, Color.Red)), Rectangle.Inflate(e.Bounds, -1, -1)); @@ -237,8 +211,8 @@ void AddSelectedEffect() { ConfigToken token = (ConfigToken)theEffectToken; string effectidentifiername = SearchResultEffects[lbAvailable.SelectedIndex]; - Type t = AvailableEffects[effectidentifiername]; - Effect effect = (Effect)t.GetConstructor(Type.EmptyTypes).Invoke(new object[0]); + IEffectInfo effectInfo = AvailableEffects[effectidentifiername]; + Effect effect = effectInfo.CreateInstance(); effect.EnvironmentParameters = Effect.EnvironmentParameters; effect.Services = Services; if (effect.Options.Flags.HasFlag(EffectFlags.Configurable)) @@ -250,23 +224,23 @@ void AddSelectedEffect() if (dialog.ShowDialog(this) == DialogResult.OK) { - token.effects.Add(new ScriptStep(effect.Name, effect.Image, t, dialog.EffectToken, Effect.EnvironmentParameters.PrimaryColor, Effect.EnvironmentParameters.SecondaryColor)); - lbScript.Items.Add(effect.Name); + token.effects.Add(new ScriptStep(effectInfo, dialog.EffectToken, Effect.EnvironmentParameters.PrimaryColor, Effect.EnvironmentParameters.SecondaryColor)); + lbScript.Items.Add(effectInfo.Name); FinishTokenUpdate(); } } catch (Exception e) { MessageBox.Show(this, - "Error occurred in configuration dialog for " + effect.Name + ":\n\n" + e.ToString(), - effect.Name+" Error", + "Error occurred in configuration dialog for " + effectInfo.Name + ":\n\n" + e.ToString(), + effectInfo.Name+" Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { - token.effects.Add(new ScriptStep(effect.Name, effect.Image, t, null, Effect.EnvironmentParameters.PrimaryColor, Effect.EnvironmentParameters.SecondaryColor)); - lbScript.Items.Add(effect.Name); + token.effects.Add(new ScriptStep(effectInfo, null, Effect.EnvironmentParameters.PrimaryColor, Effect.EnvironmentParameters.SecondaryColor)); + lbScript.Items.Add(effectInfo.Name); FinishTokenUpdate(); } } @@ -351,10 +325,10 @@ private void ChangeSelectedEffect() ConfigToken token = (ConfigToken)theEffectToken; int i = lbScript.SelectedIndex; ScriptStep step = token.effects[i]; - Type type = step.EffectType; - if (type != null) + + if (step.EffectAvailable) { - Effect effect = (Effect)type.GetConstructor(Type.EmptyTypes).Invoke(new object[0]); + Effect effect = step.CreateInstance(); effect.EnvironmentParameters = Effect.EnvironmentParameters; effect.Services = Services; if (effect.Options.Flags.HasFlag(EffectFlags.Configurable)) @@ -464,10 +438,10 @@ private bool SaveTxtFile(string path, out Exception exception) sw.WriteLine(); string effectname = sls.Effects[i]; - Type effecttype; - if (AvailableEffects.TryGetValue(effectname, out effecttype)) + IEffectInfo effectInfo; + if (AvailableEffects.TryGetValue(effectname, out effectInfo)) { - Effect effect = (Effect)effecttype.GetConstructor(Type.EmptyTypes).Invoke(new object[0]); + Effect effect = effectInfo.CreateInstance(); effect.EnvironmentParameters = Effect.EnvironmentParameters; effect.Services = Services; if (effect.Options.Flags.HasFlag(EffectFlags.Configurable)) @@ -681,7 +655,7 @@ private void txtSearch_TextChanged(object sender, EventArgs e) { name = name.Remove(name.IndexOf("&"), 1); } - Effect effect = (Effect)AvailableEffects[keyenumerator.Current].GetConstructor(Type.EmptyTypes).Invoke(new object[0]); + IEffectInfo effect = AvailableEffects[keyenumerator.Current]; if (name.ToLower().Contains(txtSearch.Text.ToLower()) || (effect.SubMenuName != null && effect.SubMenuName.ToLower().Contains(txtSearch.Text.ToLower()))) { @@ -853,10 +827,10 @@ private void EnableScriptControlButtons() ConfigToken token = (ConfigToken)theEffectToken; int i = lbScript.SelectedIndex; ScriptStep step = token.effects[i]; - Type type = step.EffectType; - if (type != null) + + if (step.EffectAvailable) { - Effect effect = (Effect)type.GetConstructor(Type.EmptyTypes).Invoke(new object[0]); + Effect effect = step.CreateInstance(); if (effect.Options.Flags.HasFlag(EffectFlags.Configurable)) { btnChange.Enabled = true; diff --git a/ScriptLab/Effect.cs b/ScriptLab/Effect.cs index c531693..fe2d8eb 100644 --- a/ScriptLab/Effect.cs +++ b/ScriptLab/Effect.cs @@ -105,21 +105,21 @@ public override void Render(EffectConfigToken parameters, RenderArgs dstArgs, Re for (int i = 0; i < token.effects.Count; ++i) { ScriptStep step = token.effects[i]; - Type type = step.EffectType; - if (type == null) + if (!step.EffectAvailable) { if (dialog != null) dialog.IncrementProgressBarValue(i, 1); } else { - Effect effect = (Effect)(type.GetConstructor(Type.EmptyTypes).Invoke(new object[0])); + Effect effect = step.CreateInstance(); effect.Services = Services; effect.EnvironmentParameters = new EffectEnvironmentParameters( step.PrimaryColor, step.SecondaryColor, EnvironmentParameters.BrushWidth, + Document.DefaultResolution, selection, EnvironmentParameters.SourceSurface); diff --git a/ScriptLab/Properties/AssemblyInfo.cs b/ScriptLab/Properties/AssemblyInfo.cs index 50e9eb1..667d06e 100644 --- a/ScriptLab/Properties/AssemblyInfo.cs +++ b/ScriptLab/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Versioning; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -12,6 +13,7 @@ [assembly: AssemblyCopyright("Copyright © 2007-2016 Zach Walker")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] +[assembly: SupportedOSPlatform("Windows")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/ScriptLab/ScriptLab.csproj b/ScriptLab/ScriptLab.csproj index 5ca499b..a7357a9 100644 --- a/ScriptLab/ScriptLab.csproj +++ b/ScriptLab/ScriptLab.csproj @@ -1,147 +1,71 @@ - - + - Debug - AnyCPU - 9.0.30729 - 2.0 - {13154F93-F0E2-4A2D-BCAF-3A3E4CD44A60} + net5.0-windows Library - Properties pyrochild.effects.scriptlab - ScriptLab - - - - - 3.5 - - - v4.7 - - - - - - - - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - + false + true + true + false - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 true AllRules.ruleset - false - pdbonly - true - bin\Release\ - TRACE - prompt - 4 true 4096 AllRules.ruleset - false true bin\Custom\ - DEBUG;TRACE true - full - AnyCPU - prompt AllRules.ruleset - false bin\Debug x86\ AllRules.ruleset - false - false - true bin\x64\Debug\ - DEBUG;TRACE true - full - x64 bin\Debug\ScriptLab.dll.CodeAnalysisLog.xml bin\x64\Debug\ScriptLab.dll true GlobalSuppressions.cs - prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false bin\x64\Release\ - TRACE true - true 4096 - pdbonly - x64 ..\.dll\ScriptLab.dll.CodeAnalysisLog.xml bin\x64\Release\ScriptLab.dll true GlobalSuppressions.cs - prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false true bin\x64\Custom\ - DEBUG;TRACE true - full - x64 bin\Custom\ScriptLab.dll.CodeAnalysisLog.xml bin\x64\Custom\ScriptLab.dll true GlobalSuppressions.cs - prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false bin\x64\Debug x86\ - x64 bin\Debug x86\ScriptLab.dll.CodeAnalysisLog.xml bin\x64\Debug x86\ScriptLab.dll true @@ -149,18 +73,6 @@ AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false - - - - - copy "$(TargetPath)" "%25PROGRAMW6432%25\Paint.NET\Effects\" @@ -175,107 +87,31 @@ C:\Program Files\paint.net\PaintDotNet.Effects.dll - - - 3.5 - - - - - - - 3.0 - - - + Component - - ColorPanel.cs - - + Component - + Component - - - - - - - - - Form - - - TwoColorDialog.cs - - - Form - - - ConfigDialog.cs - - + Component - - - - True - True - Resources.resx - - - - - - - ColorWheel.cs - - - Designer - ConfigDialog.cs - - - Designer - ResXFileCodeGenerator - Resources.Designer.cs - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Microsoft Visual Basic PowerPacks 10.0 - true - - - False - Windows Installer 3.1 - true - - + + + \ No newline at end of file diff --git a/ScriptLab/ScriptLabScript.cs b/ScriptLab/ScriptLabScript.cs index 6fcf73f..815bbe4 100644 --- a/ScriptLab/ScriptLabScript.cs +++ b/ScriptLab/ScriptLabScript.cs @@ -88,13 +88,13 @@ public static ScriptLabScript FromToken(ConfigToken token) foreach (ScriptStep step in token.effects) { - if (step.EffectType == null) + if (!step.EffectAvailable) { sls.Add(step.Name, step.Name, new object[0], new object[0], Pair.Create(step.PrimaryColor, step.SecondaryColor)); } else if (step.Token == null) { - sls.Add(step.EffectType.FullName + ":" + step.Name, step.Name, null, null, Pair.Create(step.PrimaryColor, step.SecondaryColor)); + sls.Add(step.EffectKey, step.Name, null, null, Pair.Create(step.PrimaryColor, step.SecondaryColor)); } else if (step.Token is PropertyBasedEffectConfigToken) { @@ -117,13 +117,13 @@ public static ScriptLabScript FromToken(ConfigToken token) } } - sls.Add(step.EffectType.FullName + ":" + step.Name, step.Name, properties, null, Pair.Create(step.PrimaryColor, step.SecondaryColor)); + sls.Add(step.EffectKey, step.Name, properties, null, Pair.Create(step.PrimaryColor, step.SecondaryColor)); } else { object[][] propertiesAndFields = MembersToObjectArray(step.Token); - sls.Add(step.EffectType.FullName + ":" + step.Name, step.Name, propertiesAndFields[0], propertiesAndFields[1], Pair.Create(step.PrimaryColor, step.SecondaryColor)); + sls.Add(step.EffectKey, step.Name, propertiesAndFields[0], propertiesAndFields[1], Pair.Create(step.PrimaryColor, step.SecondaryColor)); } } @@ -224,19 +224,19 @@ private static bool IsSerializable(Type t) } } - public ConfigToken ToToken(Dictionary effects, IServiceProvider services, Surface effectSourceSurface) + public ConfigToken ToToken(Dictionary effects, IServiceProvider services, Surface effectSourceSurface) { ConfigToken token = new ConfigToken(); for (int i = 0; i < Effects.Count; i++) { EffectConfigToken stepToken = null; - Type type; - if (effects.TryGetValue(Effects[i], out type)) + IEffectInfo effectInfo; + if (effects.TryGetValue(Effects[i], out effectInfo)) { - Effect effect = (Effect)(type.GetConstructor(Type.EmptyTypes).Invoke(new object[0])); + Effect effect = effectInfo.CreateInstance(); effect.Services = services; - effect.EnvironmentParameters = new EffectEnvironmentParameters(allcolors[i].First, allcolors[i].Second, 2, new PdnRegion(effectSourceSurface.Bounds), effectSourceSurface); + effect.EnvironmentParameters = new EffectEnvironmentParameters(allcolors[i].First, allcolors[i].Second, 2, Document.DefaultResolution, new PdnRegion(effectSourceSurface.Bounds), effectSourceSurface); if (effect.Options.Flags.HasFlag(EffectFlags.Configurable)) { try @@ -273,18 +273,18 @@ public ConfigToken ToToken(Dictionary effects, IServiceProvider ser } catch (Exception) { } } - token.effects.Add(new ScriptStep(effect.Name, effect.Image, type, stepToken, Colors[i].First, Colors[i].Second)); + token.effects.Add(new ScriptStep(effectInfo, stepToken, Colors[i].First, Colors[i].Second)); } else { - token.effects.Add(new ScriptStep(Effects[i], null, null, null, Colors[i].First, Colors[i].Second)); + token.effects.Add(new ScriptStep(Effects[i], Colors[i].First, Colors[i].Second)); } } return token; } - private static void SetObjectProperty(object obj, PropertyInfo propertyInfo, object val, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) + private static void SetObjectProperty(object obj, PropertyInfo propertyInfo, object val, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) { if (val != null) { @@ -311,7 +311,7 @@ private static void SetObjectProperty(object obj, PropertyInfo propertyInfo, obj } } - private static void SetObjectField(object obj, FieldInfo fieldInfo, object val, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) + private static void SetObjectField(object obj, FieldInfo fieldInfo, object val, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) { if (val != null) { @@ -339,7 +339,7 @@ private static void SetObjectField(object obj, FieldInfo fieldInfo, object val, } } - private static void SetObjectPropertiesAndFields(object val, object[] properties, object[] fields, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) + private static void SetObjectPropertiesAndFields(object val, object[] properties, object[] fields, Dictionary effects, IServiceProvider services, Surface effectSourceSurface) { Type type = val.GetType(); PropertyInfo[] pi = type.GetProperties(); diff --git a/ScriptLab/ScriptStep.cs b/ScriptLab/ScriptStep.cs index 2402da1..84742de 100644 --- a/ScriptLab/ScriptStep.cs +++ b/ScriptLab/ScriptStep.cs @@ -7,21 +7,33 @@ namespace pyrochild.effects.scriptlab { public class ScriptStep { + public readonly bool EffectAvailable; public string Name; public Image Icon; - public Type EffectType; public EffectConfigToken Token; public ColorBgra PrimaryColor; public ColorBgra SecondaryColor; + public Func CreateInstance; + public string EffectKey; - public ScriptStep(string name, Image icon, Type type, EffectConfigToken token, ColorBgra primary, ColorBgra secondary) + public ScriptStep(IEffectInfo effectInfo, EffectConfigToken token, ColorBgra primary, ColorBgra secondary) { - Name = name; - Icon = icon; - EffectType = type; + Name = effectInfo.Name; + Icon = effectInfo.Image; Token = token; PrimaryColor = primary; SecondaryColor = secondary; + CreateInstance = () => effectInfo.CreateInstance(); + EffectAvailable = true; + EffectKey = effectInfo.Type.FullName + ":" + effectInfo.Name; + } + + public ScriptStep(string notInstalledName, ColorBgra primary, ColorBgra secondary) + { + Name = notInstalledName; + PrimaryColor = primary; + SecondaryColor = secondary; + EffectAvailable = false; } } } \ No newline at end of file diff --git a/ScriptLab/common/BackgroundEffectRenderer.cs b/ScriptLab/common/BackgroundEffectRenderer.cs index 13d5093..f1b4fe0 100644 --- a/ScriptLab/common/BackgroundEffectRenderer.cs +++ b/ScriptLab/common/BackgroundEffectRenderer.cs @@ -47,7 +47,7 @@ internal sealed class BackgroundEffectRenderer private Rectangle[][] tileRegions; private PdnRegion[] tilePdnRegions; private int tileCount; - private PrivateThreadPool threadPool; + private EffectRendererWorkItemQueue threadPool; private RenderArgs dstArgs; private RenderArgs srcArgs; private int workerThreads; @@ -171,7 +171,7 @@ private static unsafe void RenderWithClipMask( { // dstArgs = (srcArgs * (1 - clipMask)) + (dstArgs * clipMask) // TODO: optimize, or at least refactor into its own method - using (ISurface clipMask = clipMaskRenderer.UseTileOrToSurface(bounds)) + using (ISurface clipMask = clipMaskRenderer.ToSurface(bounds)) { int width = bounds.Width; int height = bounds.Height; @@ -183,14 +183,14 @@ private static unsafe void RenderWithClipMask( int srcStride = srcArgs.Surface.Stride; int clipMaskStride = clipMask.Stride; - ColorBgra* dstNextRowPtr = dstArgs.Surface.GetPointAddress(left, top); - ColorBgra* srcNextRowPtr = srcArgs.Surface.GetPointAddress(left, top); + ColorBgra* dstNextRowPtr = dstArgs.Surface.GetPointPointer(left, top); + ColorBgra* srcNextRowPtr = srcArgs.Surface.GetPointPointer(left, top); byte* clipMaskNextRowPtr = (byte*)clipMask.Scan0; int rows = height; while (rows > 0) { - ColorBgra.Underwrite(srcNextRowPtr, dstNextRowPtr, clipMaskNextRowPtr, width); + Underwrite(srcNextRowPtr, dstNextRowPtr, clipMaskNextRowPtr, width); dstNextRowPtr = (ColorBgra*)((byte*)dstNextRowPtr + dstStride); srcNextRowPtr = (ColorBgra*)((byte*)srcNextRowPtr + srcStride); @@ -202,6 +202,42 @@ private static unsafe void RenderWithClipMask( } } + private static unsafe void Underwrite(ColorBgra* pSrc1, ColorBgra* pDstSrc2, byte* pSrc2A, int length) + { + int skipCount = 0; + while ((length - skipCount) > 0 && (*(pSrc2A + skipCount)) == 255) + { + ++skipCount; + } + + length -= skipCount; + pSrc1 += skipCount; + pDstSrc2 += skipCount; + pSrc2A += skipCount; + + while (length > 0) + { + byte src2A = *pSrc2A; + if (src2A == 255) + { + // pDstSrc2 already equals what it should be + } + else if (src2A == 0) + { + *pDstSrc2 = *pSrc1; + } + else + { + *pDstSrc2 = ColorBgra.Blend(*pSrc1, *pDstSrc2, src2A); + } + + --length; + ++pSrc1; + ++pDstSrc2; + ++pSrc2A; + } + } + private void ThreadFunction() { if (srcArgs.Surface.Scan0.MaySetAllowWrites) @@ -249,10 +285,10 @@ private void ThreadFunction() token = effectTokenCopy.CloneT(); } - threadPool.QueueUserWorkItem(rcwc, token); + this.threadPool.Enqueue(() => rcwc(token)); } - threadPool.Drain(); + threadPool.Join(); /* if (i == this.workerThreads) @@ -275,7 +311,7 @@ private void ThreadFunction() { try { - threadPoolP.Drain(); + threadPoolP.Join(); } catch (Exception) @@ -380,7 +416,7 @@ public void Abort() } Join(); - threadPool.Drain(); + threadPool.Join(); } } @@ -590,7 +626,10 @@ public BackgroundEffectRenderer( this.clipMaskRenderer = clipMaskRenderer; - threadPool = new PrivateThreadPool(this.workerThreads, false); + threadPool = new EffectRendererWorkItemQueue( + MultithreadedWorkItemDispatcher.Default, + WorkItemQueuePriority.Normal, + workerThreads); } ~BackgroundEffectRenderer() diff --git a/ScriptLab/common/ColorWheel.cs b/ScriptLab/common/ColorWheel.cs index 7c0c4bd..519a988 100644 --- a/ScriptLab/common/ColorWheel.cs +++ b/ScriptLab/common/ColorWheel.cs @@ -174,7 +174,7 @@ private unsafe void DrawWheel(Surface sfc) for (int y = 0; y < sfc.Height; y++) { - ColorBgra* ptr = sfc.GetRowAddress(y); + ColorBgra* ptr = sfc.GetRowPointer(y); float cy = radius - y; float cy2 = cy * cy; for (int x = 0; x < sfc.Width; x++) diff --git a/ScriptLab/common/CommonUtil.cs b/ScriptLab/common/CommonUtil.cs deleted file mode 100644 index d67a574..0000000 --- a/ScriptLab/common/CommonUtil.cs +++ /dev/null @@ -1,74 +0,0 @@ -using PaintDotNet; -using PaintDotNet.Effects; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; - -namespace pyrochild.effects.common -{ - public static class CommonUtil - { - - public static List GatherEffects() - { - List assemblies = new List(); - List ec = new List(); - - // PaintDotNet.Effects.dll - assemblies.Add(Assembly.GetAssembly(typeof(Effect))); - - // TARGETDIR\Effects\*.dll - string homeDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - string effectsDir = Path.Combine(homeDir, "Effects"); - bool dirExists; - - try - { - dirExists = Directory.Exists(effectsDir); - } - - catch - { - dirExists = false; - } - - if (dirExists) - { - string fileSpec = "*.dll"; - string[] filePaths = Directory.GetFiles(effectsDir, fileSpec); - - foreach (string filePath in filePaths) - { - Assembly pluginAssembly = null; - - try - { - pluginAssembly = Assembly.LoadFrom(filePath); - assemblies.Add(pluginAssembly); - } - catch (Exception) - { - } - } - } - - foreach (Assembly a in assemblies) - { - try - { - foreach (Type t in a.GetTypes()) - { - if (t.IsSubclassOf(typeof(Effect)) && !t.IsAbstract && !t.IsObsolete(false)) - { - ec.Add(t); - } - } - } - catch { } - } - - return ec; - } - } -} \ No newline at end of file diff --git a/ScriptLab/common/EffectRendererWorkItemQueue.cs b/ScriptLab/common/EffectRendererWorkItemQueue.cs new file mode 100644 index 0000000..5610bec --- /dev/null +++ b/ScriptLab/common/EffectRendererWorkItemQueue.cs @@ -0,0 +1,140 @@ +///////////////////////////////////////////////////////////////////////////////// +// paint.net // +// Copyright (C) dotPDN LLC, Rick Brewster, and contributors. // +// All Rights Reserved. // +///////////////////////////////////////////////////////////////////////////////// + +using PaintDotNet; +using PaintDotNet.Diagnostics; +using PaintDotNet.Threading; +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading; + +namespace pyrochild.effects.common +{ + internal sealed class EffectRendererWorkItemQueue + : WorkItemQueue + { + private readonly object sync = new object(); + private readonly int maxThreadCount; + private readonly ConcurrentQueue queue; + private int activeThreadCount; + private long totalEnqueueCount; + private long totalNotifyCount; + private ManualResetEvent idleEvent; + private IDisposable threadCountToken; + + public override int WorkItemCount + { + get + { + lock (this.sync) + { + return (this.queue == null) ? 0 : Math.Min(this.maxThreadCount, this.queue.Count); + } + } + } + + public EffectRendererWorkItemQueue( + WorkItemDispatcher dispatcher, + WorkItemQueuePriority priority, + int maxThreadCount) + : base(dispatcher, priority) + { + this.maxThreadCount = maxThreadCount; + this.queue = new ConcurrentQueue(); + this.idleEvent = new ManualResetEvent(true); + + MultithreadedWorkItemDispatcher asMTWID = dispatcher as MultithreadedWorkItemDispatcher; + if (asMTWID != null) + { + this.threadCountToken = asMTWID.UseThreadCount(maxThreadCount); + } + } + + protected override void Dispose(bool disposing) + { + DisposableUtil.Free(ref this.threadCountToken, disposing); + base.Dispose(disposing); + } + + public void Enqueue(Action workItem) + { + Validate.IsNotNull(workItem, nameof(workItem)); + + lock (this.sync) + { + this.queue.Enqueue(workItem); + ++this.totalEnqueueCount; + this.idleEvent.Reset(); + } + + UpdateNotifyWorkItemsQueued(); + } + + public void Join() + { + this.idleEvent.WaitOne(); + } + + private void UpdateNotifyWorkItemsQueued() + { + int notifyCount; + lock (this.sync) + { + long maxNotifyCount = this.totalEnqueueCount - this.totalNotifyCount; + int availableThreads = this.maxThreadCount - this.activeThreadCount; + notifyCount = checked((int)Math.Min(availableThreads, maxNotifyCount)); + this.totalNotifyCount += notifyCount; + this.activeThreadCount += notifyCount; + + if (this.queue.Count == 0 && this.activeThreadCount == 0) + { + Debug.Assert(this.totalNotifyCount == this.totalEnqueueCount); + Debug.Assert(notifyCount == 0); + this.idleEvent.Set(); + } + } + + if (notifyCount > 0) + { + NotifyWorkItemsQueued(notifyCount); + } + } + + protected override void OnExecuteNextWorkItem() + { + Action workItem; + lock (this.sync) + { + if (!this.queue.TryDequeue(out workItem)) + { + throw new InternalErrorException(); + } + } + + try + { + workItem(); + } + catch (Exception ex) + { + if (!TryReportException(new WorkItemExceptionInfo(this, ex, workItem))) + { + throw; + } + } + finally + { + lock (this.sync) + { + --this.activeThreadCount; + } + + UpdateNotifyWorkItemsQueued(); + } + } + } +} diff --git a/ScriptLab/common/FileMan.cs b/ScriptLab/common/FileMan.cs index 3be9b8a..e643df8 100644 --- a/ScriptLab/common/FileMan.cs +++ b/ScriptLab/common/FileMan.cs @@ -93,7 +93,6 @@ public static bool LoadFileXML(string fileName, Type type, out object loadedObje } public static bool LoadFileXML(string fileName, Type type, out object loadedObject, XmlAttributeOverrides xao) { - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); StreamReader sr = null; try { @@ -111,30 +110,15 @@ public static bool LoadFileXML(string fileName, Type type, out object loadedObje finally { if (sr != null) sr.Close(); - AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve); } } - static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e) - { - try - { - Assembly assembly = Assembly.Load(e.Name); - if (assembly != null) - return assembly; - } - - catch { } - return Assembly.GetExecutingAssembly(); - } - public static bool SaveFileXML(string fileName, object saveMe) { return SaveFileXML(fileName, saveMe, null); } public static bool SaveFileXML(string fileName, object saveMe, XmlAttributeOverrides xao) { - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); StreamWriter sw = null; try { @@ -156,7 +140,6 @@ public static bool SaveFileXML(string fileName, object saveMe, XmlAttributeOverr finally { if (sw != null) sw.Close(); - AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve); } } } diff --git a/ScriptLab/common/PrivateThreadPool.cs b/ScriptLab/common/PrivateThreadPool.cs deleted file mode 100644 index abb4e0b..0000000 --- a/ScriptLab/common/PrivateThreadPool.cs +++ /dev/null @@ -1,203 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// Paint.NET // -// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. // -// Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // -// See src/Resources/Files/License.txt for full licensing and attribution // -// details. // -// . // -///////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections; -using System.Threading; -using PaintDotNet.Threading; -using PaintDotNet; - -namespace pyrochild.effects.common -{ - /// - /// Uses the .NET ThreadPool to do our own type of thread pool. The main difference - /// here is that we limit our usage of the thread pool, and that we can also drain - /// the threads we have ("fence"). The default maximum number of threads is - /// Processor.LogicalCpuCount. - /// - public class PrivateThreadPool - { - private static PrivateThreadPool global = new PrivateThreadPool(2 * Environment.ProcessorCount); - public static PrivateThreadPool Global - { - get - { - return global; - } - } - - private ArrayList exceptions = ArrayList.Synchronized(new ArrayList()); - private bool useFXTheadPool; - - public static int MinimumCount - { - get - { - return WaitableCounter.MinimumCount; - } - } - - public static int MaximumCount - { - get - { - return WaitableCounter.MaximumCount; - } - } - - public Exception[] Exceptions - { - get - { - return (Exception[])this.exceptions.ToArray(typeof(Exception)); - } - } - - public void ClearExceptions() - { - exceptions.Clear(); - } - - public void DrainExceptions() - { - if (this.exceptions.Count > 0) - { - throw new WorkerThreadException("Worker thread threw an exception", (Exception)this.exceptions[0]); - } - - ClearExceptions(); - } - - private WaitableCounter counter; - - public PrivateThreadPool() - : this(Environment.ProcessorCount) - { - } - - public PrivateThreadPool(int maxThreads) - : this(maxThreads, true) - { - } - - public PrivateThreadPool(int maxThreads, bool useFXThreadPool) - { - if (maxThreads < MinimumCount || maxThreads > MaximumCount) - { - throw new ArgumentOutOfRangeException("maxThreads", "must be between " + MinimumCount.ToString() + " and " + MaximumCount.ToString() + " inclusive"); - } - - this.counter = new WaitableCounter(maxThreads); - this.useFXTheadPool = useFXThreadPool; - } - - /* - private sealed class FunctionCallTrampoline - { - private Delegate theDelegate; - private object[] parameters; - - public void WaitCallback(object ignored) - { - theDelegate.DynamicInvoke(this.parameters); - } - - public FunctionCallTrampoline(Delegate theDelegate, object[] parameters) - { - this.theDelegate = theDelegate; - this.parameters = parameters; - } - } - - public void QueueFunctionCall(Delegate theDelegate, params object[] parameters) - { - FunctionCallTrampoline fct = new FunctionCallTrampoline(theDelegate, parameters); - QueueUserWorkItem(fct.WaitCallback, null); - } - */ - - public void QueueUserWorkItem(WaitCallback callback) - { - QueueUserWorkItem(callback, null); - } - - public void QueueUserWorkItem(WaitCallback callback, object state) - { - IDisposable token = counter.AcquireToken(); - ThreadWrapperContext twc = new ThreadWrapperContext(callback, state, token, this.exceptions); - - if (this.useFXTheadPool) - { - System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(twc.ThreadWrapper), twc); - } - else - { - Thread thread = new Thread(new ThreadStart(twc.ThreadWrapper)); - thread.IsBackground = true; - thread.Start(); - } - } - - public bool IsDrained() - { - bool result = counter.IsEmpty(); - - if (result) - { - Drain(); - } - - return result; - } - - public void Drain() - { - counter.WaitForEmpty(); - DrainExceptions(); - } - - private sealed class ThreadWrapperContext - { - private WaitCallback callback; - private object context; - private IDisposable counterToken; - private ArrayList exceptionsBucket; - - public ThreadWrapperContext(WaitCallback callback, object context, - IDisposable counterToken, ArrayList exceptionsBucket) - { - this.callback = callback; - this.context = context; - this.counterToken = counterToken; - this.exceptionsBucket = exceptionsBucket; - } - - public void ThreadWrapper() - { - using (IDisposable token = this.counterToken) - { - try - { - this.callback(this.context); - } - - catch (Exception ex) - { - this.exceptionsBucket.Add(ex); - } - } - } - - public void ThreadWrapper(object state) - { - ThreadWrapper(); - } - } - } -}