There is nothing wrong with your television set. Do not attempt to adjust the picture.
1 Apr 2008
In this first of two articles, I will demonstrate how to easily create an alpha blended translucent splash screen using Delphi.
Although I use Delphi 2007 and PhotoShop here, the techniques apply equally well to other versions of Delphi and other image editing tools.
There are many different ways to implement and use transparency. In this tutorial we will use only one kind of transparency; Alpha Blending, and one implementation; The
UpdateLayeredWindow API. Before we get our hands dirty with some code, I will introduce the most common kinds of transparency, but first we need to define some terms (definitions courtesy of dictionary.com):
Color Key transparency is the simplest form of transparency. Transparency is accomplished by defining one color that wont be drawn when the window is rendered onto the screen. Color Key transparency does not support alpha blending, but it can be combined with Uniform Translucency to soften the edges of the image.
Delphi support Color Key transparency with the
Uniform Translucency controls the opacity of the window by applying a single alpha blend value to the whole window. This is often used to make a window semitransparent while it is being moved. It can also be used to create a fade-in/fade-out effect, but the
AnimateWindow API is better suited for that purpose.
Delphi support Uniform Translucency with the
With Alpha Blended Bitmap Translucency (or just Alpha Blending for short), each pixel in the source bitmap is accompanied by its own individual transparency value. The transparency values are known as the Alpha channel and is usually of the same depth (bit size) as each of the color channels.
Masked Transparency basically works the same way as Color Key transparency. The transparency is specified with a 1-bit bitmap called the mask.
Color Key transparency is usually implemented by creating a mask from the source bitmap. See the
CopyBitmapAsMask function in the
Graphics unit for an example.
Clipping really isn’t a kind of transparency, but it can be used to give the illusion of transparency by altering the normal rectangular shape of a window.
See example 1 below for an example of how to apply clipping to a window.
Windows has supported simple window pseudo transparency since Windows 95 by means of clipping. Transparency with clipping is accomplished using the
CreatePolygonRgn APIs and while clipping based transparency is relatively simple to implement, the results are also very crude.
Simple region based transparency
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
interface type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); protected procedure WMNCHitTest(var Msg:TMessage); message WM_NCHITTEST; end; implementation procedure TForm1.FormCreate(Sender: TObject); begin BorderStyle := bsNone; SetWindowRgn(Handle,CreateEllipticRgn(10,10,Width-10,Height-10), False); end; procedure TForm1.WMNCHitTest(var Msg:TMessage); begin Msg.result := HTCAPTION; end;
Although more complex shapes can be made by combining a gazzilion small rectangles into a region, the results still aren’t very pretty and performance is poor. The visual appearance suffers from the lack of alpha blending between the window and the background, and the performance suffers because of the conversions that has to take place between the vector based regions and the bitmapped display device.
The concept of layered windows was introduced in Windows 2000 beta 3 with the
WS_EX_LAYERED window style. Layered windows not only support alpha blended bitmap translucency, but also the more simple uniform- and color key translucency.
A layered window can be created either by specifying the
WS_EX_LAYERED style when creating the window, or by setting
SetWindowLong after the window has been created.
In Delphi a custom layered window is best created with
TForm’s own transparency support messes with the
WS_EX_LAYERED flag when the window is created.
The easy, and most limited, method to use layered windows is through the
SetLayeredWindowAttributes API. SetLayeredWindowAttributes causes the window to redirect the normal,
WM_PAINT based, drawing of the window into an off-screen bitmap, where the desired effect is applied before the bitmap is drawn onto the screen.
The advantage of
SetLayeredWindowAttributes is that it can be applied to existing code with little or no modifications. The limitations are that it only supports uniform- and color key based translucency
Delphi’s native form transparency is implemented with the
Another, and more powerful, way to use layered windows is via the
UpdateLayeredWindow API. Where as the
SetLayeredWindowAttributes API depends on the application’s regular handling of
WM_PAINT messages, the
UpdateLayeredWindow API completely does away with them; When using
UpdateLayeredWindow the application doesn’t need to handle
WM_PAINT or other painting messages. Instead the application must provide
UpdateLayeredWindow with a 32-bit alpha blended bitmap and the desired color key and transparency values.
The primary advantage of
UpdateLayeredWindow is the support for per-pixel alpha blending. The disadvantage is that the application cannot rely on the normal
WM_PAINT mechanism to draw controls.
For this application we need alpha blended translucency and thus the
Typically a color bitmap image has three channels: Red, Green and Blue (RGB). In the case of a 24 bit bitmap, each pixel is divided into three channels of 8 bits each; 8 bits for Red, 8 bits for Green and 8 bits for Blue. An Alpha Channel is just like any one of the RGB color channels, except its value doesn’t represent a color value, but rather a transparency value. In most alpha channels a value of zero means completely transparent while a value of 255 means completely opaque or not transparent. Any value in between means partly transparent. Since the color channels requires 24 bits and the Alpha channel uses 8 bits, we need 32 bit per pixel for a color bitmap with an Alpha channel.
Most image editing tools can save 32 bit windows bitmaps with an alpha channel (also known as RGBA, “A” being the Alpha channel), but unfortunately
UpdateLayeredWindow is a bit of a snob and doesn’t eat regular alpha blended bitmap;
UpdateLayeredWindow only works correctly with premultiplied alpha.
Premultiplied Alpha means that the Red, Blue and Green color channels have already been multiplied with the Alpha channel. The premultiplication is performed after the following formula: Color = Color * Alpha / 255. Or to put it another way:
Red := MulDiv(Red, Alpha, 255); Blue := MulDiv(Blue, Alpha, 255); Green := MulDiv(Green, Alpha, 255);
This calculation is fairly easy to perform at run-time, and I will show you an example of how to do it in a later tutorial, but I will also demonstrate how to create a premultiplied bitmap in PhotoShop so you don’t have to fiddle with the pixels at run-time.
UpdateLayeredWindow requires premultiplied alpha is probably to improve run-time performance by moving an operation, that has to be performed under all circumstances, from run-time to design-time. In my opinion it would have been nice if the API had also supported regular alpha blended bitmaps. After all, on modern hardware, it only takes a few µS to premultiply a large bitmap at run-time.