RAD Studio 10.3 includes a lot of work on the VCL, our native Windows UI library. In this release, we’ve focused heavily on extensive high DPI support. You can read Marco’s blog about the perMonitorV2 support in the VCL, and this means with 10.3 you can have any VCL application scale and render well on scaled or high DPI screens.
One key component of high DPI is scaling graphic resources, when drawing toolbars, menus, buttons, and anywhere else that you use glyphs. If you are using a 16x16 pixel icon on your toolbar, you will want that to scale up to a 32x32 pixel icon when the application is running at 200% scaling. Despite the high DPI APIs Windows has added, the underlying image list in Windows, which is what the TImageList component wraps, has no inbuilt scaling support. This means that when you have a form that changes scale when the DPI changes, it is difficult to scale toolbar or other images using TImageList, without some form of complex add-on code to repopulate with new images. Yet, VCL controls, since they often are genuine native Windows controls, are closely tied to using Windows image lists and a HIMAGELIST handle for drawing.
In Delphi and C++Builder 10.3, we have added an entirely new image list control. It automatically supports scaling based on high DPI, so your forms will always draw with crisp, high-res images no matter the monitor or scale. It is also fully compatible with existing VCL controls and with Windows image lists, even providing a HIMAGELIST handle for use with WinAPI calls.
Sound like magic? Read on!
The new image list is actually made with two components: an image collection, which is simply a container for multi-resolution images; and the image list, which connects to the collection and presents the images at a specific size.
The image collection (TImageCollection) is straightforward. Place it anywhere, such as a form or data module. You can then add images to it. The collection can hold multiple resolutions of a single image: for example, you might load a 16x16, 32x32 and 128x128 bitmap, which are regarded as multiple sizes of a single logical image.
You can add different resolutions to a logical image manually, but if you have a folder with many images including multiple resolutions of the same image, the collection will automatically detect that based on similar filenames plus an image size designation. You can tweak this by changing the Size Separator character. For example, loading three files ‘foo-16.png’, ‘foo-32.png’ and ‘bar-16.png’ will create two logical images, the one for foo containing both 16x16 and 32x32 versions. The predefined characters follow common size naming conventions used by popular icon libraries, so if you buy an icon pack, you should find it very easy to import.
The collection supports PNG images and 32bpp bitmaps (a BMP file with an alpha channel.) Any non-32bpp bitmap is treated as a traditional colorkeyed bitmap with a single transparent color, as used in the old TImageList component. You can give images a name and category, which is useful for organisation.
The end result is a collection of alpha-aware, multi-resolution images.
The TVirtualImageList component presents images at a specific size and the current DPI/scale.
The component connects to an image collection. You can edit the image list and add images from the collection to it, or have it automatically contain all images in the collection via the AutoFill property, and it has some other useful settings, such as settings to draw disabled images better than the traditional image list (you can make them grayscale and/or semitransparent.)
The key for the image list is that it presents the images it uses at a specific size, and that size is in pixels at 100% scaling. Previously, you would have specified an image list holds images at 16x16. For the virtual image list, you specify the Height and Width the same, but the meaning is different - they are the height and width at 100%.
High DPI scales per-window. That means that different forms can have different scaling applied. It also means that scaling (when the image list needs to scale based on DPI changes) is based on the form, or rather on the form’s scale. Therefore, a virtual image list should always be placed on a form (never on a data module, since as a non-visual control a data module cannot scale.) It then picks up the scaling from the form’s current scale, and applies that automatically to the images it draws, which are sourced from the collection.
This is where we return to the height and width being defined at 100%. If the form a virtual image list is on is at 200% scaling, a 16x16 image list (Height and Width are both 16) will draw icons at 32x32, because 32 is 200% of 16.
The image list draws high quality images. Images are chosen from an exact match, if one is available, or if not from a higher-resolution image and scaled down using a high quality scaling algorithm. Images are cached, so the resizing work is done only once per image. It inherits from TCustomImageList, and has a HIMAGELIST handle, meaning you can use it with WinAPI methods if you need.
An image collection includes tools to help convert from old image lists to the new image list. Right-click on a collection and you will see the ability to import image from multiple old TImageList-s at once.
There is extensive documentation on the image collection and virtual image list in our online docwiki. This includes a far more extensive writeup on the architecture and components than in this blog post, as well as a guide to migrating images and best practices.
Here are some screenshots of a small image list demo app. At 100%:
And at 800%:
You can see that images remain crisp and clear.
The new high DPI image list controls (TImageCollection and TVirtualImageList) provide adaptive DPI-aware high-quality automatically scaling images, fully backwards compatible with traditional TImageLists, including importing old-style bitmaps and lists. Combined with the perMonitorV2 high DPI support plumbed through the entire VCL, also in 10.3, you should be able to create great visually high-quality scaling apps using RAD Studio 10.3.