One nice thing about the Move tools in Paint.NET is that they implement something I call fine-grained history. When you go to edit a selection or move its pixels around, you get 1 history entry for each adjustment you make. This allows you to move it around, rotate it, etc. and then when you press âUndoâ it only backs out of the most recent adjustment you made. Contrast this to the Gradient, Line/Curve, and Text tools which donât keep track of each edit you make and can only undo things wholesale.
Recently Iâve been working on adding this to the Gradient tool for 4.0. This support will then make its way into the shape tools, and probably the Text tool as well (also, shapes will be undergoing an overhaul soon). The reason Iâve been working on the Gradient tool is that it is a fairly simple tool and this allows me to get the code for fine-grained history working without having to worry about as many âmoving parts.â
Hereâs a screenshot showing the Gradient tool having gone through itâs initial âdrawâ operation, followed by two âeditâ operations:
(click for full resolution version)
Once this makes its way to the new Shapes tool (not yet written unfortunately), youâll be able to: 1) draw a shape, 2) move, rotate, resize the shape, 3) change the colors, 4) change the stroke, 5) change any other properties, and then 6) finish/commit it to the layer. Currently the shape tools in v3.5 are fairly barbaric and donât even let you change their color or other properties once youâve pressed down the mouse button. It can be quite painful if you are consistently off by a few pixels and have to constantly redo your Rectangle until itâs just right.
There are some other important benefits to doing this. Iâll be able to significantly reduce memory usage, for starters. In Paint.NET v3.5, the tools are implemented in a rather cumbersome way. While you draw, itâs modifying the layer data so it contains what youâre drawing ⌠then it draws that to the screen. Then, when you move the mouse it has to copy back the original contents of the layer before drawing the new stuff. When youâre done, it has to copy back the original pixels, save it to disk, then redraw everything one last time. This is implemented with a very carefully debugged protocol that requires all sorts of data to be copied around various buffers before, during, and after any edit operation. Any bug in the protocol for drawing, copying, and emitting history entries can lead to data loss.
With the new system, the gradient (et. al.) draws itself using a layer overlay, which only affects the rendering pipeline and not the image itself. Undoing the edit before itâs committed is a simple matter of throwing away the overlay. When itâs time to commit the drawing, the code just says âhey I changed this rectangular areaâ and then a simple diffing algorithm copies away only the parts of the layer that changed. Thereâs no need for me to manage complicated geometry masks and dirty regions any more (other than whatâs needed for an active selection, of course). All the large and temporary (and permanent, ack!) buffers are simply no longer needed.
This is all very important! Reducing memory usage is very important for an application like Paint.NET, especially on 32-bit systems. This also removes another barrier toward migrating to a fully tile-based memory allocator, which then opens up doors for many other very, very cool features.
Another benefit to doing this is that it moves the tool rendering code off of the UI thread and into the rendering pipeline (which was already moved off the UI thread for 4.0). The UI will always be responsive while youâre drawing, even if the canvas is still âcatching upâ to what youâve done. Itâs pretty cool to be able to throw around the Gradient toolâs âhandlesâ (aka ânubsâ) and see them responding at a much higher framerate than the canvas itself. If youâve ever had to work with an image that is very large, or has a lot of layers, or both, then you probably already know that this will be a great thing.
Also, right now the Move Selected Pixels tool is a monster (for me that is; not for you). While it does work perfectly well, the code is wildly complicated and fragile. Adding or changing any features would be a recipe for disaster. By migrating it to this much simpler system, Iâll be able to reduce memory usage further, increase performance and quality, and remove any âhitchâ you may notice at the start of a move operation. The reason the âhitchâ happens is that the history data (the layerâs pixels) must be saved off into a temporary buffer before any rendering can be done. Afterward, thereâs a frightening dance of copying pixel data from the layer to a scratch area, and then transforming pixels from a 2nd copy back to the layer, and then undoing it all before redoing it, etc. The nightmare gets worse when history entries come into the mix. The code is basically the worst time travel paradox you could possibly imagine â I canât honestly say I fully understand how it works, even though I wrote it. What I can say is that it works, but only because itâs been thoroughly and exhaustively tested, debugged, and bugfixed.
Oh, and youâll notice in this screenshot that Iâve finished up the last bits of UI reorganizing. Iâve done away with the Window and Utilities menus. Instead, each of the floating windows has its own toggle button at the top-right. The gear icon opens up the Settings dialog, and the question mark icon gives you the traditional help menu. Anything that was in the Utilities menu has either been moved into the Settings dialog, or removed. Iâve also increased the size of these icons to 24×24 because I found they were just too darn small at 16×16.
Within the Settings dialog Iâve added a Diagnostics page which gives you a peek at your system information as Paint.NET sees it (basically the information thatâs printed at the top of a crash log, and more). It will also give you access to the crash logs folder and some other simple things that should make troubleshooting a bit simpler. Oh, and for those who donât like the âdancing antsâ (animated selection outline), Iâve included a checkbox to control whether this is on or off (not on the Diagnostics tab though).
In time, I hope to elaborate further on what I mean by âthis stuff opening doors for other very cool features.â Iâve been throwing around ideas for the last few days and itâs exciting how much things are radically simplified by all this. Iâm hoping that 4.0 will serve as a solid basis for many, many features over its lifetime. 4.0 wonât have all the features everyone wants, but it will enable me to finally add so many more of them!