Paint.NET v3.22 – Beta release now available

This release fixes a few minor bugs and adds a new, much-needed Reduce Noise effect. You can get it from the website ( or via the built-in updater by going to Help -> Check for Updates. (Make sure that “Also check for beta releases” is enabled, by clicking on the “Options” button after clicking the “Check for Updates” menu item). Final release is tentatively scheduled for mid-January.


  • New: “Reduce Noise” effect.
  • Changed: Ctrl+W will now close Paint.NET if zero images are open.
  • Fixed: In Windows XP, when launching web content, sometimes Internet Explorer was used instead of the user’s chosen default browser.
  • Fixed: Unfocus was not handling alpha values properly.
  • Fixed: Brightness / Contrast adjustment was only displaying its text in English.
  • Fixed: The /auto parameter for the installer now correctly allows for an automated installation. This was inadvertantly broken in 3.20.

Typo Bugs – OnScroll() versus OnShown()

I just recently fixed a bug in Paint.NET where I had inadvertently overridden the OnScroll() method instead of the OnShown() method. The code I added was meant to raise the priority of the UI thread while a dialog was initializing (in its constructor), and then drop the priority back to normal once the dialog was finally visible (in OnShown()). This was shown to improve the snappiness of Paint.NET’s UI because it does a lot of processing in background threads, which was then competing with dialog initialization and initial rendering. Since the reversion code was in OnScroll(), which never gets called, the reversion simply never happens. If I’d done a more careful code review, I probably would have caught this.

The reason it was in OnScroll() is because I typed “override OnS” and then expected Visual Studio to auto-complete the method signature. It did this correctly, but I had already overridden OnShown() and so the next match was OnScroll(). Luckily in this case there was no major negative consequence.

Don’t Forget To Accept Pounds!

Awhile ago I blogged about making sure you accept Euros in addition to U.S. dollars. I also blogged on Friday that I had [finally] added Australian dollars to the Paint.NET donations page. Several were quick to comment that I did not have British pounds on there, and so I also decided to put that on the donations page.

You see, the pound is worth even more than the Euro. The former is worth $1.9856 while the latter is currently at $1.4366. And, imagine my surprise when a significant percentage of new donations are now in pounds! In fact, over 30% of them are. The numbers seem smaller – 4 pounds here, 8 pounds there – but it is still good for people to be able to donate in whatever currency they are most comfortable with. With the conversion rates the way they are it generally ends up as a little bonus on my side anyway. The U.S. dollar is just not what it used to be.

So please donate to Paint.NET – just make sure you use anything except U.S. dollars 🙂 And if you’re setting up a donations page for your freeware or other project, you’d do good to accept as many types of currencies as your payment processor(s) will allow (for me, that is PayPal). I’m reminded of the comic by Mike Peters where the kid holds out her hand and asks her dad, “Can I have my allowance for this month?” The dad pulls out a wad of cash and she scowls, “…. in Euros.”
(sorry folks, can’t inline the image due to their copyright/licensing)

Australian $ added to Paint.NET Donations page

Someone made a blog comment the other day about not having a button on the Paint.NET Donate page for Australian currency. I realized they were totally right and there was no reason to not have it, so today I added it.

Also, freeware developers … do not underestimate having a release close to Christmas time. My donations are up 111% this month compared to November (which, in turn, was up 64% over October). Next month will probably show a sharp downward correction, but that’s fine and is to be expected.

Quick Thoughts on Continuations for UI Code

Continuations in C#, as exposed via the yield keyword and “iterator blocks”, is a seriously underused and under-researched feature. In C++, you can use Win32 fibers to accomplish the same, and this is in fact how they are implemented in the .NET 2.0 runtime. Update: They are not implemented using Fibers. Thanks for the clarification James. Learn something knew every day!

Did you know that you can use these to implement blocks of code that can literally jump between threads? Quite useful for UI code so you can avoid using all sorts of tedious synchronization and messaging primitives, and also avoid having to split your tasks into multiple functions. Instead of writing function 1 which kicks off a task on the thread pool, then function 2 that performs the long-running task (load a file, download some data, compute something useful), and then function 3 that executes on the UI thread when that is done and sets some UI flags … you can write 1 function that is hosted by a dispatcher or executor of some sort, and then use “yield return” to spit instructions at it. Need to report progress? No problem. Switch to the UI thread, tell your ProgressBar control what’s up, then switch back to the background thread. “yield return new SwitchTo(Thread.UI)” and “yield return new SwitchTo(Thread.Background)” are going to prove quite handy for Paint.NET v4.0.

I’ve been experimenting with this concept a little bit, and the results are promising. I have a simple application that has a “AsyncImageBox” control that uses continuations. It has one function, “LoadTheImage” which implements all of the logic for establishing a connection with a server, downloading the image, reporting progress in the UI, and hooking up the final image to the UI. When the block of code needs to do something that must be done on the UI thread (report progress), it just switches over to the UI thread and does it. Then it reverts to the background thread. This is all inside of 1 function of code, there is no “queue this to the thread pool” or “invoke this message over to the other thread” or “wait on this mutex” or “set this signal” nonsense. I can use traditional synchronous/blocking code (aka, “easier to write, read, debug, maintain, understand, etc.”) for all of this.

Usability: Yes, No, Cancel, Huh?

I think MessageBox should be added to the list of banned API’s in Windows. I think it is overused and presents an easy but lazy way for a developer to ask questions that the user shouldn’t have to care about. People never read the text that is presented on them, and as a result the wrong things often happen.

As an example, consider the meeting I had with a CPA today*. I brought along a spreadsheet on a USB stick that detailed some of my finances. While he had it open, he inadvertently made a change to the spreadsheet (not a big deal – it was just a copy). When our discussion and meeting was done, he closed the spreadsheet so that he could return my USB stick. So he clicked on File, and then Exit, and was immediately presented with:

Most of us are used to this dialog and usually know what our answer will be even before we’re asked. We would click “No.” But here’s the interesting thing that I heard him say out loud:

    CPA: “Yes, I want to close it.”

And then he clicked on the “Yes” button. The dialog was asking him to save it, not confirm his intent to close it. But for some reason he assumed the dialog was asking him to confirm his intent instead of serve a warning. I hardly blame it on a lack of intelligence, but rather on the usability of Excel, et. al. and the terrible “Yes, No, Cancel” pattern that [almost] all developers seem to be stuck on reusing.

If a task dialog pattern had been used instead, he may have clicked on the button that corresponded with his actual intent, which would have been to click “No”. For Paint.NET v3.0, I instituted these all over Paint.NET in an effort to improve usability.

In this case, “Yes” is replaced by “Save” and “No” is replaced by “Don’t Save”. It is much clearer what will happen when you click on them – there is less left to be assumed, extrapolated, or guessed at by the user. In the “Yes, No, Cancel” dialog above, it is not really clear what happens when you click “No” or “Cancel” – for the former, the changes are discarded and the application exits. For the latter, the changes are left in memory and the application stays open.

These dialogs require more code, but only because there are 8 string resources and 5 graphic resources to load or prepare. The code itself is still quite clear and easy to read, and easy to duplicate. I have a class called TaskDialog with a static function that takes all the text and graphics and then prepares a WinForms dialog that is shown modally. The button that was pressed is then returned to the caller.

Anyway, this was an interesting real-life story and I thought it was worth sharing.

* Yes, this is a true story! 🙂

Ed Harvey's Effects updated for Paint.NET v3.20

Ed Harvey has made quite a name for himself on the Paint.NET forum by consistently releasing some of the best and most comprehensive effect plugins. He has just released an updated version of his effects plugin pack that supports Paint.NET v3.20 and uses some of its IndirectUI capabilities.

I highly recommend this plugin pack, so go and download it! In all, there are over 20 effects that go into the Blurs, Distort, and Color effect menus. Here’s a preview:

Original (Olivia Wilde)

Dents (one of his coolest and most popular effects)


Multi View Warp


Paint.NET Usability: Why can’t you move the toolbars around?

That’s a good question. Office lets you move the toolbars and even the menubar around (it’s just a differently rendered type of toolbar). Visual Studio lets you, and the “toolstrip” controls in .NET 2.0 supposedly have the capability. So why, then, doesn’t Paint.NET allow you to move them around or do other customization?

As Raymond Chen would say, “Because the alternative is worse.”

It becomes a usability problem, or at least an annoyance, for the people who don’t want to customize their toolbar*, and this is something I was reminded of just now when I looked over at RSS Bandit and saw this:

Why is my menu bar below the toolbar? Because I accidentally dragged something around. I don’t know when or what.

It becomes a usability and support nightmare because people would lose the toolbars, or otherwise be unable to find features they already found. Do not underestimate individuals’ ability for this! And it’s not about stupidity or intelligence, it’s just about usability. I recently had a reasonably intelligent friend ask me, “Where is that new ‘Fixed Size’ functionality for the selection tool that you’ve been talking about?”, and it was right in front of his face.

It also hinders productive troubleshooting or communication between two Paint.NET users.

Bob: “Just click on X in the toolbar.”
John: “I don’t see it.”
Bob: “It’s kinda on the far-right side. Do you have the latest version?”
John: “Yes I have 3.20.”
Bob: “Umm … is part of the window off-screen?”
John: “No the whole thing is only taking up half my monitor.”
Bob: “Yargh. Did you hide or move the toolbar or is the toolbar off-screen or under some other stuff?”
John: “I don’t know?”
Bob: “Click the Reset Toolbars menu item in the Window menu.”
John: “Hmm. We have another problem. I have no menus either.”
Bob: “I will start using four-letter words from now on. Plug your ears.”

Here’s the conversation without customized toolbars:

Bob: “Just click on X in the toolbar.”
John: “I don’t see it.”
Bob: “It’s kinda on the far-right side. Do you have the latest version?”
John: “Oh there it is! And yeah I have 3.20.”

So, because I do not want an inbox full of people asking me how to fix their toolbars or how to find their features, and nor do I want to have to add complicated self-healing code or a “Reset Toolbars” menu item (that people would still lack the psychic powers to find, mind you!), I have disallowed this type of functionality. The toolbars will only ever show up in Paint.NET in one way. Oh, and I also want people to be able to use the software and not have a need to consult documentation or a support line. Because if users have to do that, then you have failed.**

* According to conventional insight, most people do not customize anything about the software they use.

** I think this insight comes from a blog I’ve read elsewhere but for the life of me I can’t find it.

Paint.NET’s plans for 2008

I want to start working on Paint.NET v4.0, and the time has finally come. I have some wicked ideas on pervasive parallel programming that I am hoping to thoroughly integrate throughout the application*. However, this will take a lot of time to engineer, and may involve significant refactoring or a “spiritual rewrite”. It will take at least a year to complete, if not more.

In the meantime, however, the v3.xx architecture still has some life left in it (duh). My plan is to have new, minor releases of the v3.xx branch throughout the course of 2008 at 6-week intervals. To start, I plan on releasing v3.22 in mid-January. It will have some bug fixes, a performance tweak, and Tom’s “Reduce Noise” plugin. After that, you can expect something like a v3.24 release by March. Maybe I’ll add 8-bit and 24-bit support to PNG or something. I’m not sure yet. It will depend on what the user base is clamoring for of course.

For a freeware like Paint.NET, you cannot just have a year-long gap between releases. You have to keep interest in the project alive which means staying on the front page of sites like Neowin. The project grows by having short, large spurts of press coverage. When I released version 3.0 in January of this year, the short-term increase in traffic was enormous and the smaller but long-term, sustained increase was crucially important.

Other revenue opportunities might present themselves as well, such as developing and selling a “Paint.NET Plugins Pack” for $10 or something. These could provide fuel for more rapid development of the major version 4.0 release.

* I have a lot of thoughts in this area, but so far have not distilled them into short-enough blog posts. It’s one of those, “where do I start?” and “who’s my audience?” problems.