There were still a few nasty bugs that were found in 4.0.8, so here’s an update that squashes them. The selection and Line/Curve tools should now be trouble-free for everyone!

As usual, you can download the update directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

Here are the fixes:

  • Fixed: Selection tools would sometimes crash
  • Fixed: Line/Curve tool would draw nothing if the Shapes tool was configured for “Draw Filled Shape”
  • Fixed: Thumbnails for .PDN files were not working on some systems
  • Fixed: The “Finish” button in the toolbar would not update its enable/disable state when switching between images

Enjoy!

Unfortunately there was a nasty crashing bug that snuck into the Line/Curve tool in the 4.0.7 release earlier today. So, here’s a 4.0.8 update with the fix.

As usual, you can download the update directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

Here’s the official, and very brief, changelog for 4.0.8:

  • Fixed: The Line/Curve tool was crashing when drawing vertical lines of particular lengths.

As for 4.0.7, it has its own blog post, but here is its changelog for convenience:

  • Changed: .NET Framework 4.6 is now required, and will be installed if necessary
  • New: Swedish translation
  • Fixed: Title bar now uses the Windows 10 accent color
  • Fixed: Floating windows were not remembering their locations on some non-English systems
  • Improved: Performance of "committing" (finishing) is now up to 10x faster, which greatly improves responsivness for quick drawing operations
  • Improved Shapes tool quality when drawn without antialiasing, with line styles, and at 1 pixel brush width
  • Improved Shapes tool rendering performance on CPUs with many cores (8+)
  • Improved: Installing a "complex" custom Shape no longer causes very long hangs at app startup
  • Improved: Image->Resize is now much faster
  • Fixed: Image->Resize would sometimes cause the main window to flicker or lose focus
  • Fixed: Image->Resize would sometimes do nothing instead of resizing the image to a very large size
  • Fixed: Labels on the vertical ruler were misleading because they were on the wrong side of the tick mark
  • Improved the ruler’s performance
  • Fixed: Edit->Copy now works if the data copied to the clipboard is larger than 2GB
  • Fixed: Page Up, Page Down, Home, and End keys now work in the main canvas area
  • Fixed: Paint.NET will no longer incorrectly block Windows from restarting
  • Fixed: Rotate/Zoom no longer crashes on very large images
  • Improved: Reduced CPU/GPU and battery use when the app is not in the foreground
  • Improved: Reduced CPU and battery use of the UI for the Move and Shapes tools
  • Added: Custom Shapes XAML now supports cardinal splines via PolyCurveSegment (a new PathSegment type)
  • Improved: Various performance improvements
  • Fixed: Several rare or uncommon crashes

Enjoy!

Whew! I originally started work on getting Paint.NET’s title bar area to respect the Windows 10 TH2 “accent color” functionality (which is NOT easy for a classic (non-universal / desktop) app … how about some documentation, Microsoft?!). Along the way I found (and rediscovered) many important performance issues that I’ve now fixed. I also finally found some fixes for a handful of rare and vexing startup crashes, including one that’s caused by a silly bug when WPF’s data binding system initializes (it’s a bug in WPF). Oh, and there’s now a Swedish translation. Various things related to the Shapes tool have been improved as well.

As usual, you can download the update directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

Hare are all the the new fixes and changes:

  • Changed: .NET Framework 4.6 is now required, and will be installed if necessary
  • New: Swedish translation
  • Fixed: Title bar now uses the Windows 10 accent color
  • Fixed: Floating windows were not remembering their locations on some non-English systems
  • Improved: Performance of "committing" (finishing) is now up to 10x faster, which greatly improves responsivness for quick drawing operations
  • Improved Shapes tool quality when drawn without antialiasing, with line styles, and at 1 pixel brush width
  • Improved Shapes tool rendering performance on CPUs with many cores (8+)
  • Improved: Installing a "complex" custom Shape no longer causes very long hangs at app startup
  • Improved: Image->Resize is now much faster
  • Fixed: Image->Resize would sometimes cause the main window to flicker or lose focus
  • Fixed: Image->Resize would sometimes do nothing instead of resizing the image to a very large size
  • Fixed: Labels on the vertical ruler were misleading because they were on the wrong side of the tick mark
  • Improved the ruler’s performance
  • Fixed: Edit->Copy now works if the data copied to the clipboard is larger than 2GB
  • Fixed: Page Up, Page Down, Home, and End keys now work in the main canvas area
  • Fixed: Paint.NET will no longer incorrectly block Windows from restarting
  • Fixed: Rotate/Zoom no longer crashes on very large images
  • Improved: Reduced CPU/GPU and battery use when the app is not in the foreground
  • Improved: Reduced CPU and battery use of the UI for the Move and Shapes tools
  • Added: Custom Shapes XAML now supports cardinal splines via PolyCurveSegment (a new PathSegment type)
  • Improved: Various performance improvements
  • Fixed: Several rare or uncommon crashes

