Paint.NET 3.35 … "ugly" canvas background?

In the latest update, I changed the canvas background in Paint.NET so that it was a solid color instead of a grey gradient. The rationale for this change was based on a comment made by forum member "mkidd":

"The top-to-bottom grayscale gradient on the desktop area of Paint .NET (3.30 in my specific case) can cause serious toning misjudgments. I didn’t fully appreciate this until a colleague pointed out how much it was throwing him off, i.e. printed results didn’t seem to match on-screen results due to improper relative corrections applied to the top and bottom areas of images, say sky vs. ground. It seems that the plain uniform gray background in Photoshop is actually pretty important."

I agreed. So, I changed it to a solid color and got rid of the gradient. The color I used is based on a system color — in fact, it’s a 50% blend between SystemColors.Control and SystemColors.ControlDark. This produces mostly good results, but apparently there are some custom color schemes where it looks downright awful. The first person to really express a passionate opinion about this was Jelle Mees (forum name), who wrote a forum post about it, and included a screenshot:

In Vista, I think it looks pretty good:

 

Here is how it looks in XP with the Luna / Olive Green theme:

Similarly, I think it looks fine in the other built-in XP themes, especially "Classic". I was trying to honor the system color choices, but a few other forum members have expressed a very strong opinion that the canvas background should be a static (or configurable) color that is not based on the system theme.

It got me to thinking, and I realized that I didn’t fully parse the original statement about the gradient versus a plain gray background. In fact, I’m now convinced that basing the canvas background on a system color is a bad thing. The reason for this has to do with color calibration between systems: if the canvas background is grey on one system, but off-white/yellow on another, then it could throw off a person’s perceptions of colors between the systems. Or, two different people on two separate but differently configured systems would perceive colors differently (all else being equal of course, such as the monitors’ color reproduction). Gray is a neutral color, and won’t have much of a radiosity effect than colors with hue.

Well, what do you think? Is the new canvas background in 3.35 ugly? Would you rather it be a solid gray on all systems regardless of theme, such as in the Vista screenshot above?

In the forum post mentioned above, I have made a available a replacement PaintDotNet.Core.dll that sets the canvas background to #c6c6c6 regardless of the system theme. If you’d like, you can try it out before commenting! That DLL will not work in future versions of Paint.NET, however.

Paint.NET v3.35 final release now available!

This version has a new Posterize adjustment, Intersect selection mode, and dramatically faster selection editing performance (add, subtract, intersect). Oh, and the usual crop of miscellaneous bug fixes, of course!

You can get this update by using the built-in updater (Help menu -> “Check for Updates”), or by downloading it from the website at http://www.getpaint.net/ . The update process is very automated, so you won’t need to worry about uninstalling your old version or anything — it’s all taken care of for you.

Changes:

· New: Posterize adjustment, by Ed Harvey.
· New: Intersect selection editing mode.
· Improved: Dramatically improved selection editing performance for the Add and Subtract modes (and also for Intersect). The performance used to be dependent on image size, and thus was often unusable on images larger than 1024×768 pixels. It is now dependent only on selection complexity (number of polygon edges) — the improvement is generally between 3x and 1000x.
· New: When holding Ctrl or Alt for a selection tool, the cursor now has a plus or minus indicator.
· Changed: The canvas background is now a solid color instead of a gradient. The gradient was causing certain tone misjudgments related to bright versus dark colors.
· Changed: Shortcut key for Sepia is now Ctrl+Shift+E. The shortcut for Posterize is now Ctrl+Shift+P.
· Changed: Hotkeys for selection modes are now Left click for Replace, Control+Left click for Add (union), Alt+Left click for Subtract (difference), Alt+Right click for Intersect, and Ctrl+Right click for Invert (xor).
· Changed: When using a selection mode other than “replace”, it will now draw the selection outline so that you can see both the original and resulting selection areas. Before, it would only draw the resulting selection area outline, which made modes such as Intersect hard to use.
· Fixed: The Resize dialog had some rounding errors with the “Maintain aspect ratio” feature, which caused a few discrepancies and even a spurious “out of memory” error.
· Fixed: Some quirks with the Color Wheel control for IndirectUI-based effect plugins.
· Fixed: Several miscellaneous and rare crashes.
· Fixed: The installer would display a bizarre error if a “blank” install folder was attempted.
· Fixed: The installer now only accepts absolute path locations, instead of relative ones. This fixes an ambiguity between where Paint.NET believes it is installing itself to, and the directory that Windows Installer actually uses.
· Fixed: Sometimes pasting would result in a crash when certain types of malfored data were on the clipboard.
· Fixed: It was possible to get around some of the protections imposed by the IndirectUI system. This was causing instability with some effect plugins such as “Fern Fractal”.
· Fixed: There were some issues with the implementation of “linked” sliders for effects based on IndirectUI.

