Alpha Blended Splash Screen in Delphi - Part 2

In this the second, and concluding, part of our experiments with Alpha Blended forms in Delphi, I will modify the demo application to use a compressed alpha transparent 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.

If you haven’t read part 1 yet I strongly suggest you do so first: Alpha Blended Splash Screen in Delphi - Part 1. This part will be right here waiting for you when you get back.

For this part I have replaced the sample bitmap with another one. Mostly because the original was too big but also because I didn’t quite like the look of it. The new one is just a grid of 15 colored glass orbs. Not very meaningful, but I love glass orbs :-)

Each of the techniques we try out in this part builds on the source code of the previous steps. If you don’t want to enter the code manually or copy/paste from the article, you can “cheat” and download the complete source.

Loading the bitmap from a resource

The first step in improving our demo application is to get rid of the external bitmap file. Instead of reading the splash bitmap from an external file, we move the bitmap to a resource file, link it into the application and read it from there instead.

Resource files

A resource file, most commonly seen with the file extension .res, is a container for resources such as strings, bitmaps, icons, cursors, forms and dialogs. Resource files are rarely used by themselves. Instead they are linked into the applications or DLLs that uses them.

Resource files can be created with specialized resource editors such as Colin Wilson’s opensource XN Resource Editor or a commercial tool such as Resource Builder. Or if you are really desperate, the crummy, buggy imagedit.exe that came with Delphi up to Delphi 2006 or the equally buggy, 20 year old Resource Workshop. The upcoming Delphi 2008 is supposed to (finally!) include some sort of integrated resource management.

A resource script in the Delphi Project Manager

You can also skip the resource editor and let Delphi create the resource file for you. Delphi comes with a resource compiler brcc32.exe that can be used to compile resource script files into binary resource files. All you have to do is include the resource script file in your project and Delphi will take care of the rest.

The resource script is just a plain text file with the file extension .rc. The complete resource script syntax is beyond the scope of this article (also, I can’t find the documentation for it anymore), but for our purpose it is very simple; Each line of the file lists the name of the resource, the resource type and the source filename of the resource.

Normally the resource type for a bitmap would be BITMAP, but because our bitmap is a bit special (remember it’s a 32 bit bitmap with premultiplied alpha) and the BITMAP resource type is reserved for “plain vanilla” bitmaps, we have to use the RCDATA resource type. RCDATA is the resource equivalent of a BLOB in a database; It’s a data type that can be used to store anything you like. For example Delphi’s forms (the DFM files) are stored as RCDATA resources.

Loading the bitmap

With me so far? OK, let’s try it out:

  1. Create a text file with the following content and save it as splash.rc.
    1
    2
    
    // Name                 Type	Filename
    SPLASH                  RCDATA  "splash.bmp"
  2. Next, add the resource script file to your Delphi project.
    Delphi should now add the following code to your project file:

    {$R 'splash.res' 'splash.rc'}
  3. Modify the application to load the bitmap from a resource instead of from an external file:
    procedure TFormSplash.Execute;
    var
      Stream: TStream;
      ···
    begin
      ···
      Bitmap := TBitmap.Create;
      try
        // Bitmap.LoadFromFile('splash.bmp');
        Stream := TResourceStream.Create(HInstance, 'SPLASH', RT_RCDATA);
        try
          Bitmap.LoadFromStream(Stream);
        finally
          Stream.Free;
        end;
        ···

    You might wonder why we don’t just use TBitmap.LoadFromResourceName(). The reason is simply that LoadFromResourceName is hardwired to only load the BITMAP resource type and thus can’t be used to load our RCDATA resource.

  4. Save, compile and run.
    Once you compile the project, Delphi should automatically compile the splash.rc resource script into the splash.res resource file and link it into the application.

Run-time premultiplication

As you might remember from the first part, I promised to show you how to premultiply the bitmap at run-time. Premultiplication requires us to perform the following simple transformation on each pixel in the bitmap: Color = Color * Alpha / 255. The following function does just that:

procedure PremultiplyBitmap(Bitmap: TBitmap);
var
  Row, Col: integer;
  p: PRGBQuad;
begin
  for Row := 0 to Bitmap.Height-1 do
  begin
    Col := Bitmap.Width;
    p := Bitmap.ScanLine[Row];
    while (Col > 0) do
    begin
      p.rgbBlue := p.rgbBlue * p.rgbReserved div 255;
      p.rgbGreen := p.rgbGreen * p.rgbReserved div 255;
      p.rgbRed := p.rgbRed * p.rgbReserved div 255;
      inc(p);
      dec(Col);
    end;
  end;
end;

Since there’s only 256 possible color values per channel and only 256 possible alpha values, we can optimize this process by sacrificing a little memory (256*256 = 64Kb) and replace the calculations with a table lookup. Here’s the optimized version:

procedure PremultiplyBitmap(Bitmap: TBitmap);
var
  Row, Col: integer;
  p: PRGBQuad;
  PreMult: array[byte, byte] of byte;
begin
  // precalculate all possible values of a*b
  for Row := 0 to 255 do
    for Col := Row to 255 do
    begin
      PreMult[Row, Col] := Row*Col div 255;
      if (Row <> Col) then
        PreMult[Col, Row] := PreMult[Row, Col]; // a*b = b*a
    end;
 
  for Row := 0 to Bitmap.Height-1 do
  begin
    Col := Bitmap.Width;
    p := Bitmap.ScanLine[Row];
    while (Col > 0) do
    begin
      p.rgbBlue := PreMult[p.rgbReserved, p.rgbBlue];
      p.rgbGreen := PreMult[p.rgbReserved, p.rgbGreen];
      p.rgbRed := PreMult[p.rgbReserved, p.rgbRed];
      inc(p);
      dec(Col);
    end;
  end;
end;

My tests show the optimized version to be 2-3 times faster. The function still has plenty of optimization opportunities but since it’s already fast enough, further performance gains doesn’t seem worth the resulting obfuscation of the code. I clocked the above function to 5 mS with the sample bitmap.

To try it out, go back to your original PhotoShop image and save it as a normal 32-bit bitmap with an alpha channel:

  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. Auto-select the Image Layer (Ctrl+Click on the layer or Right-click on the layer+Select Pixels).
  4. Switch to the Channels tab and Save selection as channel (PhotoShop - The “Save selection as channel” button).
  5. Save the image as a BMP (make sure Alpha Channels is enabled and checked).
    Under Advanced Modes, select the 32-bit, A8R8G8B8 format.

Now modify your application to perform premultiplication on the bitmap after is has been loaded:

    ···
    Bitmap.LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
 
  ASSERT(Bitmap.PixelFormat = pf32bit, 'Wrong bitmap format - must be 32 bits/pixel');
 
  // Perform run-time premultiplication
  PremultiplyBitmap(Bitmap);
 
  // Resize form to fit bitmap
  ClientWidth := Bitmap.Width;
  ClientHeight := Bitmap.Height;
  ···

Before you compile you should delete the old splash.res to force the resource compiler to recompile with your new bitmap.

Give it a go before we move on.