Enjoy!

This update is probably a little more interesting than you may have been expecting. It has the usual grab bag of small bug fixes, but also includes the ability to install custom shapes (for the Shapes tool, obviously Smile). I’ve also updated everything to look good in Windows 10, and fixed many high-DPI blemishes that had gone unfixed for awhile.

If you’d like more info about custom shapes, you can check out BoltBait’s guide, “How to Install Paint.NET Shapes", which has installation instructions and a pack of sample shapes. It’s all XAML based and will look very familiar to you if you’ve ever worked with WPF’s Geometry system.

As usual, you can download the update directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

  • New: You can now create and install custom shapes for the Shapes tool.
  • New: Updated to work better with Windows 10.
  • New: Increased the maximum brush size to 2000.
  • New: IndirectUI-based effect plugins can now provide help text, accessible via the question mark button.
  • New: Effect plugins may now access the current palette via IPalettesService.
  • Improved: Reduced memory usage of brush tools when using large brush sizes.
  • Fixed: Holding shift to constrain the angle of the roll control (Layers -> Rotate/Zoom) wasn’t working correctly.
  • Fixed: Various shapes (hexagon, pentagon, triangle, etc.) are now symmetric when holding the shift key.
  • Fixed: Multiple high-dpi blemishes in the main window and many dialog boxes (Settings, Save Configuration, all effect dialogs, etc.)
  • Fixed: Various minor/rare crashes.

Enjoy!

(Side note: expect a Paint.NET 4.0.6 update soon, hopefully timed with the availability of Windows 10. It’s not just a bugfix release, either. More info soon!)

If you have some code whose performance is limited by string building or concatenation, you may want to read this. For everyone else: close your eyes! Here be dragons! (just kidding, but seriously, be careful with this stuff)

Everybody knows that strings are immutable in .NET. When I first tried out C# (in 2003?) this annoyed me, as I was used to C and C++ and being able to do whatever I wanted. I quickly learned to love this property, and embraced immutability and functional programming style in general. The benefits far outweigh the initial clumsiness.

Everybody also knows that you can pin a string with unsafe and fixed, get a pointer to the first character, and read directly from it or even hand it off the native code. No copying required. Right? Right?!

And following from that, there’s a dirty little secret here that nobody really likes to talk about: .NET strings aren’t actually immutable. You can get a pointer to one, and you can write to that pointer because it’s allocated in the regular heap and not in some special read-only memory pages (e.g. via VirtualProtect). Strings in .NET don’t cache their hash code, therefore there’s nothing to cause an inconsistency if you do something like …

String s = “I’m immutable, right?”;
unsafe
{
    fixed (char* p = s)
    {
        *(p + 4) = ‘_‘;
        *(p + 5) = ‘_‘;        
    }
}

Console.WriteLine(s); // outputs “I’m __mutable, right?”;

Note that if you do this anywhere other than while initializing a string, be prepared for the local townsfolk to come knocking on your door. With torches and pitchforks. It’s very important to respect the immutability contract once you hand off a String to external code.

If we browse around with Reflector or over on github in the coreclr repo, we can see that .NET itself takes advantage of this in functions such as System.String.Concat()  and its helper function FillStringChecked():

public static String Concat(String str0, String str1, String str2) {
    // … parameter checking and other stuff omitted for brevity …
    int totalLength = str0.Length + str1.Length + str2.Length;
    String result = FastAllocateString(totalLength);
    FillStringChecked(result, 0, str0);
    FillStringChecked(result, str0.Length, str1);
    FillStringChecked(result, str0.Length + str1.Length, str2);
    return result;
}

unsafe private static void FillStringChecked(String dest, int destPos, String src)
{
    // … parameter checking omitted for brevity
    fixed(char *pDest = &dest.m_firstChar)
    fixed (char *pSrc = &src.m_firstChar) {
        wstrcpy(pDest + destPos, pSrc, src.Length);
    }
}

And wstrcpy just calls into some regular ol’ memcpy/memmove type code. Some of those code paths (via wstrcpy) are managed code, others eventually thunk into native, but none of it’s magic: you can do all of this yourself! You can’t call FastAllocateString()*, but you can still allocate a string of a specified length via new String(char c, int count). Just use a placeholder character for the first parameter, like a space or even null ‘’. Also note that all .NET strings are null-terminated, so if you create one of length N, you actually have N+1 characters. The memory layout intentionally matches BSTR which gives a lot of flexibility for passing things to native code without having to do any copying. (I don’t know what would happen if you overwrote the length prefix, but feel free to try it out and post a comment!)