Enjoy!

C# Extension Methods … Portability Aid?

In my first post about Paint.NET v4, I mentioned that I was learning a lot about C# 3.0 and .NET 3.5. One of the things I’m absolutely falling head-over-heels for is extension methods.

One use of these is to add utility functionality on to existing classes that you don’t own the implementation for. As an example, consider the System.Uri class. In my opinion, it could use an Append method:

public static class UriExtensions
{
    public static Uri Append(this Uri baseUri, string relativePath)
    {
        return new Uri(baseUri, relativePath);
    }
}

You can then use code such as this:

Uri donateUri = new Uri("http://www.getpaint.net").Append("/donate.html");

Instead of:

Uri donateUri = new Uri(new Uri("http://www.getpaint.net"), "/donate.html");

(Bear in mind that the string literals would probably be pulled out of a list of constants, and not inlined like in the code above.) In this case, we’re simply enabling a choice of coding style and not really doing anything new. We are avoiding pathological nesting such as "new Uri(new Uri(new Uri(…" for when we need to paste together multiple relative Uri paths.

One place I’m finding extensions methods to be very useful is in defining and implementing interfaces with only strictly orthogonal functionality. You don’t need to add utility methods to interfaces, you can simply define them as extension methods.

For example, my resource manager is contained in an IResourcesService interface that has GetString() and GetBinaryStream() methods. And that’s it.

using System.IO;

namespace PaintDotNet.Framework
{
    public interface IResourcesService
    {
        string GetString(string stringID);
        Stream GetBinaryStream(string streamID);
    }
}

All resources other than strings are stored as binary streams, such as icons, bitmaps, text files, etc. If you want to load a bitmap, then you can use the handy extension method:

using System.Drawing; // pull in GDI+

namespace PaintDotNet.WinForms
{
    public static class ResourcesServiceExtensions
    {
        public static Bitmap GetBitmap(this IResourcesService resources, string streamID)
        {
            return new Bitmap(resources.GetBinaryStream(streamID));
        }

        // similarly for GetIcon, GetCursor, etc.
    }
}

… elsewhere in the code …

menuItem.Image = Resources.GetBitmap("Icons.MenuFileExitIcon.png");
    // Resources is a local property that gets the IResourcesService assigned to us

I could have multiple implementations of IResourcesService. For example, I could have the "normal" implementation that pulls from the local file system, then a test version that does all sorts of fakery, and then maybe experiment with one that loads all my resources via HTTP GET. But, I only ever need one implementation of GetBitmap() because it is just a helper method sitting on top of GetBinaryStream().

But wait, there’s more!

GetBitmap() returns a reference to a System.Drawing.Bitmap object. You generally only care about this type if you’re writing a WinForms application (which I am doing). Or, put another way, you wouldn’t want an object of this type if you were writing a WPF application. You’d want a BitmapSource or something. I am not writing a WPF version of Paint.NET v4, but one of my goals is to have the ability to write one. This is where the "separation of UI and logic" comes into play from my previous post. I refer to the WinForms UI as the shell. The "business logic" portion of the code base is referred to as the application (app for short). You could potentially have a WPF shell, or a command-line shell. Or you could write your own UI toolkit that P/Invokes straight to the Win32 API and bypasses WinForms completely (I believe the latest Zune UI does this).

So, if we wanted a WPF shell for Paint.NET, we could still use the same interface and implementation for IResourcesService. No changes needed anywhere. Why should the resource loader care about what UI you’re using? Its job is to give you strings and binary streams. It’s your job to parse them. It shouldn’t need a reference to the likes of System.Drawing.dll or PresentationCore.dll.

Thus, we’d probably find the following extension method setup useful in our hypothetical WPF shell:

using System.Windows.Media.Imaging;
namespace PaintDotNet.Wpf
{
    public static class ResourcesServiceExtensions
    {
        public static BitmapSource GetBitmapSource(this IResourcesService resources, string streamID)
        {
            return …; // not sure what code goes here exactly 🙂
        }
    }
}

The WPF-based shell would never pull in the PaintDotNet.WinForms namespace, and would instead use the PaintDotNet.Wpf namespace. They’d almost certainly be in separate DLL’s. So these two static extension classes will never collide, even if they have similarly named methods.

Using extension methods this way is probably preferable to "interface inheritance" for adding utility methods. For example, now I don’t need to declare a IWinFormsResourceService that derives from IResourcesService. I can avoid putting all the utility functionality into the base interface which could cause headaches.

