views:

1367

answers:

2

I asked a question about this some years back when Vista was first released, but never resolved the problem and shelved it as something to consider later.

I have a splash screen that I went to great effort to make look great. It's a 32bpp alpha-blended PNG. I have some code (which I can dig up if required!) that works great under Windows XP or under Vista+ when desktop composition is turned off. However, under Vista+ all the transparent parts are black, destroying everything that looks great about it!

So, my question is this: as anyone been able to display a 32bpp alpha-blended PNG as a splash screen in a way that works both with and without desktop composition activated? I'm not adverse to using third-party components if required, free or otherwise.

Ideally, this would work in Delphi 7.

Update: Besides the answers below, which work very well, I found that the TMS TAdvSmoothSplashScreen component also handles this task very well, if somewhat more complex.

+4  A: 

Tim, I just tried this on Vista/D2007 with 'Windows Classic' theme selected:

Alpha Blended Splash Screen in Delphi - Part 2 http://melander.dk/articles/alphasplash2/2/

no black background that I could see... it still looks great.

Bob S
+1. I followed that article recently (parts 1 and 2) and can confirm it works on Vista Aero.
Mike Sutton
Hrm. It seems to almost work under Aero for me. For reasons unknown, the window border appears. Compiled under Delphi 7; does this happen for anyone else?
Tim Sullivan
Yes, even looks betterer with Aero on... :)Are your drivers current?
Bob S
Yep, as current as they can be! To be clear, it's not a black border, it's as if it's a normal form, with minimize, maximize and close buttons, a title bar and whatnot.
Tim Sullivan
Per a comment below, setting BorderStyle to bsNone fixed the visual problem! Thanks!
Tim Sullivan
+3  A: 

The article Bob S links to gives the correct answer. Since that article contains quite a bit extra information that you actually need, here is the form/unit I create through it (Note that you'll need the GraphicEx library from here:

unit Splash2Form;

interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, ExtCtrls, GraphicEx;

type
    TSplash2 = class(TForm)
    private
     { Private declarations }
    procedure PreMultiplyBitmap(Bitmap: TBitmap);
    public
     constructor Create(Owner: TComponent);override;
     { Public declarations }
     procedure CreateParams(var Params: TCreateParams);override;
    procedure Execute;
  end;

var
  Splash2: TSplash2;

implementation

{$R *.dfm}

{ TSplash2 }

constructor TSplash2.Create(Owner: TComponent);
begin
  inherited;
  Brush.Style := bsClear;
end;

procedure TSplash2.CreateParams(var Params: TCreateParams);
begin
    inherited;
end;

procedure TSplash2.Execute;
var exStyle: DWORD;
    BitmapPos: TPoint;
  BitmapSize: TSize;
  BlendFunction: TBlendFunction;
  PNG: TPNGGraphic;
  Stream: TResourceStream;
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);

  PNG := TPNGGraphic.Create;
  try

      Stream := TResourceStream.Create(HInstance, 'SPLASH', RT_RCDATA);
      try
       PNG.LoadFromStream(Stream);
    finally
     Stream.Free;
     end;

    PreMultiplyBitmap(PNG);

      ClientWidth := PNG.Width;
    ClientHeight := PNG.Height;

      BitmapPos := Point(0, 0);
    BitmapSize.cx := ClientWidth;
      BitmapSize.cy := ClientHeight;

      // 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, PNG.Canvas.Handle,
      @BitmapPos, 0, @BlendFunction, ULW_ALPHA);

      Show;

  finally
    PNG.Free;
  end;
end;

procedure TSplash2.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;

end.
Mike Sutton
Again, I have the same problem as above: in Vista Aero, it has the window border around it. Compiled in Delphi 7. Thoughts?
Tim Sullivan
Did you set the TSplash2.BorderStyle to bsNone? That should remove the window border completely.
skamradt
Try changing the border style to bsNone. If that doen't work I'll have another look when I'm at my work machine later.
Mike Sutton
Changing the border style fixed it! Thanks!
Tim Sullivan