Alpha Blended Splash Screen in Delphi

本文介绍如何使用Delphi创建带有Alpha混合透明效果的启动画面。通过详细步骤和代码示例,展示了不同类型的透明度实现方法,并重点介绍了UpdateLayeredWindow API的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 PhotoShophere, 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

Transparency, Opacity, Translucency

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):

Transparent
trans·par·ent [trans- pair- uh nt, - par-]
Capable of transmitting light so that objects or images can be seen as if there were no intervening material.
In other words; Transparent means invisible.
Translucent
trans·lu·cent [trans- loo- suh nt, tranz-]
Permitting light to pass through but diffusing it so that persons, objects, etc., on the opposite side are not clearly visible.
In other words: Translucent means partially transparent. Semi transparent is the same as translucent.
Opaque
o·paque [oh- peyk]
Impenetrable by light; neither transparent nor translucent.
In other words; Opaque is the opposite of transparent.

Different Kinds of Transparency

Color Key transparency

Color Key Transparency

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 andTForm.TransparentColorValue properties.

Uniform Translucency

Uniform Translucency

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 andTForm.AlphaBlendValue properties.

Alpha Blended Bitmap Translucency

Alpha Blended Translucency

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

Masked Transparency

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 Graphicsunit for an example.

Clipping

Clipping

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.

Transparent windows

Windows 95 - Window regions

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.

Example 1

Simple region based transparency

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.

Windows 2000 - Layered Windows

The concept of layered windows was introduced in Windows 2000 beta 3 with theWS_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: TheUpdateLayeredWindow 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.

SetLayeredWindowAttributes

The easy, and most limited, method to use layered windows is through theSetLayeredWindowAttributes 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 theSetLayeredWindowAttributes API.

UpdateLayeredWindow

Another, and more powerful, way to use layered windows is via theUpdateLayeredWindow 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 normalWM_PAINT mechanism to draw controls.

For this application we need alpha blended translucency and thus theUpdateLayeredWindow API.

Alpha Blending

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 unfortunatelyUpdateLayeredWindow is a bit of a snob and doesn’t eat regular alpha blended bitmap;UpdateLayeredWindow only works correctly with premultiplied alpha.

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.

OK, enough of the theory. Let’s try this stuff out.


Create the Image

The first thing to do is to create some stunning graphics for your splash screen. My artistic skills are somewhat, um, limited, so I usually just go with a logo or thematic symbol of some sort and apply some effects. Since we would like to show of the alpha blending, a fat drop shadow, some glow and maybe a lense flare is probably a good choice.

If you are lazy there’s plenty of free goodies you can use to get started. The important thing for now is that we should end up with an image that contains some semitransparent areas.

Creating a premultiplied bitmap in PhotoShop

