If you’re writing a custom effect for Direct2D, which involves implementing the ID2D1EffectImpl interface, you may run into a chicken-and-egg problem if you’re trying to use another custom effect that isn’t yet registered with the Direct2D factory.

Direct2D is set up very carefully to prevent the custom effect from getting access to things like the ID2D1DeviceContext. Unfortunately, you also don’t get access to the ID2D1Factory1, and this creates a brick wall that blocks you from registering effects just-in-time (versus ahead-of-time).

When Direct2D calls your ID2D1EffectImpl::Initialize() method, it’ll provide you with an ID2D1EffectContext and an ID2D1TransformGraph. The ID2D1EffectContext::CreateEffect() method allows you to instantiate another effect, which you can then use in a call to ID2D1EffectContext::CreateTransformNodeFromEffect() to create an ID2D1TransformNode that you can place into your transform graph.

However, there are no registration methods on ID2D1EffectContext like you see on ID2D1Factory1. This means that you have to register your effects up-front, probably in the custom effect’s static Register method (which is the pattern used in the Direct2D documentation).

Notably, this requires that your custom effect has what amounts to a dependency manifest in its Register method whereby it calls all of the Register methods for the custom effects that it needs to utilize later. This can be seen as a violation of the DRY (“Don’t Repeat Yourself") principle, and can be a source of subtle bugs and frustrating debugging if it gets out of date.

However, you can still implement a proper deferred registration mechanism. Here’s how I do it in Paint.NET, although I won’t show the actual code because it’s in C# with lots of my own custom things that won’t make sense out-of-context:

Step 1: Implement your custom effect all the way up through ID2D1EffectImpl::Initialize(). Note that this method receives an ID2D1EffectContext and an ID2D1TransformGraph.

At this point you’ll be wanting to use ID2D1EffectContext::CreateEffect(), followed by ID2D1EffectContext::CreateTransformNodeFromEffect(), and then an appropriate method on the ID2D1TransformGraph to add the effect’s transform node into your transform graph.

Step 2: If ID2D1EffectContext::CreateEffect() returns an error, then either the effect can’t be created or it hasn’t yet been registered. If it didn’t return an error, then go about your business normally. Otherwise, proceed to step 3.

NOTE: Direct2D is supposed to return D2DERR_EFFECT_IS_NOT_REGISTERED (0x88990028), but it doesn’t. It’ll actually return HRESULT_FROM_WIN32(ERROR_NOT_FOUND), which is 0x80070490. I check for both, just in case Microsoft decides to fix its code.

Step 3: At this point you’re normally SOL, as we need to register the other custom effect we want to use, but we don’t have an ID2D1Factory1 to call a RegisterEffect method on.

So, step 3 is to actually create an effect that does exist using ID2D1EffectContext::CreateEffect(). I use CLSID_D2D1Flood, as it’s a very simple effect and probably cheap. We won’t be using the effect for its intended purpose – it’s a tool to get what we really want.

Step 4: Now that you have an ID2D1Effect, we’re still not quite where we want to be because it doesn’t implement ID2D1Resource which has the GetFactory() method that we need to get the ID2D1Factory that has the RegisterEffect methods we need. If only there were a way …

But we’re not stuck yet! Either call ID2D1Effect::GetOutput() to get the effect’s ID2D1Image, or use QueryInterface to retrieve a pointer to the ID2D1Image interface. The documentation for GetOutput establishes these as equivalent, btw.

Step 5: Now you can call ID2D1Resource::GetFactory() on the effect’s ID2D1Image pointer, and then do the obvious thing … (e.g. QI for ID2D1Factory1, then use that for registering the other effect). I like to call ID2D1Factory1::GetEffectProperties() first to try and distinguish between the effect not being registered versus the effect’s initialization returning an error. If the effect is already registered then just bail and return a failure HRESULT instead of trying to forge ahead with re-registering the effect.

Step 6: Don’t forget to release all of the temporary objects.

An important thing here is that effect registration works correctly when called from within the initialization of another effect. Hopefully it stays that way!

Anyway, this was one weird trick I found that enabled a lot of my code to be simpler. It’s still better if you can find a different way to provide access to the ID2D1Factory1 for your custom effect, but this is a good fallback technique.

In an upcoming update, Paint.NET will be opening the doors for effect plugins to use Direct2D, to implement custom Direct2D effects, to chain these effects together, and to not have to worry about up-front registration of custom effect dependencies. Stay tuned!

This is a small update that fixes some important bugs and substantially improves plugin loading performance. Sorry for the random crashes — it was actually a bug in Direct2D (a Windows component), and it took about a week to fully debug.

For the Classic release, you should be offered the update automatically within the next week or so. You can get the updater sooner by going to ⚙ Settings → Updates → Check Now. You can also download and install it directly from the website.

Fixed a crash that would happen when closing an image, exiting the app, or sometimes just at random. This was happening due to a bug in Direct2D where ID2D1EffectContext does not honor the multithreaded initialization flag from its ID2D1Factory, and was thus corrupting its own internal data structures when released on the finalizer thread.

Fixed: DirectX 9 GPUs can now utilize hardware acceleration again (in 4.1.3 they were forced to use software rendering)

This is a hotfix for 4.1.2 that fixes the crash that people were seeing on old DirectX 9 era GPUs like the NVIDIA GeForce 7000 series (not to be confused with the much newer GeForce 700 series). This release will fall back to software rendering for affected GPUs, although I’m hoping to reinstate hardware acceleration in the next update.

As a reminder, 4.1.2 (and 4.1.3 of course) addresses two security vulnerabilities. As such, administrators who managed deployments of Paint.NET are urged to begin the update process immediately. Everyone else is also urged to update as soon as possible, of course.

… is causing issues on DirectX 9-era hardware such as the GeForce 6000 and 7000 series, NVIDIA nForce, and the Matrox M9140. I even got a crash report from someone with an SiS Mirage (?!). Talk about a trip through memory lane …

If you have 4.1.2 installed and are not experiencing a crash when zooming in or out, then you have nothing to worry about.

If you have 4.1.2 installed and are experience a crash, then go into Settings and un-check “Enable Hardware Acceleration (GPU)”.

You can still download 4.1.2 from the website, but the built-in updater will not offer it to you. In fact, it’ll give a 404 error if you try to manually check. I decided I’d rather have 404’s than app crashes for the time being.

I’ll be rolling out a 4.1.3 update just as soon as I can find something like a GeForce 7000-series to test with … there has to be a PC recycling store somewhere in the bay area with a box full of them, right? (I ordered some cards off of eBay but I’d really like to get this solved today)

NOTE: The deployment of this update has been paused due to an issue with pre-DirectX 10 GPUs (e.g. GeForce 6000 and 7000 series). A new update will be issued very soon! You can still download 4.1.2 from the website, but the built-in updater will not offer it to you.

This is a small update that improves startup performance, fixes some bugs, and addresses two security vulnerabilities. Administrators who manage deployments of Paint.NET are urged to begin updating immediately. Details of CVE-2018-18446 and CVE-2018-18447 are pending publication.

Fixed: For the Store release, launching with the paintdotnet: protocol no longer shows an error if no parameters are provided (thanks @Bruce Bowyer-Smyth!)

Fixed a crash for some effects that were rendering too quickly

Fixed a bug in the Image->Resize dialog where the asterisk and the note about which resampling mode was being used, e.g. “* Bicubic will be used,” weren’t going away or updating correctly

New: Added a /set:SETTING=VALUE command-line parameter. This is specifically useful for disabling hardware acceleration if the app won’t start or you can’t reach the Settings dialog. This is also necessary for the Store release whose virtualized registry cannot be edited. To launch the Store version with hardware acceleration disabled, go to Start -> Run and type in “paintdotnet:/set:UI/EnableHardwareAcceleration=false” (without quotes) and press OK

New: Added detection for when “Diebold Warsaw” is causing Paint.NET to crash. The error message will indicate this cause.

This update converts some effects to use the GPU for rendering, resulting in huge performance gains. Copy/Paste can now be performed on selections, Bicubic resampling is now available for the Move Selected Pixels tool, and there are lots of little quality-of-life improvements and general bug fixes.

GPU-powered Effects! Some of the effects have been rewritten to use Direct2D’s image processing system, and the results are phenomenal for performance. Gaussian Blur is actually fast now and no longer takes ages to complete for large images or radii, and the same goes for both Motion Blur and Radial Blur (although Radial Blur does currently need a pretty powerful GPU). A few other effects were converted for the sake of completeness and for my own learning, and there are two new ones (Morphology and Turbulence). This is where most of my time (the aforementioned "last 7 months") and sweat was spent: getting a GPU-based effect rendering system up and running end-to-end was no small feat! Expect to see more effects converted over to the GPU in subsequent updates as this tech matures. I am also planning to make the GPU available for plugins to use in a future update.

Copy/Paste for Selections. You can now copy the active selection to the clipboard, as well as paste from the clipboard to the active selection (all selection combine modes are supported). This copies only the selection, not the pixels that are within the selection. You can then apply the selection to another layer or image, or just save it for later. Because the selection is copied to the clipboard as JSON text, you can use external utilities to maintain them or even do processing on them (you aren’t limited to Paint.NET’s built-in selection operations, in other words). For example: if you want to save and reuse 5 selections, just open up 5 copies of Notepad and use them as a storage buffer. You can also use Paste Selection when a regular image is on the clipboard: if the image is from Paint.NET, the embedded selection will be used, otherwise the image’s size will be converted to a selection (a rectangle anchored to x=0,y=0 with the same width and height as the image). More formal documentation for the data format will be available soon.

Bicubic resampling for the Move Selected Pixels tool. This can produce much higher quality results than Bilinear resampling. It is very CPU-intensive, so a fast CPU with 6+ cores is highly recommended if you plan to use it a lot. The default is still Bilinear, so if you’d prefer to always use Bicubic you can change it from Settings -> Tools.

New +/- buttons for Tolerance and Hardness. This is a great quality-of-life improvement, especially for the Magic Wand when you’re trying to find just the right value for Tolerance.

Full change log:

Improved: Gaussian Blur, Motion Blur, and Radial Blur now render using the GPU and are significantly faster

Changed: The following effects are now rendered using the GPU: Edge Detect, Pixelate, and Relief

Changed: The following adjustments are now rendered using the GPU: Black & White, Invert Colors

New Effect: Distort -> Morphology (uses the GPU)

New Effect: Render -> Turbulence (uses the GPU)

New: Bicubic resampling is now supported for the Move Selected Pixels tool. A CPU with 6 or more cores is highly recommended if you want to make it the default mode from within Settings -> Tools.

New: Edit -> Copy Selection. This will copy the current selection’s geometry to the clipboard as JSON text.

New: Edit -> Paste Selection. This will apply the selection from the clipboard to the current selection. All 5 selection combine modes are supported.

Changed: The Text tool now uses Points for font size measurement. You can also elect to use the old font size metric, "Fixed (96 DPI)" via the dropdown to the right of the font size.

Fixed: The Text tool’s recentering algorithm when typing reaches the edge of the viewport should be better

New: Added +/- buttons to the Tolerance and Hardness sliders.

Changed: Increased max zoom level to 6400%

New: The mouse wheel may now be used to scroll a long menu, such as when a lot of effects are installed (thanks @toe_head2001!)

Fixed: The "Finish" button wasn’t working if it was in an overflow container (thanks @toe_head2001!)

Fixed: Opening certain large images and then zooming with the mouse wheel would sometimes result in the scroll position being completely wrong

Changed: Hiding a layer no longer auto-selects the nearest visible layer. You can re-enable this in Settings, although this setting may be removed in a future version.

New: For the Windows Store release, the paintdotnet: protocol can now be used to launch the app so that you can specify command-line parameters. For example, you can go to Start -> Run and type in paintdotnet:"path to file1.png" path_to_file_2.png then both images will be opened (even if Paint.NET is already open)

Fixed: The background color for the Tolerance slider has been corrected for non-dark themes

Changed: Removed shortcut keys for all built-in Adjustments except for Invert Colors (Ctrl+Shift+I). This is being done to free up shortcut keys for other functionality.

New: Added a shortcut key to trigger a full .NET garbage collection: Ctrl+Alt+Shift+` (tick/tilde). This should only ever be needed for troubleshooting purposes.

New: Battery Saver Mode in Windows 10 is now respected, whether enabled manually or due to low battery. UI animations will be disabled.

New: Long file paths (>MAX_PATH) are now supported on Windows 10, as long as the group policy setting is enabled ("Enable Win32 long paths")

New for plugin developers: The IndirectUI Color Wheel control now supports an alpha slider. To use this, set the range (min/max values) of the Int32PropertyControl to [Int32.MinValue, Int32.MaxValue] (thanks @BoltBait!)

New for plugin developers: there is a new FileChooser property control type for string properties (thanks @toe_head2001!)

New for plugin developers: there is a new BinaryPixelOp.Apply(Surface, Surface, Surface, Rectangle) method overload (thanks @BoltBait!)

Changed: The IndirectUI text control now has a vertical scroll bar when multiline mode is enabled (thanks @toe_head2001!)

Changed: Visual C++ and OpenMP runtimes are now only loaded from the "app local" copy (never from the system directory). This fixes a number of headaches, and also works around a compatibility issue with the Surface Go

Fixed: Crash when drag-and-dropping images onto Paint.NET when a tool was active but with uncommitted changes

Fixed a very old and nasty crash, "InvalidOperationException: ScratchSurface already borrowed"

I’ve been hard at work on this for the last 7 months and it’s finally time for a beta release! There’s a lot of small- and medium-size things all over the place There have been some significant performance improvements to some important effects, especially those in the Blur menu. There are also a lot of quality-of-life improvements, some great bug fixes, and the ability to perform copy and paste operations on the selection, among many other things. I’ve also been making a lot of improvements “under the hood” that won’t really be visible, but that will make it easier and faster for me to make progress on features and other performance improvements.

GPU-powered Effects! Some of the effects have been rewritten to use Direct2D’s image processing system, and the results are phenomenal for performance. Gaussian Blur is actually fast now and no longer takes ages to complete for large images or radii, and the same goes for both Motion Blur and Radial Blur (although Radial Blur does currently need a pretty powerful GPU). A few other effects were converted for the sake of completeness and for my own learning, and there are two new ones (Morphology and Turbulence). This is where most of my time (the aforementioned “last 7 months”) and sweat was spent: getting a GPU-based effect rendering system up and running end-to-end was no small feat! Expect to see more effects converted over to the GPU in subsequent updates as this tech matures. I am also planning to make the GPU available for plugins to use in a future update.

Copy/Paste for Selections. You can now copy the active selection to the clipboard, as well as paste from the clipboard to the active selection (all selection combine modes are supported). This copies only the selection, not the pixels that are within the selection. You can then apply the selection to another layer or image, or just save it for later. Because the selection is copied to the clipboard as JSON text, you can use external utilities to maintain them or even do processing on them (you aren’t limited to Paint.NET’s built-in selection operations, in other words). For example: if you want to save and reuse 5 selections, just open up 5 copies of Notepad and use them as a storage buffer. You can also use Paste Selection when a regular image is on the clipboard: if the image is from Paint.NET, the embedded selection will be used, otherwise the image’s size will be converted to a selection (a rectangle anchored to x=0,y=0 with the same width and height as the image). More formal documentation for the data format will be available soon.

Bicubic resampling for the Move Selected Pixels tool. This can produce much higher quality results than Bilinear resampling. It is very CPU-intensive, so a fast CPU with 6+ cores is highly recommended if you plan to use it a lot. The default is still Bilinear, so if you’d prefer to always use Bicubic you can change it from Settings -> Tools.

New +/- buttons for Tolerance and Hardness. This is a great quality-of-life improvement, especially for the Magic Wand when you’re trying to find just the right value for Tolerance.

Change log since 4.0.21:

Improved: Gaussian Blur, Motion Blur, and Radial Blur now render using the GPU and are significantly faster

Changed: The following effects are now rendered using the GPU: Edge Detect, Pixelate, and Relief

Changed: The following adjustments are now rendered using the GPU: Black & White, Invert Colors

New Effect: Distort -> Morphology (uses the GPU)

New Effect: Render -> Turbulence (uses the GPU)

New: Bicubic resampling is now supported for the Move Selected Pixels tool. It is very CPU-intensive, so a CPU with 6 or more cores is highly recommended.

New: Edit -> Copy Selection. This will copy the current selection’s geometry to the clipboard as JSON text.

New: Edit -> Paste Selection. This will apply the selection from the clipboard to the current selection. All 5 selection combine modes are supported.

Changed: The Text tool now uses Points for font size measurement. You can also elect to use the old font size metric, “Fixed (96 DPI)” via the dropdown to the right of the font size.

Fixed: The Text tool’s recentering algorithm when typing reaches the edge of the viewport should be better

New: Added +/- buttons to the Tolerance and Hardness sliders.

Changed: Increased max zoom level to 6400%

New: The mouse wheel may now be used to scroll a long menu, such as when a lot of effects are installed (thanks @toe_head2001!)

Changed: For Windows 7 SP1 users, the Platform Update from 2013 is now required.

Fixed: Opening certain large images and then zooming with the mouse wheel would sometimes result in the scroll position being completely wrong

Changed: Hiding a layer no longer auto-selects the nearest visible layer. You can re-enable this in Settings

New: For the Windows Store release, the paintdotnet: protocol can now be used to launch the app so that you can specify command-line parameters. For example, you can go to Start -> Run and type in paintdotnet:”path to file1.png” path_to_file_2.png and both images will be opened (even if Paint.NET is already open)

Fixed: The background color for the Tolerance slider has been corrected for non-dark themes

Changed: Removed shortcut keys for all built-in Adjustments except for Invert Colors (Ctrl+Shift+I). This is being done to free up shortcut keys for other functionality.

New: Added a shortcut key to trigger a full .NET garbage collection: Ctrl+Alt+Shift+` (tick/tilde). This should only ever be needed for troubleshooting purposes.

New: Battery Saver Mode in Windows 10 is now respected, whether enabled manually or due to low battery. UI animations will be disabled.

New for plugin developers: The IndirectUI Color Wheel control now supports an alpha slider. To use this, set the range (min/max values) of the Int32PropertyControl to [Int32.MinValue, Int32.MaxValue] (thanks @BoltBait!)

New for plugin developers: there is a new FileChooser property control type for string properties (thanks @toe_head2001!)

New for plugin developers: there is a new BinaryPixelOp.Apply(Surface, Surface, Surface, Rectangle) method overload (thanks @BoltBait!)

Changed: The IndirectUI text control now has a vertical scroll bar when multiline mode is enabled (thanks @toe_head2001!)

Fixed a very old and nasty crash, “InvalidOperationException: ScratchSurface already borrowed”

Edit: This is now in effect for version 4.1. There was no 4.0.22 release — it was renamed to 4.1.

I wanted to give a heads up that the next update for Paint.NET, version 4.0.22, will require that Windows 7 users have something called the “Platform Update” installed. Later versions of Windows, meaning 8.1 and 10, do not need this because they already have what it includes.

If you’re using Windows 7 then you probably already have this installed and I wouldn’t worry about it; Windows Update should have taken care of that a long, long time ago. KB2670838, as it’s called in some circles, is over 5 years old at this point This will be more relevant to folks who are setting up new installations of Windows 7, and is especially important for sequencing of auto-install or deployment scripts, because the Paint.NET installer will not handle the installation of this dependency.

As usual, whenever I add a dependency on a newer OS or .NET or anything, I get people pulling their hair out and asking me “why?!” (okay maybe I’m exaggerating)

In this case, the Platform Update includes significantly updated versions of Direct2D, Direct3D, DirectWrite, Windows Imaging Component (WIC), Windows Advanced Rasterization Platform (WARP), and Windows Animation Manager. Paint.NET uses all of these, and especially the Direct2D v1.1 update brings a lot of new features that will be very beneficial.

One of the projects I’m working on is to use the GPU much more aggressively in Paint.NET. Right now the GPU is mostly just used to blit the canvas and draw decorations on top of it. I’d like to go further, starting with the use of Direct2D v1.1’s effect system for, well, effects.

I’ve got a prototype up and running that surfaces a few of Direct2D’s built-in effects as Paint.NET effects and the performance is very promising, even when using WARP (software rendering). It also appears to automatically work with SLI, although I still need to do some benchmarking to confirm any actual performance increase.

If this works out well then you can expect more use of the GPU throughout Paint.NET as updates trickle out over time.