Part 2 really is coming, and I’m sorry it’s taking so long. In the meantime, here is a quick post, Part 1 1/2, addressing a couple of questions asked in the comments for Part 1.
GDI+ is great, and is a good and easy way to render good-looking graphics. I don’t want to disparage it. However, it does have some flaws, and I’m starting to think that including it in the software I work on was a mistake. The main problem is speed: it’s mostly CPU-bound on Vista and Windows 7, and often on XP too. From memory, and I don’t believe this is documented on MSDN, GDI+ only makes use of hardware acceleration through GDI hardware acceleration, and it can only do so when its coordinates are integer wholes. Using floating-point coordinates for drawing such as RectF, PointF etc will prevent this.
If I was to implement the same feature back then (2006? 2007?) with the knowledge I’ve accumulated since, I would use GDI techniques like the ones in this series.
If I was to implement the same feature now, assuming I had XE2, I’d probably use FireMonkey embedded in the VCL application. FireMonkey supports hardware acceleration, zooming or scaling etc, all built in. This particular feature was a zoomable user interface for a specific part of the software I work on, and speedy, fast zooming is an essential feature.
Non-rectangular shapes, such as selections
Commenter Ulrich asked about drawing a non-rectangular selection. There are two ways to do this:
- Draw a polygon on a 32-bit bitmap. It will require per-pixel alpha so that the areas outside the polygon are fully transparent, so the transparency loop (iterating through the pixels and premultiplying the alpha) will have to be modified to handle this.
- Draw and keep one solid rectangular non-per-pixel bitmap, and instead use clipping regions to restrict where GDI can blend the image. The basic idea is that you can make GDI clip its drawing to an arbitrary shape, so create a region in the shape of the selection (probably using CreatePolygonRgn) and use IntersectClipRgn to set the device context’s clipping region to the combination of its existing region (normally, the whole visible area) and your polygonal region. When you blend in your rectangular image, it will only appear within the polygonal shape set as the clipping area. Don’t forget to set the DC’s clipping region back afterwards. CodeProject has a good article about using clipping regions.
A selection is normally ‘live’, that is, the user sees it update as the mouse moves. This means that the drawing and alpha premultiplication has to be done many times a second. Method 1, using per-pixel alpha, is what I use in my own software, and it seems to be fast enough with a well-optimised premultiplication method. Method 2, using clipping regions where the region is updated on the fly instead of the bitmap, is almost certainly going to be faster. I haven’t tested this and it’s usually not wise to say ‘technique X will be faster’ without measuring, but I’m fairly sure. If I was implementing the feature today instead of years ago this is what I’d investigate. Measure and decide.
So there you go – my apologies for the short post and absence of code samples. Part 2 of the series really is coming “soon”.