As another example of only implementing "strictly orthogonal functionality", consider the case of the System.Windows.Threading.Dispatcher class and its CheckAccesss() and VerifyAccess() methods. The latter basically calls the former and converts true/false into return/throw. It could be done as an extension method. In fact, I have my own IDispatcher interface in Paint.NET v4, and this is exactly what I do. For sections of my code that are P/Invoke heavy, I’ve found it useful to have a SizeOf() method attached to any struct, as opposed to calling Marshal.SizeOf(Type). Less keystrokes, and I think it improves readability.

Yet Another Poorly Done Business Proposal

I can’t make these things up, folks. I honestly feel that if someone really wants to do business with you, they’ll at least do more than 3 seconds of research and get your product’s name correct. It should be easy to figure out that the product hosted at http://www.getpaint.net/ is called Paint.NET, and not "Get Paint." The logo is only emblazoned at the top of every page…

Hello,

Our company, Firstlook, is currently seeking partners to distribute our latest software product, SearchInOneStep.com.

With 10 million active users of search software and a stellar record in the search industry, we feel we would be a great match with Get Paint.

Get Paint attracts a large number of downloads, with which our software could be easily bundled during install to provide an alternate steady source of risk free revenue  which goes directly to your bottom line.

We offer our bundling partners the option to be paid per download and would be open to any volume of downloads that your company could provide.

Please feel free to contact me at your earliest convenience if you are interested in monetizing your downloads with Search in One step.  I look forward to discussing this matter with you further.

Thank you,

Sanjay

Well, Sanjay, my product is not called "Get Paint". I went to the website and tried the "search in one step" and it’s garbage. All it does is syndicate a bunch of ads. There aren’t even any search results! So I told Sanjay that I would not be doing business with him.

As for everyone else, if you want a good search engine for Paint.NET stuff, might I recommend http://searchpaint.net instead? 🙂

The very first Paint.NET v4.0 screenshot!

It’s really not as exciting as it sounds unfortunately 🙂 I’ve been quiet on the subject for some time, though, and wanted to put out some information.

For a long time I debated whether I should fully refactor Paint.NET, or do it from scratch. I finally decided on the latter, and have finally been able to make some progress. Bear in mind that "from scratch" means that I started with a brand new, empty solution file in Visual Studio. It does not mean I’m throwing out all the current Paint.NET code. There are many places where I will be able to reuse vast amounts of it — rendering kernels, effects, math, algorithms, etc. The entire structure of the program will be different, however. If you demolish your old house to build a new one, you don’t throw out the grand piano in the living room too. It still has plenty of value.

The screenshot above is from a build that so far contains about 12,000 lines of code. This sounds like an exorbitant amount for a few menus and an About dialog, but hear me out. I’m building it using Inversion-of-Control (IoC) and related patterns, which requires a lot of new decisions to be made regarding assembly layout (layering, essentially). I’ve also got a new system for asynchronous programming, separation of UI and logic, separation of "contract" and "implementation" assemblies (this becomes very important when plugins come into play), command routing and handling, etc. Along the way I’m learning more about .NET 3.5 and C# 3.0, and have come across some wickedly cool things that are now possible.

As far as possible, everything is lazy initialized or loaded in Paint.NET v4. To aid this, I have a new asynchronous programming model and exception handling pattern, which I could probably talk about for hours. For example, in the screenshot above, the icons for the menu items are not loaded until you click on the menu — in fact, they are loaded using background threads (the threadpool). The graphics and rich text that populate the About dialog are also loaded from the resource manager service using a background thread. For these specific cases, there are no major performance requirements that necessitate this; however, it’s much easier to solve these simple cases and to establish good patterns for later, than the reverse. I’ve also got an old Pentium 4 that I now have available for baseline performance testing, and it will be interesting to see if I can keep the program as responsive on that system as on my Core 2 Quad — even if rendering kernels take longer to complete, it should still always immediately respond to input. *crosses fingers* (Anyone else get sick of Safari on iPhone not being responsive while loading some web pages? Yeah.)

Also, in the screenshot you may notice that the mouse cursor is resting in the File menu even though the About dialog is open. In Paint.NET 3, all dialogs are modal and you can’t do anything with the rest of the program while one is open. In Paint.NET 4, I’m trying to remove this as much as possible and as far as it makes sense to. For example, why should an update download prevent you from using the rest of the program? Why can’t you switch from image 1 to image 2 while a compute-intensive (aka, "takes a long time") effect is rendering on image 1? Why can’t you interact with the canvas while an effect dialog is open? Or change the selected colors?

Anyway, that’s all for now. I estimate that Paint.NET v4 will be 200,000 lines-of-code when it is done, which places me at about 6% complete.