• Content count

  • Joined

  • Last visited

Community Reputation


About Tim!

  1. Hi verdy_p, You can reset the flag in OnSetRenderInfo()
  2. >the dotNet compiler already optimizes integer divisions or multiplications by constant powers of 2 using binary shifts. @verdy_p Thanks for enlightening me. I didn't know that. (The principle is still sound though.) What we need is higher resolutions VDUs, say 600dpi, then we wouldn't need antialiasing at all.
  3. @verdy_p: When multiplying or dividing by 2^n you can use shift left or shift right. The following code snippet: for (y2 *= 2, x2 = (n = e = x2) * 2; n > 0; --n) { becomes: for (y2 <<= 1, x2 = (n = e = x2) << 1; n > 0; --n) { It shaves off more than a few CPU cycles and doesn't use the FPU.
  4. I've tried the following plug-in experiment. It gives inconsistent results, but Loop 2 is usually a bit faster than Loop 1 (the JIT compiler has done its job well). The Windows Forms experiment gave a consistent and marked speed difference. So why the difference? Hidden Content: using System.Diagnostics; using System.Drawing; using System.Windows.Forms; using PaintDotNet; using PaintDotNet.Effects; namespace EffectsPluginTemplate1 { public class EffectPlugin : PaintDotNet.Effects.Effect { public static string StaticName { get { return "Effect Plugin"; } } public static Bitmap StaticIcon { get { return new Bitmap(typeof(EffectPlugin), "EffectPluginIcon.png"); } } public static string StaticSubMenuName { get { //return null; // Use for no submenu #if DEBUG return "_test"; // Use for custom submenu #else return SubmenuNames.Render; #endif } } public EffectPlugin() : base(EffectPlugin.StaticName, EffectPlugin.StaticIcon, EffectPlugin.StaticSubMenuName, EffectFlags.Configurable | EffectFlags.SingleThreaded) { show_msgbox = false; } ~EffectPlugin() { if (show_msgbox) { string str = ""; str += "Loop 1: " + loop1_ticks + "\r\n"; str += "Loop 2: " + loop2_ticks + "\r\n"; str += "Loop 1 / Loop 2: " + loop1_ticks / loop2_ticks + "\r\n"; str += "Ticks per second: " + Stopwatch.Frequency; MessageBox.Show(str); } } public override EffectConfigDialog CreateConfigDialog() { return new EffectPluginConfigDialog(); } protected override void OnSetRenderInfo(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs) { loop1_ticks = 0; loop2_ticks = 0; show_msgbox = true; base.OnSetRenderInfo(parameters, dstArgs, srcArgs); } private Stopwatch sw = new Stopwatch(); private float loop1_ticks; private float loop2_ticks; private bool show_msgbox; public override void Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, int startIndex, int length) { PdnRegion selectionRegion = EnvironmentParameters.GetSelection(srcArgs.Bounds); sw.Reset(); sw.Start(); // // Loop 1 // for (int i = startIndex; i < startIndex + length; ++i) { Rectangle rect = rois[i]; for (int y = rect.Top; y < rect.Bottom; ++y) { for (int x = rect.Left; x < rect.Right; ++x) { // Render Code Here dstArgs.Surface[x, y] = srcArgs.Surface[rect.Right - x - 1, y]; } } } sw.Stop(); loop1_ticks += sw.ElapsedTicks; sw.Reset(); sw.Start(); // // Loop 2 // int r = startIndex + length; for (int i = startIndex; i < r; i++) { Rectangle rect = rois[i]; int left = rect.Left; int right = rect.Right; int top = rect.Top; int bottom = rect.Bottom; for (int y = top; y < bottom; y++) { for (int x = left; x < right; x++) { dstArgs.Surface[x, y] = srcArgs.Surface[right - x - 1, y]; } } } sw.Stop(); loop2_ticks += sw.ElapsedTicks; } } }
  5. I'm running it by double-clicking on the exe in Windows Explorer.
  6. And when I switch off overflow/underflow checks, Loop 2 runs about six times faster than Loop 1.
  7. I checked optimise code in project properties (Release). Loop 2 runs 3.64 times faster than Loop 1. Here's my ILDASM where Loop 1 uses call instance and Loop 2 uses ldloc.s: Hidden Content: .method private hidebysig instance void buttonLoops_Click(object sender, class [mscorlib]System.EventArgs e) cil managed { .maxstack 5 .locals init ( [0] class [system]System.Diagnostics.Stopwatch sw, [1] valuetype [system.Drawing]System.Drawing.Rectangle rect, [2] string str, [3] int32 y, [4] int32 x, [5] int32 left, [6] int32 right, [7] int32 top, [8] int32 bottom, [9] int32 V_9, [10] int32 V_10, [11] object CS$0$0000, [12] object[] CS$0$0001, [13] object CS$0$0002, [14] object[] CS$0$0003) L_0000: call class [system.Windows.Forms]System.Windows.Forms.Cursor [system.Windows.Forms]System.Windows.Forms.Cursors::get_WaitCursor() L_0005: call void [system.Windows.Forms]System.Windows.Forms.Cursor::set_Current(class [system.Windows.Forms]System.Windows.Forms.Cursor) L_000a: newobj instance void [system]System.Diagnostics.Stopwatch::.ctor() L_000f: stloc.0 L_0010: ldloca.s rect L_0012: ldc.i4.0 L_0013: ldc.i4.0 L_0014: ldc.i4 0x2710 L_0019: ldc.i4 0x2710 L_001e: call instance void [system.Drawing]System.Drawing.Rectangle::.ctor(int32, int32, int32, int32) L_0023: ldstr "" L_0028: stloc.2 L_0029: ldloc.0 L_002a: callvirt instance void [system]System.Diagnostics.Stopwatch::Start() L_002f: ldloca.s rect L_0031: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Top() L_0036: stloc.3 L_0037: br.s L_0059 L_0039: ldloca.s rect L_003b: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Left() L_0040: stloc.s x L_0042: br.s L_004a L_0044: ldloc.s x L_0046: ldc.i4.1 L_0047: add.ovf L_0048: stloc.s x L_004a: ldloc.s x L_004c: ldloca.s rect L_004e: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Right() L_0053: blt.s L_0044 L_0055: ldloc.3 L_0056: ldc.i4.1 L_0057: add.ovf L_0058: stloc.3 L_0059: ldloc.3 L_005a: ldloca.s rect L_005c: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Bottom() L_0061: blt.s L_0039 L_0063: ldloc.0 L_0064: callvirt instance void [system]System.Diagnostics.Stopwatch::Stop() L_0069: ldloc.2 L_006a: stloc.s CS$0$0000 L_006c: ldc.i4.4 L_006d: newarr object L_0072: stloc.s CS$0$0001 L_0074: ldloc.s CS$0$0001 L_0076: ldc.i4.0 L_0077: ldloc.s CS$0$0000 L_0079: stelem.ref L_007a: ldloc.s CS$0$0001 L_007c: ldc.i4.1 L_007d: ldstr "Loop 1: " L_0082: stelem.ref L_0083: ldloc.s CS$0$0001 L_0085: ldc.i4.2 L_0086: ldloc.0 L_0087: callvirt instance int64 [system]System.Diagnostics.Stopwatch::get_ElapsedTicks() L_008c: box int64 L_0091: stelem.ref L_0092: ldloc.s CS$0$0001 L_0094: ldc.i4.3 L_0095: ldstr "\r\n" L_009a: stelem.ref L_009b: ldloc.s CS$0$0001 L_009d: call string [mscorlib]System.String::Concat(object[]) L_00a2: stloc.2 L_00a3: ldloc.0 L_00a4: callvirt instance void [system]System.Diagnostics.Stopwatch::Reset() L_00a9: ldloc.0 L_00aa: callvirt instance void [system]System.Diagnostics.Stopwatch::Start() L_00af: ldloca.s rect L_00b1: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Left() L_00b6: stloc.s left L_00b8: ldloca.s rect L_00ba: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Right() L_00bf: stloc.s right L_00c1: ldloca.s rect L_00c3: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Top() L_00c8: stloc.s top L_00ca: ldloca.s rect L_00cc: call instance int32 [system.Drawing]System.Drawing.Rectangle::get_Bottom() L_00d1: stloc.s bottom L_00d3: ldloc.s top L_00d5: stloc.s V_9 L_00d7: br.s L_00f1 L_00d9: ldloc.s left L_00db: stloc.s V_10 L_00dd: br.s L_00e5 L_00df: ldloc.s V_10 L_00e1: ldc.i4.1 L_00e2: add.ovf L_00e3: stloc.s V_10 L_00e5: ldloc.s V_10 L_00e7: ldloc.s right L_00e9: blt.s L_00df L_00eb: ldloc.s V_9 L_00ed: ldc.i4.1 L_00ee: add.ovf L_00ef: stloc.s V_9 L_00f1: ldloc.s V_9 L_00f3: ldloc.s bottom L_00f5: blt.s L_00d9 L_00f7: ldloc.0 L_00f8: callvirt instance void [system]System.Diagnostics.Stopwatch::Stop() L_00fd: ldloc.2 L_00fe: stloc.s CS$0$0002 L_0100: ldc.i4.4 L_0101: newarr object L_0106: stloc.s CS$0$0003 L_0108: ldloc.s CS$0$0003 L_010a: ldc.i4.0 L_010b: ldloc.s CS$0$0002 L_010d: stelem.ref L_010e: ldloc.s CS$0$0003 L_0110: ldc.i4.1 L_0111: ldstr "Loop 2: " L_0116: stelem.ref L_0117: ldloc.s CS$0$0003 L_0119: ldc.i4.2 L_011a: ldloc.0 L_011b: callvirt instance int64 [system]System.Diagnostics.Stopwatch::get_ElapsedTicks() L_0120: box int64 L_0125: stelem.ref L_0126: ldloc.s CS$0$0003 L_0128: ldc.i4.3 L_0129: ldstr "\r\n" L_012e: stelem.ref L_012f: ldloc.s CS$0$0003 L_0131: call string [mscorlib]System.String::Concat(object[]) L_0136: stloc.2 L_0137: ldloc.2 L_0138: ldstr "Ticks per second: " L_013d: ldsfld int64 [system]System.Diagnostics.Stopwatch::Frequency L_0142: box int64 L_0147: call string [mscorlib]System.String::Concat(object, object, object) L_014c: stloc.2 L_014d: ldloc.2 L_014e: call valuetype [system.Windows.Forms]System.Windows.Forms.DialogResult [system.Windows.Forms]System.Windows.Forms.MessageBox::Show(string) L_0153: pop L_0154: call class [system.Windows.Forms]System.Windows.Forms.Cursor [system.Windows.Forms]System.Windows.Forms.Cursors::get_Default() L_0159: call void [system.Windows.Forms]System.Windows.Forms.Cursor::set_Current(class [system.Windows.Forms]System.Windows.Forms.Cursor) L_015e: ret }
  8. A common rendering inefficiency amongst many code samples is caused by the repetitive accessing of properties of the Rectangle class in for-loops in the Render method. Consider the following code sample. The second style is faster because it uses local variables to access the rectangle data. The first style makes repeated subroutine calls to access the rectangle data: Hidden Content: using System.Diagnostics; using System.Drawing; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void buttonLoops_Click(object sender, System.EventArgs e) { Cursor.Current = Cursors.WaitCursor; Stopwatch sw = new Stopwatch(); Rectangle rect = new Rectangle(0, 0, 10000, 10000); string str = ""; sw.Start(); for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { int result = 0; } } sw.Stop(); str += "Loop 1: " + sw.ElapsedTicks + "\r\n"; sw.Reset(); sw.Start(); int right = rect.Right; int left = rect.Left; int top = rect.Top; int bottom = rect.Bottom; for (int y = top; y < bottom; y++) { for (int x = left; x < right; x++) { int result = 0; } } sw.Stop(); str += "Loop 2: " + sw.ElapsedTicks + "\r\n"; str += "Ticks per second: " + Stopwatch.Frequency; MessageBox.Show(str); Cursor.Current = Cursors.Default; } } } Another common problem is unecessary recalculation of for-loop limits every time the loop is executed. Limits should be calculated before the for-loops. In Example 2 below, for an image of 800x600px, it saves 480598 computations (600 + 800 x 600 - 2) in comparison to Example 1: Hidden Content: // // Example 1 // int a = ComputeA(); int b = ComputeB(); int left = rect.Left; int right = rect.Right; int top = rect.Top; int bottom = rect.Bottom; for (int y = top; y < bottom - a; y++) { for (int x = left; x < right - b; x++) { int result = 0; } } // // Example 2 // int a = ComputeA(); int b = ComputeB(); int left = rect.Left; int right = rect.Right - b; int top = rect.Top; int bottom = rect.Bottom - a; for (int y = top; y < bottom; y++) { for (int x = left; x < right; x++) { int result = 0; } }
  9. I've just had another look at that problem. I should have put a lock statement in OnSetRenderInfo(). Thanks for highlighting it.
  10. Yes. Tile height and width of the tessarae can be changed independently using sliders ranging from 2px to 100px. I'll update my original post with a better description. No, but I could add this capability to a future release. Currently the tesserae are drawn on opaque background (it's the Secondary colour with Alpha set to fully opaque 255).
  11. Thanks for your suggestions. I've now published it here (and here) Rounded Corners: Hmmm. That involves calculating eight tangents and four x-y offsets and drawing an arc about each of the four x-y offsets between pairs of tangents. Easy enough to say, but tricky to implement. I'll have a think... EDIT 1: Updated links for new forum.
  12. Hi All, Here's my Rounded Rectangle effect. (A full list of my effects is here.) Rounded Rectangle - Version - 27 February 2017 ┬ęCopyright Tim Mathias 2017. All rights reserved. USE AT YOUR OWN RISK. Menu: Effects -> Render -> Rounded Rectangle... LavEnt.Effects.RoundedRectangle. Example 1 Example 2 The Rounded Rectanlge effect uses the Primary and Secondary colours and the current brush width to draw a rectangle with rounded corners into the current selection. The roundness of the corners, the border outline and fill type are user-selectable. You can have multiple selections (Ctrl key). If there's no selection, it uses the entire canvas. The selection can be any shape (rectangle, lasso, ellipse or magic wand). After drawing the first shape, you can change the colours and brush width for subsequent shapes without having to re-open the config dialog. Just change them in Paint.NET's toolbars and then press Ctrl+F to paint into the selection with the new colours and brush width. Note: If you choose corner radii whose sum is greater than the side length, the radii will be scaled to fit the side length. The scaling is indicated by a green bar. Version - 27 February 2017: Recompiled for .NET 4.6.1. Version - 14 May 2009: Added a lock statement to prevent multiple simultaneous renders. Removed cross-threaded calls. Removed UI bug where independent radii were overwritten when typing a new top-left x radius. Version - 30 Mar 2009 (789 downloads): Added a supplementary brush width slider to the config dialog. Note: It reverts to the brush width set in the Paint.NET toolbar every time the dialog opens and when re-applying the effect using Ctrl+F. Added a fill overlap slider. It controls by how much the fill colour overlaps the outline colour. It's useful for removing gaps that sometimes appear due to antialiasing. The overlap ranges from zero to half the brush width. To see it in action use semi-transparent colours for the outline and fill, then move the Fill Overlap slider. Added a Reset Radii button. Added a Show Original checkbox. Improved slider performance. Improved radii UI logic. Version - 16 Mar 2009 (266 downloads): Added support for the number keypad. Improved outline-fill algorithm so that semi-transparent colours don't overlap. Removed UI glitch when moving radii sliders. Version (168 downloads): Original.
  13. Hi All, Here's my Mosaic effect. (A full list of my effects is here.) Mosaic - Version - 27 February 2017 ┬ęCopyright Tim Mathias 2017. All rights reserved. USE AT YOUR OWN RISK. Menu: Effects -> Artistic -> Mosaic... LavEnt.Effects.Mosaic. Before After The Mosaic effect converts a drawing into a mosaic pattern and uses the Secondary colour as the adhesive or grout between the tesserae. Tessera Width: 2px to 100px Tessera Height: 2px to 100px Tessera Colour: Centre spot sample or average. Tessera Shape Irregularity: 0% (square) to 50% of tessera side length. Adhesive Thickness: 0px to 50px. Antialias: On or off. Possible enhancements I might add in future: Mathematical formulae to generate the mosaic pattern; at the moment it just uses a random irregularity based on a square shape. Fully transparent Secondary colour so that the underlying image shows through the gaps between tessarae. Tip: Try using a dark semi-transparent Secondary colour for the adhesive. Version - 27 February 2017: Recompiled for .NET 4.6.1. Version - 14 May 2009: Added a lock statement to prevent multiple simultaneous renders. Version - 30 March 2009 (813 downloads): Original.
  14. I value your comments but I don't know mean by that one. Mosaics are made of (usually) square tiles. One of the intrinsic properties of a mosaic is that it comprises versatile tesselating tesserae. ;-) Before After EDIT 1: Updated links for new forum.
  15. I've re-done the UI logic. All those multiple inter-dependencies is quite tricky. I've also added a few other things (see my original post).