I've been dreading this, but it ended up being a great chance to revisit some old code, written before PD switched to its current "always premultiply alpha" approach. Because of that, I was able to not only get Crop to Selection working with layers, but also to greatly improve the performance of cropping in general, as well as retrieving a chunk of the image as processed by a selection (yay!). Note that cropping an image to a selection may cause some layers to become fully transparent, if they lie off completely off the selection. So it's not a bug if layers suddenly don't have thumbnails after the operation.
In the future I may add this feature back as a plugin of some sort, but right now it would require an unfortunate amount of of work to make it layers-friendly (and given its niche appeal, I'd rather spend that time elsewhere).
Another key step on the path to "full layers support". As part of revisiting this function, I also removed the old custom resampling code (which worked well, but was enormous) in favor of leaning on GDI+ if FreeImage can't be located.
This function is only used a handful of places (such as the Unsaved Changes dialog, where a preview of the image is needed), but its previous results were quite poor due to StretchBlt mangling RGBA data. GDI+ is now used for high-quality resampling.
This is the big one! To my knowledge, no other free photo editor (including biggies like GIMP and Paint.NET) allow arbitrary rotation of layered images. They all require you to flatten the image first. But we are better than that. ;) Large images might take some time to rotate, but the results will be perfectly aligned (and by perfectly I mean with sub-pixel accuracy!). Also, I got live previews working again.
Unpleasant project; glad to have it over.
With this commit, all items in the Adjustment and Effects menus should be working 100% correctly. If something is not working, please report it as a bug.
PD is awesome because it manages all layer boundaries automatically, rather than forcing them to the size of the image like other photo editors *cough* Paint.NET *cough*, or making the user manually enlarge layers when they do something that lies outside layer boundaries *cough* GIMP *cough*. This makes PD both user-friendly and very light on RAM. Unfortunately, it also creates some ugly programming situations. This commit tackles one such area. Selections can exist anywhere on an image, including completely outside the active layer's boundaries, which causes a conundrum when the user tries to apply an action to the selected area. I've now solved the problem for final adjustment and effect actions, but previews remain to be done. They are even messier on account of the dynamic scroll/zoom considerations.
I wanted to tackle this operation next because it requires a flat image, which is kind of a unique challenge. Fortunately, finding a Layers-friendly solution wasn't too difficult. Basically, the dialog now warns the user that it will flatten the image before proceeding (but the image can still be previewed to get an idea of how it would look at 8bpp). Previews work similar to the JPEG export dialog, by requesting a non-standard preview source - in this case, the composited image. I also cleaned up the code a bit, thanks to more familiarity with FreeImage these days, and added a progress bar so the user has some idea of how long the function may take.
Typed ID when I meant Index, ugh.
Arguably the easiest function to rewrite, as no pixel data has to be modified - just the virtual bounding box around the layer collection.
Technically, these functions are simple enough that they could be written using custom math. However they provided an easy way to test the new null-padding layer transformation system - which works beautifully!
This is key to fixing the Image menu. One of the big difficulties with Layers support is how to apply image-wide transforms. Some software outright disallows this (for example, GIMP does not allow arbitrary rotation without first flattening an image) because of the nasty math involved. But PD is better than that! My solution is as follows: before applying an image-wide transform, like "rotate image", we must first pad all layers with transparent pixels so that they are the size of the full image. Then, apply the transformation to each layer individually. When the transformation is complete, auto-crop all layers using this new "null padding trim" function. (In effect, look for fully transparent rows and columns, and remove them from the image.) My approach provides a universal way to process any full-image transforms, without needing ugly custom-transform code for each transformation. Hopefully this means I can get the Image menu back in working order sooner rather than later...
One of the great things about PD's Layers rewrite is that it forced me to rework a lot of the existing codebase, which in turn gave me an excuse to clean-up some of PD's oldest infrastructure bits. The new infrastructure makes it much easier for me to do complex things (like duplicate an image). Anyway, the new "Duplicate Image" function is significantly smaller than the old one, but with much better performance and elegance. It also supports full duplication of multilayered images.
Better results, better performance, everyone wins.
All transparency options should now work just fine with Layers. (Note also that all transparency options are now located in the Layers menu instead of the Image menu.)
After a bunch of profiling on my Win 7 PC, I can state with great confidence that the HighQualityBicubic interpolation mode is the fastest mode for downsizing 32bpp images via GDI+. I have no idea why this is, but many, many iterative tests confirmed it. Stranger still, in descending order, the fastest algorithms are as follows. (Testing done on a 6 megapixel, 5 layer image with tons of varying alpha.) NearestNeighbor (obviously) - < 10 ms per frame HighQualityBicubic - mid 40's HighQualityBilinear - mid 50's Bilinear - low 60's Bicubic - 150+ ms per frame Regular bicubic interpolation is some 4x slower than the high quality mode, which makes NO FRIGGIN' SENSE. Also, how badly do you have to write a bilinear interpolation algorithm to have it outperformed significantly by a bicubic one?? Anyway, I'm happy to put this matter to rest. It's possible that different hardware/software combinations could cause a different result, but I'm content to stick with HighQualityBicubic for now, especially since it makes the viewport noticeably smoother while also improving render quality while zoomed out.
Also, a number of fixes and edge-cases added to the menu enable/disable system.
More nuances to this than I expected, as you can have cases where the number of visible (and by extension, hidden layers) extends from 0 to numOfLayers, in any conceivable pattern. This function required new helper functions for counting visible/hidden layers, and those allowed me to knock out more "menu state synching" issues, e.g. only allow "Merge Visible Layers" if there is more than one layer visible.
Both Copy and Copy Merged are now available, and both work with selections active or inactive. (If no selection is present, Copy will copy the active layer, while Copy Merged will copy a composited version of the full image.)
There are now two supported Paste operations: - Paste as new layer - Paste as new image "Paste as new layer" supports nearly all the same features as "Paste as new image" - e.g. 32bpp data and file paths are handled just fine (including multiple file copies from Explorer; each file will be loaded as a new layer). The one exception is URL paths. This is not currently supported for "Paste as new layer", but it's on my to-do list.
There are pros and cons to including non-destructive actions like layer order changes in the undo/redo chain. At present, undo file creation remains slow on very large images, so I'm disabling it for basic "raise or lower layer" actions.
There's a ton of work to do on undo/redo performance (right now, a full copy of the entire layer stack is saved to file for every action), but it's quite unpleasant and complicated work. In the meantime, this commit will help a little bit. When working with undo data, PD now: - skips compression - skips checksum calculation (when saving) and reading (when loading)
Also, layer stack order changes now triggers Undo/Redo creation
It's fairly complicated, as things like layer position and count affect what options are available (e.g. no Merge options for single-layer images). Hopefully it's all working as it should...
All the layer menus are now ready to have processor calls plugged in.
Not completely, just yet - Layer transforms (resize, rotate, etc) will be done later. For now, I mostly wanted a framework so I could start plugging in various Layer menu actions.
Duplicate, Merge Down, Merge Up, and Delete Layer are now processed by PD's central processor, which means they trigger Undo/Redo handling, and can be recorded as part of macros.
…ing number e.g. "<base layer name> (2)". The new support function that handles this is generalized around any string, and it's smart enough to automatically detect an existing number. Thus a string like "<base layer name> (2)" gets incremented to "<base layer name> (3)", as opposed to "<base layer name> (2) (2)". I like how GIMP calls duplicate layers "<base layer name> Copy", "<base layer name> Copy 2", etc, but that approach has ugly translation implications, so for now I'm just going with a numeric indicator when duplicating layers.