Lester's WPF\SL Blog

Simple, Easy & Beautiful

Creating Non-Rectangular Splash Screen - using Alpha Channel

Creating Non-Rectangular Splash Screen - using Alpha Channel

  • Comments 0

For one of my apps that I created and I will be posting that soon, I wanted to have a splash screen just like the common desktop apps have. So that began my quest for a non-rectangular window. Fortunately WPF does have the capability, but my first attempt was using a Win32 Form and it came out pretty well. It takes a 32 bit bitmap and displays it within a form. So depending on the shape of the image you have a window. So lets look at the code.

class Splash : Form

{

public Splash(Bitmap bitmap)

{

   // Window settings

   this.TopMost = true;

   this.ShowInTaskbar = false;

   this.Size = bitmap.Size;

   this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;

   this.Show();

   // Must be called before setting bitmap

   this.SelectBitmap(bitmap);

   this.BackColor = Color.Red;

   this.Click += new EventHandler(Splash_Click);

   this.MouseClick += new MouseEventHandler(Splash_MouseClick);

}

void Splash_MouseClick(object sender, MouseEventArgs e)

{

   this.Close();

}

void Splash_Click(object sender, EventArgs e)

{

}

// Sets the current bitmap

public void SelectBitmap(Bitmap bitmap)

{

   // Does this bitmap contain an alpha channel?

   if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)

   {

      throw new ApplicationException("The bitmap must be 32bpp with alpha-channel.");

   }

   // Get device contexts

   IntPtr screenDc = APIHelp.GetDC(IntPtr.Zero);

   IntPtr memDc = APIHelp.CreateCompatibleDC(screenDc);

   IntPtr hBitmap = IntPtr.Zero;

   IntPtr hOldBitmap = IntPtr.Zero;

   try

   {

      // Get handle to the new bitmap and select it into the current device context

      hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));

      hOldBitmap = APIHelp.SelectObject(memDc, hBitmap);

      // Set parameters for layered window update

      APIHelp.Size newSize = new APIHelp.Size(bitmap.Width, bitmap.Height); // Size window to match bitmap

      APIHelp.Point sourceLocation = new APIHelp.Point(0, 0);

      APIHelp.Point newLocation = new APIHelp.Point(this.Left, this.Top); // Same as this window

      APIHelp.BLENDFUNCTION blend = new APIHelp.BLENDFUNCTION();

      blend.BlendOp = APIHelp.AC_SRC_OVER; // Only works with a 32bpp bitmap

      blend.BlendFlags = 0; // Always 0

      blend.SourceConstantAlpha = 255; // Set to 255 for per-pixel alpha values

      blend.AlphaFormat = APIHelp.AC_SRC_ALPHA; // Only works when the bitmap contains an alpha channel

      // Update the window

      APIHelp.UpdateLayeredWindow(Handle, screenDc, ref newLocation, ref newSize,

         memDc, ref sourceLocation, 0, ref blend, APIHelp.ULW_ALPHA);

   }

   finally

   {

      // Release device context

      APIHelp.ReleaseDC(IntPtr.Zero, screenDc);

      if (hBitmap != IntPtr.Zero)

      {

         APIHelp.SelectObject(memDc, hOldBitmap);

         APIHelp.DeleteObject(hBitmap); // Remove bitmap resources

      }

      APIHelp.DeleteDC(memDc);

   }

}

protected override CreateParams CreateParams

{

   get

   {

      // Add the layered extended style (WS_EX_LAYERED) to this window

      CreateParams createParams = base.CreateParams;

      createParams.ExStyle |= APIHelp.WS_EX_LAYERED;

      return createParams;

   }

}

// Let Windows drag this window for us (thinks its hitting the title bar of the window)

protected override void WndProc(ref Message message)

{

   if (message.Msg == APIHelp.WM_NCHITTEST)

   {

      // Tell Windows that the user is on the title bar (caption)

      message.Result= (IntPtr)APIHelp.HTCAPTION;

   }

   else

   {

      base.WndProc(ref message);

   }

}

}

 

// Class to assist with Win32 API calls

class APIHelp

{

// Required constants

public const Int32 WS_EX_LAYERED = 0x80000;

public const Int32 HTCAPTION = 0x02;

public const Int32 WM_NCHITTEST = 0x84;

public const Int32 ULW_ALPHA = 0x02;

public const byte AC_SRC_OVER = 0x00;

public const byte AC_SRC_ALPHA = 0x01;

 

public enum Bool

{

   False = 0,

   True = 1

}

 

[StructLayout(LayoutKind.Sequential)]

public struct Point

{

   public Int32 x;

   public Int32 y;

   public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }

}

 

[StructLayout(LayoutKind.Sequential)]

public struct Size

{

   public Int32 cx;

   public Int32 cy;

   public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }

}

 

[StructLayout(LayoutKind.Sequential, Pack = 1)]

struct ARGB

{

   public byte Blue;

   public byte Green;

   public byte Red;

   public byte Alpha;

}

 

[StructLayout(LayoutKind.Sequential, Pack = 1)]

public struct BLENDFUNCTION

{

   public byte BlendOp;

   public byte BlendFlags;

   public byte SourceConstantAlpha;

   public byte AlphaFormat;

}

 

[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);

[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]

public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]

public static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("user32.dll", ExactSpelling = true)]

public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]

public static extern Bool DeleteDC(IntPtr hdc);

[DllImport("gdi32.dll", ExactSpelling = true)]

public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]

public static extern Bool DeleteObject(IntPtr hObject);

}

The code has comments which are self explanatory. Its much simpler in Avalon but before delving into it, I wanted to show how it can be possibly done using alpha channels.

PS:the above code is taken from one of the bloggers. Cant seem to remember the person.. Blame my memory .. ;)