With your PhotoShop image at hand you should now be ready to practice the black art of creating a premultiplied bitmap. From my research on this topic it appears to be a closely guarded secret known only to a select few ninja PhotoShoppers, as none of the instructions I have found worked for me. The following however works:

  1. Open or create a transparent image in PhotoShop.
  2. Merge Visible Layers (Shift+Ctrl+E).
    This flattens the image while keeping the transparency.
  3. Create a new Solid Color Fill Layer (Layer, New Fill Layer, Solid Color…).
    Set the fill color to black.
  4. Move the Fill Layer to the bottom (Shift+Ctrl+[).
  5. Auto-select the Image Layer (Ctrl+Click on the layer or Right-click on the layer+Select Pixels).
  6. Switch to the Channels tab and Save selection as channel (PhotoShop - The “Save selection as channel” button).
  7. Save the image as a BMP (make sure Alpha Channels is enabled and checked).
    Under Advanced Modes, select the 32-bit, A8R8G8B8 format.
  8. Presto!

With any luck you should now have created the magic bitmap. Now we just need an application to display it, so without further ado we move on to…

Displaying an Alpha Blended Bitmap

Ready? OK, fire up Delphi and… download the source code. I won’t guide you through the steps needed to create this tiny application. Instead let me explain the key points while you browse through the code:

The project file has been modified so the splash form isn’t created withTApplication.CreateForm. This is important because the first form that is created withTApplication.CreateForm automatically becomes the application’s main form and we don’t want that to happen. Why? Because the application terminates once the main form is destroyed. Instead the splash form is created anonymously and itsExecute method is called.

program AlphaSplashDemo;
...
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  with TFormSplash.Create(Application) do
    Execute;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Moving on to the splash form, the Position property has been set to poScreenCenterso the form will center itself on the screen and the FormStyle property has been set to fsStayOnTop so it will stay on top of other forms.

The OnFormClose event handler makes sure the form is destroyed when it it closed and the OnKeyPress event handler just makes it easier to close the form during tests.

procedure TFormSplash.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
 
procedure TFormSplash.FormKeyPress(Sender: TObject; var Key: Char);
begin
  Close;
end;


The WM_NCHITTEST message handler makes it possible to move the form even though it hasn’t got a caption or a border.

procedure TFormSplash.WMNCHitTest(var Message: TWMNCHitTest);
begin
  Message.Result := HTCAPTION;
end;

The Execute method contains the meat. In it we set the WS_EX_LAYERED window flag, load the bitmap and resize the window to fit the bitmap. Then the alpha blending parameters are prepared and UpdateLayeredWindow is called. Finally we start a timer to close the splash form after a short while. Don’t display the splash form for too long or you will just annoy your users.

procedure TFormSplash.Execute;
var
  BlendFunction: TBlendFunction;
  BitmapPos: TPoint;
  BitmapSize: TSize;
  exStyle: DWORD;
  Bitmap: TBitmap;
begin
  // Enable window layering
  exStyle := GetWindowLongA(Handle, GWL_EXSTYLE);
  if (exStyle and WS_EX_LAYERED = 0) then
    SetWindowLong(Handle, GWL_EXSTYLE, exStyle or WS_EX_LAYERED);
 
  Bitmap := TBitmap.Create;
  try
    Bitmap.LoadFromFile('splash.bmp');
 
    ASSERT(Bitmap.PixelFormat = pf32bit,
      'Wrong bitmap format - must be 32 bits/pixel');
 
    // Resize form to fit bitmap
    ClientWidth := Bitmap.Width;
    ClientHeight := Bitmap.Height;
 
    // Position bitmap on form
    BitmapPos := Point(0, 0);
    BitmapSize.cx := Bitmap.Width;
    BitmapSize.cy := Bitmap.Height;
 
    // Setup alpha blending parameters
    BlendFunction.BlendOp := AC_SRC_OVER;
    BlendFunction.BlendFlags := 0;
    BlendFunction.SourceConstantAlpha := 255;
    BlendFunction.AlphaFormat := AC_SRC_ALPHA;
 
    // ... and action!
    UpdateLayeredWindow(Handle, 0, nil, @BitmapSize, Bitmap.Canvas.Handle,
      @BitmapPos, 0, @BlendFunction, ULW_ALPHA);
 
    Show;
  finally
    Bitmap.Free;
  end;
  // Start timer to hide form after a short while
  TimerSplash.Enabled := True;
end;


The Splash Screen Demo

When you run the demo application, with the sample bitmap, it should look something like this:

You can replace the bitmap I have provided with your own. Just copy it to the demo folder and name it splash.bmp.

What’s next?

In this part we used a windows bitmap for the splash screen. While this works just fine, 32-bit bitmaps can easily get quite big (the demo bitmap is almost 1Mb in size) and they add a considerable overhead to an application - with little benefit to the end user. Wouldn’t it be nice if we could compress the bitmap to minimize the overhead?

In the second part of this article, I will modify the demo application to use a compressed PNG image instead of a BMP. I will also move the bitmap to a resource file, demonstrate run-time premultiplication and enhance the splash screen with a few visual gimmicks.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值