Jump to content
CyberPeter

Modern way to create splash screen from transparent png with edges that blend with the background

Recommended Posts

My app has a splash screen, but it was created at least 16 years ago (BCB5 I think) and I'm really ready for something else.

It's based on two BMP's.  One is used as a mask to tell the code what should be transparent.

 

Instead of hurting my (currently covid impaired) head trying to figure out the old code and ancient methods of then, surely VCL Forms and graphic support has evolved to something where this is a lot easier to do ?

I recently upgraded to C++ Builder 11 (coming from Builder 2009)

 

I was thinking of finding a graphical artist who can make me a nice *.png that I then use as splash screen.  It's far easier to ask someone to make a nice PNG than to describe the need for two BMPs, one of which acts purely as a mask etc.

 

I tried a few things with a free test.png found on the net (artist unknown), but I'm not having the easy success I hoped I'd have.

 

Are there good and easy ways to do this using Builder / Delphi 11 ?

I looked around but wasn't very successful in finding documentation.  I see Anders has a couple blog posts about it, but that too seems outdated (correct me if I'm wrong).

 

The easy:

 

- I created a Windows VCL test app

- Set Form BorderStyle = bsNone

- Plonked a TImage on the form (Align = allClient)

- Loaded the test.png in Image1's Picture at design time

- Compiled.   Works nicely but of course the form color shows where the PNG is transparent

- I then set the Form background to MoneyGreen, TransparentColor = true, TransparentColorValue = clMoneyGreen

- Compiled.  Nice result but the PNG half transparent regions blend with the green color, and are hence not blending with the background.

- The net result is sadly not perfect.  It was simply too easy 😉  I made this screenshot on a white background.

 

image.png.2488a5f90495fff2e14a7b71c71e7450.png

 

If I set AlphaBlend = true and give it an AlphaBlendValue = 100 (for instance) there are certainly very nice additional features to play with:

 

image.png.e15276685f98953883a4cdafc2838265.png

 

Now, this doesn't need to be total failure. 

- I could experiment with different Form background colors and see how they interact with the half transparent fields of the PNG.  Perhaps the net result is pleasing.

- I could ask the artist to make me a transparent PNG but with well defined edges that don't blend, that are not half transparent.

 

In conclusion, as far as super easy methods go, this certainly is not a bad start if a good color compromise can be found for the blending issues and/or depending on the PNG.

 

But ... perhaps I'm missing quite a few things that can be done to achieve a perfect result ?

Please let me know.

 

Thanks.

The test.png also attached:

 

test.png

Edited by CyberPeter

Share this post


Link to post

Another thing you should consider is support for High-DPI. You would have to make several version of the picture in order to have good results in every possible combination of DPI settings.

 

That's the reason I've retired the old login screen with fancy graphics and went back to the classic rectangular one with svg images.

Share this post


Link to post

image.thumb.png.d271e2aeda6d2dd5fe4e216109d92cd2.png

 

Source attached.

 

I use this code in most of my open source/freeware tools.

 

Features:

  • Fades image in/out on show/hide.
  • Can be moved with the mouse.
  • Automatically hidden when deactivated.
  • A text can be displayed on top of the image.
  • Text can be scrolled to create vertical banner.
  • Can play a audio resource while visible.
  • Can also be used as an "About box".

image.png.b4fa2a66f7da1c6063108f18ae8a8304.pngimage.png.249d4d0d69c01a6b558803e46c3998c7.png

Things to note:

  • Splash does not "stay on top" when running in the debugger. This is by design.
  • Doesn't handle HighDPI scaling well (image isn't scaled).

SplashDemo.zip

  • Like 2

Share this post


Link to post
16 hours ago, Lajos Juhász said:

You would have to make several version of the picture in order to have good results in every possible combination of DPI settings.

Oh cr@p, good point.  Not doing that though 😉

 

15 hours ago, Anders Melander said:

Source attached.

Oh wow, thank you very much Anders.

I'm not good with Delphi (at all), so I will need some time to digest this.

Share this post


Link to post
On 3/18/2022 at 8:05 PM, Anders Melander said:

 

Source attached.

I built and played a bit with your code Anders and it works nicely.

 

