Advanced C# splash screen

I once posted about how to make a splash screen with C#; yet it’s still a rectangle. While it’s true that you can replace my picture with something that have curvy edges, you’ll have aliased edges i.e. crooked and crude pixels around the edges.

Example of an aliased picture, the edges are not smooth and you can't see through the semitransparent parts

There’s a solution: alpha-blend the picture and you can see through the form; pixels with alpha value of neither 0 or 255 will be mixed appropriately with the background.

Alpha-blended picture. The edges are smoother and you can see through the form

To achieve this, you have to write your custom painting function with windows API, specifically this one

[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

Which can be a daunting task. Luckily we have someone from Visual C# kicks did the hard work and released the source for public use! But that’s not the end of my story just yet. I wanted the splash screen to fades in and out. The timing part was easy, just a timer and several more lines of code and it’s done.

        enum Phases {
            FadeIn,
            Hold,
            FadeOut
        };
        Phases CurrentPhase = Phases.FadeIn;
        int FadeInStep = 0;
        const int FadeInLast = 20;
        int HoldStep = 0;
        const int HoldLast = 80;
        int FadeOutStep = 20;
        const int FadeOutLast = 0;
        const int OpacityMultiplier = 5;

        private void timerFade_Tick(object sender, EventArgs e)
        {
            if (CurrentPhase == Phases.FadeIn)
            {
                if (FadeInStep < FadeInLast)
                {
                    FadeInStep++;
                    this.Opacity = (OpacityMultiplier * FadeInStep) / 100.0;
                }
                else
                    CurrentPhase = Phases.Hold;
            }
            else if (CurrentPhase == Phases.Hold)
            {
                if (HoldStep < HoldLast)
                {
                    HoldStep++;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    CurrentPhase = Phases.FadeOut;
            }
            else if (CurrentPhase == Phases.FadeOut)
            {
                {
                    FadeOutStep--;
                    this.Opacity = (OpacityMultiplier * FadeOutStep) / 100.0;
                }
                else
                    this.Close();
            }

I have even altered the custom paint code to utilize the form’s transparency factor

                //Set up blending options
                API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
                blend.BlendOp = API.AC_SRC_OVER;
                blend.BlendFlags = 0;
                blend.SourceConstantAlpha = (byte)(255 * this.Opacity);
                blend.AlphaFormat = API.AC_SRC_ALPHA;

                API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, this.BackColor.ToArgb(), ref blend, API.ULW_ALPHA);

But that didn’t work to my expectation! The form is fading in and out gradually but the alpha blending effect is lost.

Fugly!

After fiddling around, I figured out that the custom paint code somehow didn’t override all the necessary painting functions and the setter of this.Opacity call those functions every time it’s changed. Searching around the list of functions can be overridden I found nothing interesting

That function doesn't help...

So I created a new variable and leave the form’s default Opacity alone. After all, if I have adjusted transparency myself in the custom paint function, who needs .NET’s alpha-blend-incapable render? The code above becomes:

        double MyOpacity = 0;

            if (CurrentPhase == Phases.FadeIn)
            {
                if (FadeInStep < FadeInLast)
                {
                    FadeInStep++;
                    MyOpacity = (OpacityMultiplier * FadeInStep) / 100.0;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    CurrentPhase = Phases.Hold;
            }
            else if (CurrentPhase == Phases.Hold)
            {
                if (HoldStep < HoldLast)                 {                     HoldStep++;                     this.UpdateFormDisplay(this.BackgroundImage);                 }                 else                     CurrentPhase = Phases.FadeOut;             }             else if (CurrentPhase == Phases.FadeOut)             {                 if (FadeOutStep > FadeOutLast)
                {
                    FadeOutStep--;
                    MyOpacity = (OpacityMultiplier * FadeOutStep) / 100.0;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    this.Close();
            }
        }
                //Set up blending options
                API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
                blend.BlendOp = API.AC_SRC_OVER;
                blend.BlendFlags = 0;
                blend.SourceConstantAlpha = (byte)(255 * MyOpacity);
                blend.AlphaFormat = API.AC_SRC_ALPHA;

                API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, this.BackColor.ToArgb(), ref blend, API.ULW_ALPHA);

And Voilà!

Alpha blended splash against my blog in the background, you can see through the semi-transparent form

You can get the project, source code and complied binary here.

Splash screen in C#

How come every time I build something small, no matter how trivial it could be, I wanted to share it? :-/

This afternoon, i was asked on how to do a splash screen in C#. The guy already tried threads to display they screen and and that threw an ugly exception at the user’s face. I guess it is not that simple to guess 😛

The splash screen is just a form. You can either show it on the foreground and allow your application to load on the background with thread, or just build another application and execute it with some function in the System.Diagnostic namespace, I will demo how to make a splash screen for the later since it’s easier to debug and you can show as many of them as once as you like 😛

First, you need a form, splash screen doesn’t have title bar and stuff, so change the FormBorderStyle property to none, insert a picture box and fill-docking it to the form. Why a picture box and not just the form’s background you ask? Well, the picture box is designed to host pictures and you can scale it, provide an error image for it etc. If you simply use the form itself, you’ll have to scale the picture by hand.

Next change the image attribute of the picture box to your splash bitmap. Alternatively, if you want to reuse the program without having to recompile the project, have it read the splash bitmap in the application’s directory with:

String strPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).Substring(6);
Bitmap img = new Bitmap(strPath + System.IO.Path.DirectorySeparatorChar + "splash.bmp");
pictureBox1.Image = img;

Put that in the Form_Load() event. The first line get the application’s directory; the Substring() is there to remove the file:// part form the returned path. The second line’s Bitmap constructor doesn’t like that 🙂

For the final touch, add a timer, set it to enabled, set your desired splash duration for interval, add a Tick event and type in

this.Close();

That’s it, F5 and you have a Splash screen to go

Splash screen

Download the source (dynamically load a bitmap)
Download the source (embedded bitmap in picturebox)

PS: That’s my actual Eclipse splash, I photoshopped it for personal use since I can’t stand the stock splash xD. But since I’m releasing this I guess I’ll have to add the source: I took it from here.

PPS: Argh, byet hosts are not compatible with WordPress’s integrated picture management T__T