With this information you can build your own versions of String.Concat or StringBuilder that can avoid making copies in specialized situations. However, I’m not actually sure how useful this is: I looked through the Paint.NET codebase and found only a handful of places that do lots of string manipulation, and even then they were mostly in error reporting code paths whose performance was entirely unimportant. I decided to leave the code alone, preferring safety over a little bit of performance (and fun and creativity). Your Mileage May Vary.

On a side note, the fact that you can do this at all is one of the reasons why I love .NET and C#. You get all the good stuff that a managed runtime and high-level language is supposed to provide, but you also get “escape hatches” to drill through the glass floors on the abstraction ladder. You can push the runtime and its safety guarantees out of the way in the name of performance (or for other reasons). Paint.NET takes advantage of this a lot: graphics code loves things like pointers and structs. Lately I’ve been doing a lot of work on Android** with Java, and the lack of things like pointers and structs … gosh, it just makes things so much more difficult to work with from a performance standpoint!

* Well, maybe you could with reflection Winking smile That would probably eliminate its “fast” property though.

** Sorry, not for Paint.NET.

There were a few bugs that got introduced in 4.0.4, so here’s an update that fixes them. Chiefly among these bugs are a break in how the Magic Wand tool works, a crash when using Effects, and one when trying to use the Shapes tool.

As usual, you can download it directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

  • Fixed: Subtraction mode in the Magic Wand tool was generating B-A instead of A-B.
  • Fixed: Crash when using Effect –> Repeat.
  • Fixed: Crash (AccessViolationException) on some systems when initializing the canvas for hardware accelerated rendering. It will still crash but then switch to software rendering for the next startup, which will then avoid the crashing.
  • Fixed: Crash when clicking on the Shapes tool if you had run an old 4.0 beta/alpha and had also changed which shape is used at startup to be a line or curve.
  • Fixed: Crash when using Settings –> Update –> Check Now due to incorrectly determining that UAC is not enabled.

Enjoy!

As I discussed in an earlier blog post, this update greatly improves performance in some key areas like the Magic Wand and Move Selected Pixels tools. It also fixes some important bugs, and even gives back a feature that got lost in the move from 3.5 to 4.0. Good stuff all around!

As usual, you can download it directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

Here’s the change log:

  • New: Added a ‘Fill’ property to the Paintbrush tool (regression from 3.5).
  • Faster: Move Selected Pixels has been significantly optimized.
  • Faster: Magic Wand Tool has been significantly optimized.
  • Faster: Working with aliased selections ("aliased selection quality" in the toolbar) has been significantly optimized.
  • Faster: Edit->Copy and Edit->Copy Merged are now up to 2x faster.
  • Faster: Drawing when a selection is active; clipping performance is now improved.
  • Fixed: The Text tool no longer produces terrible looking text when using Smooth rendering mode without antialiasing.
  • Fixed: The Gear shape had a few glitches.
  • Fixed: Using a Fill pattern with the Paint Bucket would sometimes produces misaligned or "corrupt looking" results.
  • Fixed: The alignment of the menu buttons in the top-right was off by 2 pixels, causing them to look weird at high-DPI.
  • Fixed: The zoom buttons in the status bar would not recognize clicks unless the main window was already in focus.
  • Fixed: Some incorrect errors about requiring Windows 7 SP1 when launching the installer from something like an old version of WinZip.
  • Fixed: Edit->Clear Selection and Edit->Cut were filling with transparent black (#00000000) instead of transparent white (#00FFFFFF). This caused some discrepancies compared to older versions of Paint.NET (regression from 3.5).
  • Fixed: Pressing Ctrl+A when a drawing tool was active would sometimes incorrectly show a tinted selection.
  • Fixed: Text in the title bar was not visible when using a 3rd party Aero theme with black titlebars.

Enjoy!

I talked about 4.0.4 in a recent blog post, and now I’ve got a beta that you can try out. Head on over to the forum to check out the list of changes and, of course, to download it: http://forums.getpaint.net/index.php?/topic/30337-paintnet-404-beta-build-5442/

In order to save a bunch of time, which I’m short on for the next few weeks, I’m not releasing this via the built-in updater or the website. Later this week I can hopefully fast-track a non-beta update for everyone to enjoy.

Whew, it’s been awhile since there was an update to paint.net 4.0. It’s finally time to start talking about the next one!

I’ve been slowly chipping away at various little things in Paint.NET, especially with respect to performance. And also some missing features and bug fixes.

Here’s a preview of what’s coming in 4.0.4:

  • The ability to choose a “Fill” is coming back for the Paintbrush tool. I didn’t think many people would miss it and so I left it out of 4.0 in order to save time for other things. I was wrong on two fronts here: 1) people do need it, and 2) it only took a few minutes to implement. So, in 4.0.4, it’ll be back.
  • Dramatically faster Magic Wand tool. I came up with a new algorithm to do the reverse scan-conversion that is needed here. On small images you probably won’t notice, but if you’ve ever had the Magic Wand tool “take forever” then the next update is going to be your favorite one of all time. I’ve seen the Magic Wand go from taking 60+ seconds in 4.0.3 to taking maybe 2 or 3 seconds in 4.0.4.
  • The improvements to the Magic Wand tool’s reverse scan-conversion algorithm have also been applied to make aliased selections work a lot faster. (Actually I’ll tell the proper truth here: This work was originally intended as a challenge to myself to see if I could make the aliased selection rendering run a lot faster. It just so happened to be more relevant for the Magic Wand Smile)
  • Much faster performance overall. I’ve chipped away at performance in the areas of antialiased selections, selection clipping, and so on. Anything involving selections should be a lot faster. In particular, the Move Selected Pixels tool has a fantastically smoother framerate. Some of this optimization just involved a better choice of sorting algorithm (e.g. by using introspective sort instead of raw quicksort). I have also aggressively applied a coding pattern in C# which allows elision of virtual function calls (via inlining) by combining generics, structs, and interfaces. If anyone is interested in hearing the details, please let me know in the comments below, as I’m not sure this has been discussed much in the broader C#/.NET community. This pattern is responsible for the first 20% performance gain in my C# implementations of quicksort and introspective sort, so it’s nothing to sneeze at Smile Other performance improvements came about by being smarter about how selection mask generation was parallelized with other computations, by replacing integer divisions with multiply/add/shift combinations, and so on. Windows Performance Analyzer is your friend, folks! I’ve been using it extensively to optimize paint.net 4.0 (both now and in the past).
  • Other miscellaneous bug fixes, of course. The Gear shape had some glitches (oops), the Text tool produced foul output if you used its Smooth rendering mode while antialiasing was disabled, and Edit –> Clear wasn’t behaving the same as it was in v3.5 and was causing problems because of it.