Stubborn as I am I created a smaller class with only a splash, highly inspired by your code (also my old code so it seems, there is certainly overlap - but now I'm able to ignore a lot of old crap and rely on PNG).  Sadly my today's implementation doesn't work fully.  I must be missing something, but I cannot spot it.  Perhaps it's obvious ?

Anybody any idea as to why UpdateLayeredWindow() always fails with error 87 ( ERROR_INVALID_PARAMETER ). I can't seem to find where the problem lies
 

//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
String ResourceName(L"SPLASH2") ;

TSplashScreen *Splash = new TSplashScreen(3000, 100, ResourceName) ;

Splash->Show() ;

}
//---------------------------------------------------------------------------


 

//---------------------------------------------------------------------------
__fastcall TSplashScreen::TSplashScreen(int TimeToDisplay, int Alpha, const String &ResourceName)
    : TForm((TComponent*)NULL), FBitmap(NULL), Alpha(Alpha)
{

    Timer->Interval = TimeToDisplay ;
    Timer->Enabled  = false ;

    // Set Layered
    if (SetWindowLong(Handle, GWL_EXSTYLE, (GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED)))
        {

        // Background covers whole form
        ControlStyle << csOpaque ;

        // Create BitMap
        FBitmap = new TBitmap ;

        // Load the PNG from resource
        LoadPNG(ResourceName) ;

        // Resize form to fit bitmap
        ClientWidth  = FBitmap->Width ;
        ClientHeight = FBitmap->Height ;
        }
}
//---------------------------------------------------------------------------
__fastcall TSplashScreen::~TSplashScreen()
{
delete FBitmap ;
}
//---------------------------------------------------------------------------
void __fastcall TSplashScreen::TimerTimer(TObject *Sender)
{
Timer->Enabled = false ; // This event only once

Hide() ;

Application->ProcessMessages() ;

delete this ; // Self destruct when time is up
}
//---------------------------------------------------------------------------
void __fastcall TSplashScreen::FormShow(TObject *Sender)
{
Display(Alpha) ;

if (Timer->Interval)
    Timer->Enabled = true ;
}
//---------------------------------------------------------------------------
void TSplashScreen::LoadPNG(const String &ResourceName)
{
TStream *Stream = NULL ;

__try // Load PNG from resource (which must be added to the project)
    {
    wchar_t Type[] = L"PNG" ;
    Stream = new TResourceStream((NativeUInt)HInstance, ResourceName, Type) ;

    TPngImage *PNG = NULL ;
    __try
        {
        PNG = new TPngImage ;
        PNG->LoadFromStream(Stream) ;
        FBitmap->Assign(PNG) ;
        FBitmap->PixelFormat = pf32bit ;
        FBitmap->AlphaFormat = afPremultiplied ;
        }
    __finally
        {
        delete PNG ;
        }
    }
__finally
    {
    delete Stream ;
    }
}
//---------------------------------------------------------------------------
void TSplashScreen::Display(int Alpha)
{
// Position bitmap on form
POINT BitmapPos = {0, 0} ;
SIZE BitmapSize = {FBitmap->Width, FBitmap->Height} ;

// Setup alpha blending parameters
BLENDFUNCTION BlendFunction ;
BlendFunction.BlendOp = AC_SRC_OVER ;
BlendFunction.BlendFlags = 0 ;
BlendFunction.SourceConstantAlpha = Alpha ; // Transparency Value (255 is not transparent, 0 is fully transparent)
BlendFunction.AlphaFormat = AC_SRC_ALPHA ;

// Display
if (!UpdateLayeredWindow(Handle, 0, NULL, &BitmapSize, FBitmap->Canvas->Handle, &BitmapPos, 0, &BlendFunction, ULW_ALPHA))
    {
    DWORD Error = GetLastError() ;
    }

Application->ProcessMessages() ;
}
//---------------------------------------------------------------------------

 

Share this post


Link to post

And the morning brings clarity .. 🙂
Turns out I still had the form's 'TransparentColor' property set to 'true' (Left over from my first attempt, see start of conversation).

I reset it to 'false' and now above code works beautifully.

 

This window cannot be moved with the mouse.

Frankly, I'm not sure what code or setting is responsible for this difference in behavior

Edited by CyberPeter

Share this post


Link to post

This looks good, but it as far as I could guess from the code, it would run on Windows only...

Share this post


Link to post
Posted (edited)

Amazing Anders, works perfectly also in Linux with Wine.
image.thumb.png.a47c712be89c47d80aa623af0a6ef78f.png

Edited by shineworld
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×