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.
[Update 2008-05-27] Part 2 has been posted: Alpha Blended Splash Screen in Delphi - Part 2
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 TForm.TransparentColor
and TForm.TransparentColorValue
properties.
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 TForm.AlphaBlend
and TForm.AlphaBlendValue
properties.
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 SetWindowRgn
and 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.
Windows provides us with two, mutually exclusive, ways to implement layered windows: The UpdateLayeredWindow
API and the SetLayeredWindowAttributes
API. I’ll get to these shortly.
A layered window can be created either by specifying the WS_EX_LAYERED
style when creating the window, or by setting WS_EX_LAYERED
using SetWindowLong
after the window has been created.
In Delphi a custom layered window is best created with SetWindowLong
since 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 SetLayeredWindowAttributes
API.
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 UpdateLayeredWindow
API.
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.
The reason 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.
Thanks from Argentina!
I'm looking for this a long time and with this article finally can understand something.
PD: sorry for my poor english
This AlphaBlending is REALLY GREAT!!! I've been looking for it ever since Konfabulator (Yahoo Widgets) came out!! You have saved my life!
The only thing I need now is to put other components onto the form. I completely undertsand why you can't but there must be a way …
How can this be done?
Thanks Anders. Can you explain how similar effects (alpha-blending) could be achieved for windowed controls (e.g.
TPanel
)?UpdateLayeredWindow
and friends only work on top level windows.For alpha blended controls, you'll have to resort to third party libraries.
Thanks heaps, this will help with my problems with trying to find a way to do this!
Waiting in anticipation on being able to use pngs to do this!
Any word on how far away part 2 will be?
I've been busy with the Drag and Drop Component Suite, but now that that's out the way I hope to get the last part finished within the next two weeks.
Part 2 has been posted.
For those dudes who will use it in Delphi 7:
To compile this demo using Delphi 7 you need to remark line
// Application.MainFormOnTaskbar := True;
Then, after message
RLINK32: Too many resources to handle
delete AlphaSplashDemo.res and delphi will recreate it.
Hi, I’m still having issues on Delphi7 with the line:
I have followed these steps but still
RLINK32: Too many resources to handle
Does anybody offer any solutions?
Regards ^^
Hi, First I want to thank the examples that you have placed on the web. But I have a doubt. How can I do to show the controls that I put on Splash form. For example buttons, labels and so on. The picture always hide my components.
I'm trying to make a splash and login screen in a single Form.
Thanks.
Olá vc conseguiu fazer aparecer os controles no form? se sim, poderia me passar como fez?
Muito Obrigado
Ivan
Rodrigo,
I also pointed this out, and unfortunately you must download or buy third-party components to do this, such as AlphaControls.com (freeware).
I will at some stage post a link of the farthest I have been to doing this. I made a little widget that shuts down your computer and performs other related tasks.
Will let you guys know…
very good, like, I would like to know if there is like putting buttons on that form, I would like to create a floating toolbar equal the http://rocketdock.com/
Good indeed, if you don't own the FXlib VCL:
"FXLib is an easy-to-use set of components that enables you to create amazing splash screens, form and image transition effects, using all the power of its advanced rendering engine. Over 70 families of animation and transition effects with hundreds of variations. Real-time rendering. WYSIWYG editing in both design-time and run-time, preview"
http://www.ksdev.com/
FXLib (by ksdev) does not support translucent PNG splash screen. It only masks, not the same thing…
Hey Anders!
Could you show some screenies in the photoshop tutorial, where you make the picture ready for the alpha blending? I am kinda stuck there. If you could show a screenie for each step it would be great!
Thanks!
A screen shot for each step? No, I don't think so. The steps are very simple and the description should be enough to get you through. Sorry.
Hi there, Anders I must say that i almost get what i wanted, i mean, i got the transparency and everything goes ok, but i still have the problem that i can not put standard controls on the layered window. My question is…
is there any way to do that??…at least any "triky" way or something?? I have searched through the inet and some people say something about making a "fake" window over the original or something about to draw our own controls, for example by sending the appropriate
WM_PRINT
messages with the correct DC, anyway, can you give me some tip about it???Thanks for such a great paper
Depending on what you need there are easier ways to get a transparent form.
1. Drop a TImage and load a bitmap BMP. The parts of the image that should be transparent should have a specific color e.g. clFuchsia.
2. Set the TImages Transparent property to True.
3 Set the Forms TransparentColor property to True, the TransparentColorValue to clFuchsia and the Color property to clFuchsia.
4. Set the Position, BorderStyle etc. properties on the form as you please.
If the TImage covers the entire form there's no need to set the forms color property. This does not seem to work with PNG images regardless if they are alpha-channel transparent or if they use a specific transparent color like clFuchsia so as far as I can tell your only option is to use BMPs.
Um… The whole point of this article is that the output is transparent and alpha blended.
The TForm.TransparentColor method, which I mentioned above in the section "Different Kinds of Transparency, Color Key transparency", does not support alpha blending.
Anders,
Of course, but I expect that many that just want a transparent form will read this article (as I did). It is not obvious that using transparentcolor does work with BMP but not with PNG (no alpha transparency used). This must be considered to be a bug in the VCL.
I just tried it with a PNG in Delphi XE. It worked fine.
Really? I had a test project that used a BMP that worked fine. I opened the BMP and re-saved it as PNG in Paint.NET and loaded that into the
TImage
. TheclFuchsia
color appeared then. Have you done any other steps?Color
=clFuchsia
,TransparentColor
=True
andTransparentColorValue
=clFuchsia
.TImage
control on the form and setAlign
=alClient
andStretch
=True
.TImage
.Be aware that any semi-transparent pixels in the PNG will appear as blended onto
clFuchsia
, so you need to make sure that all PNG pixels are either 100% opaque or 100% transparent.Thanks Anders,
I got it to work now. I must have messed it up when editing the image in Paint.NET.
After some time of researching, I finally have found a way to place child controls on layered windows. Thanks to this code of Jerry.Wang, I have reached my goal. You can read his article on codeproject.com for better understanding, but anyway, the whole point is this.
[edit: Excessive quote snipped]
Sorry for such a big comment, but I think this may be of help to someone as it was to me.
One more time, thanks Anders, for such a great paper, it is worthy of reading, thanks, bye.
thanks
Thanks, but i am searching for a TPanel or TLabel that has such alphablend.
I mean: A non transparent form and over it some Panels, one of them must let show what is under it.
For example: A Tipical Panel with "drawing in progress…" over the long time taking drawing proccess on the TForm.Canvas… so while the graphic is been painting the TPanel is shown, but lets see though it the TForm canvas.
Imagine you have a very long process that creates a curve on the TForm.Canvas… while drawing it you need to make Visible a TPanel, but you want to see what is under it with some alphablend, not opaque, not transparent.
So i need a way to add something like AlphaBlend properties of a TForm to a TPanel.
Hope it can be understand.