Paint.NET on the Windows Store

Okay so the cat’s already out of the bag. A number of tech media outlets picked up on an innocent little comment of mine on the forum :

It’s at the top of my list. I’m going to release a 4.0.17 update and then focus just on pushing to Windows Store.

So there you have it Smile You can all stop e-mailing me asking about this all the time. I’m traveling for work right now but when I’m back, my plan is: 1) release 4.0.17 which has some important fixes for performance and  high-DPI, and then 2) focus exclusively on bringing 4.0.17 to the Windows Store. I’d love to give a date but I’ve always gotten them wrong. There may also be a 1.5) or a 3) in there because my code signing certificate is expiring soon and obviously I need to renew it. Hopefully that won’t be too onerous.

4.0.17 will also bring “native” portable-ness to the app, by way of a .exe.config setting to redirect the app’s settings into a local JSON file (instead of going to the registry). Should be a neat feature for some people.

Some people have asked, “but how will you make money with the Store version?” (I’m still planning on a price of “free”) Answer: not sure. But if I wait to figure out both of these things (money + Store logistics), I’ll procrastinate forever and neither will ever happen. It’s just something I’ve learned about my own psychology: don’t let X delay Y, just do Y and X will sort itself out later.

ConcurrentDictionary allocates … a lot

I was inspecting the latest build of Paint.NET with SciTech Memory Profiler  and noticed that there were a lot of System.Object allocations. Thousands of them … then, tens of thousands of them … and when I had opened 100 images, each of which were 3440×1440 pixels, I had over 800,000 System.Objects on the heap. That’s ridiculous! Not only do those use up a ton of memory, but they can really slow down the garbage collector. (Yes, they’ll survive to gen2 and live a nice quiet retired life, for the most part … but they also have to first survive a gen0 and then a gen1 collection.)

Obviously my question was, where are these coming from?! After poking around in the object graph for a bit, and then digging in with Reflector, it eventually became clear: every ConcurrentDictionary was allocating an Object[] array of size 128, and immediately populating it with brand new Object()s (it was not lazily populated). And Paint.NET uses a lot of ConcurrentDictionarys!

Each of these Objects serves as a locking object to help ensure correctness and good performance for when there are lots of writes to the dictionary. The reason it allocates 128 of these is based on its default policy for concurrencyLevel: 4 x ProcessorCount. My system is a 16-core Dual Xeon E5-2687W with HyperThreading, which means ProcessorCount = 32.

There’s no way this level of concurrency is needed, so I quickly refactored my code to use a utility method for creating ConcurrentDictionary instead of the constructor. Most places in the code only need a low concurrency level, like 2-4. Some places did warrant a higher concurrency level, but I still maxed it out at 1x ProcessorCount.

Once this was done, I recreated the slightly contrived experiment of loading up 100 x 3440×1440 images, and the System.Object count was down to about ~20,000. Not bad!

—

This may seem like a niche scenario. “Bah! Who buys a Dual Xeon? Most people just have a dual or quad core CPU!” That’s true today. But this will become more important as Intel’s Skylake-X and AMD’s Threadripper bring 16-core CPUs much closer to the mainstream. AMD is already doing a fantastic job with their mainstream 8-core Ryzen chips (which run Paint.NET really fantastically well, by the way!), and Intel has the 6-core Coffee Lake headed to mainstream systems later this year. Core counts are going up, which means ConcurrentDictionary’s memory usage is also going up.

So, if you’re writing a Windows app with the stock .NET Framework and you’re using ConcurrentDictionary a lot, I’d advise you to be careful with it. It’s not as lightweight as you think.

(The good news is that Stephen Toub updated this in the open source .NET Core 2.0 so that only 1x ProcessorCount is employed. Here’s the commit. This doesn’t seem to have made it into the latest .NET Framework 4.7, unfortunately.)

paint.net 4.0.16 is now available

This is a hotfix for a crash that results from copying images from some web browsers. Unfortunately this seems to be the result of a bug in either the .NET Framework or Windows. I’ll try to follow up with folks at Microsoft when I have some time to do so.

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