“Reverse scan-conversion” is the inverse of polygon scan conversion (also known as rasterization). It’s the process of taking a list of non-overlapping rectangles with integer coordinates and extents (e.g. System.Windows.Int32Rect) and stitching them together to form a polygon (a list of points in 2D space). In Paint.NET this is used when you have chosen to use aliased selections: your selection’s polygonal outline is scan-converted (rasterized) to produce a list of integer rectangles which approximate its interior (this is a well-known algorithm in computer graphics). This list of rectangles (or “scans”) is then fed into the next algorithm which stitches the rectangles together to create a “pixelated” (or rectilinear) polygon outline. In 4.0.3 and before, each rectangle was first transformed into a closed polygon and then they were all run through GPC’s union algorithm. This ensured that any touching edges were coalesced. Unfortunately, GPC just does not like this scenario and performs excruciatingly slow, and sometimes even crashes via stack overflow.

As it turns out, this has a lot of overlap with what the Magic Wand tool does. It too needs to take a list of rectangles and transform it into a polygon, although the source is different. We start by applying ye ol’ flood fill algorithm, with tolerance thresholding, to the image in order to create a bitmask that tells us which pixels are included or not. This bitmask is then converted into a list of rectangles via a process akin to RLE compression. And then that list of rectangles must be converted into a polygon because that’s the way Paint.NET’s selection system works (it is geometry based: it needs polygons!). This is what takes up about 99% of the CPU time when you see the Magic Wand “taking forever,” and by reducing it from O(n^m) down to O(n), much time and frustration is saved. (I’m doing a little hand-waving here by not specifying what ‘n’ is. Also, I haven’t actually analyzed GPC’s code to figure out what m is, although it’s probably 2. Have you tried analyzing its code? It’s intense Smile)

Almost all of the CPU time is now spent drawing the selection outline with Direct2D. A future update might optimize that too since Direct2D is using a general purpose algorithm, something which is overkill when all I need is to apply a 1 pixel stroke to a polygon.

This update fixes a few small bugs and improves performance. There are still some performance issues when working with lots of layers, particularly when using Layers –> Import From File, but I plan on fixing those in the near future as well. Optimization work is never complete, and is quite fun and rewarding Smile !

As usual, you can download it directly from the website, or you can use the built-in updater via Settings –> Updates –> Check Now.

Changes since 4.0.2:

  • Further improved performance when working with images that have a lot of layers.
  • Fixed some flickering in the Layers form.
  • Fixed the Language selector in the Settings dialog.
  • Fixed 2-finger touchpad and touch screen scrolling.
  • Aero peek thumbnails now include the pixel grid if it’s enabled.
  • Fixed a crash in the thumbnail renderer that sometimes happened when switching between images.
  • Ctrl+W no longer exits the application if zero images are open.

Enjoy!

Follow

Get every new post delivered to your Inbox.

Join 315 other followers