Change log:

  • Fixed a crash or hang that sometimes happens when copying images from Internet Explorer, Edge, or Firefox, and then using File->New or Edit->Paste in paint.net.

    paint.net 4.0.15 is now available

    This is a hotfix that fixes a crash in the Magic Wand tool.

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

    The only change since 4.0.14, which you can read about here, is this:

    • Fixed a crash in the Magic Wand tool if it was used twice in a row with a selection mode other than Replace.

    Technically, it’s not "twice in a row" that causes the crash. It’s actually just using the Magic Wand tool, with a selection mode other than Replace, on any selection that is "pixelated". There are two common sources of pixelated selections: the Magic Wand tool, and the Rectangle Select tool. "Pixelated" selections are comprised solely of straight horizontal or vertical segments whose endpoints are at integer coordinates. This distinction is important because there are some great optimizations you can take advantage of when the data fits into this limitation. Sadly, one of my optimizations fell over in 4.0.14.

    *phew*! Enjoy 🙂

    paint.net 4.0.14 is now available

    This is a minor update that slightly improves the brush tools, and fixes a few weird issues that some people were seeing.

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

    Changes:

    • Improved the performance of the Brush tools when antialiasing is enabled
    • Improved the quality of the Brush tools when antialiasing is disabled
    • Fixed: Edit->Paste wasn’t working with some images that came from Firefox (as discussed at https://forums.getpaint.net/index.php?/topic/111012-not-enough-memory-to-paste-from-clipboard/ )
    • Fixed: Improved the reliability of Image->Crop to Selection on 32-bit systems. Instead of crashing when it runs out of memory, it will just show an error.
    • Fixed: Reduced crashes when loading UI images caused by an unreliable Windows component ("System.ArgumentException: Parameter is not valid")
    • Fixed: When manually checking for updates in the Settings dialog and the user didn’t actually have the necessary security privileges, a crash might result instead of an error dialog.

    Enjoy! Smile

    paint.net 4.0.13 is now available

    This is a minor update that fixes a few high priority problems that many people were bumping into.

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

    Changes:

    • Fixed "Missing api-ms-win-core-timezone-l1-1-0.dll" error that was being seen on some Windows 7 systems due to partial install or uninstall of Microsoft’s Universal C Runtime
    • Fixed the layout for the File->New, Image->Resize, and Image->Canvas Size dialogs in all known situations (various languages, DPI sizes, font configurations)
    • Fixed: Old versions of PSD plugin couldn’t load due to removal of PrivateThreadPool
    • Fixed a crash when typing a negative number for a zoom level

    Enjoy!

    paint.net 4.0.12 is now available

    This is a hotfix for some crashing on single CPU systems, and a rendering bug in the Rounded Rectangle shape.

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

    Change log:

    • Fixed: Rounded Rectangle rendering was incorrect for some values of "Corner size"
    • Fixed: Effects and Adjustments were crashing the app on systems with only 1 CPU core, or virtual machines configured for only 1 CPU

    And, for convenience, here is the change log for 4.0.11:

    Enjoy!

    paint.net 4.0.11 is now available

    This update adds the ability to configure the Rounded Rectangle shape’s corner size. It also fixes issues with scrolling and panning, effect rendering performance, high DPI, and a rare system hang caused by WPF.

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

    Change log:

    Enjoy!

    paint.net 4.0.10 is now available

    This update introduces overscroll, which is the ability to scroll the canvas past the bounds of the image. This has been a highly requested feature for several years now, and it’s finally here 🙂

    Also included is a big serving of miscellaneous bug fixes and small improvements.

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

    Change log:

    • New: The canvas may now be scrolled past the edge of the image
    • New: When holding the spacebar, you may scroll using the keyboard arrow keys (in addition to click-and-drag panning). Holding Ctrl will scroll at 10x the rate.
    • New: You can now hold Ctrl+Shift while clicking on the floating window icons in order to reset their location and docking. Ctrl+Shift along with the appropriate hotkey (F5, F6, F7, F8) also does the trick.
    • Improved: The rate of auto-scrolling, which triggers when the mouse is at the canvas edge and a button is being held down, has been improved and is based on time instead of frames
    • Improved: Fixed Size selection drawing now defaults to 400×300 pixels instead of 4×3 inches
    • Improved: Tooltips for Custom Shapes now include their file system location
    • Improved: Edit->Invert Selection should perform much faster when used with unmodified selections from the Magic Wand tool
    • Improved: When running within VMWare, "Hardware accelerated rendering" is disabled by default (Microsoft’s WARP is faster than VMWare’s D3D emulation)
    • Fixed: Erratic brush/pencil drawing when running within VMWare (this is caused by a bug in Win32’s GetMouseMovePointEx API, and is not actually limited to VMWare)
    • Fixed: Color Picker tool could crash if used at the bottom of the image, as reported at http://forums.getpaint.net/index.php?/topic/109248-paintnet-crashes-when-color-picker-off-screen-update/
    • Fixed: There was a glitch in the Gradient tool’s rendering when dx was equal to dy, as reported at http://forums.getpaint.net/index.php?/topic/107380-gradient-bugs-in-direction-and-length/
    • Fixed: The Layers window would sometimes auto-scroll the active layer to the bottom when performing certain operations, as reported at http://forums.getpaint.net/index.php?/topic/32527-layers-auto-scrolling-when-adding-or-duplicating/
    • Fixed some mouse cursor glitches when using panning using the spacebar
    • Fixed many glitches and a few crashes in the image size dialogs (File->New, Image->Resize, Image->Canvas Size)
    • Fixed a problem with inconsinstently skipped input when using the arrow keys to move tool handles, as reported at http://forums.getpaint.net/index.php?/topic/108932-selected-area-moving-with-direction-keys-bug/
    • Fixed: Custom Shapes did not always have the correct bounding box, and would have weird behavior as a result
    • Fixed: Line/Curve handles were unusable for up to 500ms after adjusting properties in the toolbar
    • Fixed some crashes and soft hangs in the Save Configuration dialog
    • Fixed a blemish at the top of the main window when Windows is set to 200% DPI
    • Fixed: There were some precision bugs with IndirectUI sliders that would cause them to get "stuck" at certain values (mostly affects effect plugins)
    • Improved: IndirectUI’s Angle Chooser control now supports the DecimalPlaces property (for plugins)
    • Improved: IndirectUI’s Slider controls now support all-negative ranges (e.g. [-200, -20])

    Enjoy!