Click here to Skip to main content
Click here to Skip to main content

Per Pixel Alpha Blend in C#

By , 3 Jun 2004
 

Preview of the per pixel alpha blend effect

Preview of the per pixel alpha blend effect

Introduction

This is a port of my other article from C++/MFC to C#/Windows Forms. The concept of creating a per-pixel-alpha blending window remains the same, the GDI function UpdateLayeredWindow.

What changed

  • Image loading is handled by GDI+ classes (System.Drawing namespace).
  • No need to pre-multiply the rgb channels with the alpha channel.
  • No more support for PSP files. PNG file are the best choice.

Usage

To use this code you need to include PerPixelAlphaForm.cs file in your project, create a class that inherits from PerPixelAlphaForm, load a bitmap using System.DrawingImage.FromFile static method and then call the PerPixelAlphaForm.SetBitmap method.

The example source code is inside the main.cs file. In the source code you will also find the new ways of doing the old things, like, handling dropped files from windows explorer.

Installing & Building

  1. Download and extract the source code of this article;
  2. To build, run the build.bat file from command prompt;
  3. Now, run the bin\main.exe and enjoy!

Changelog

31 May 2004

  • Fix clipping problems by creating the form without borders.
  • Move PerPixelAlphaForm class to PerPixelAlphaForm.cs file.

12 Feb 2002

  • Initial version.

Do you have any question or comment? Contact me!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Rui Lopes
Web Developer
Portugal Portugal
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionform controls TooltipmemberAntonio Pelleriti14 Dec '11 - 21:45 
Is there a way to display a tooltip for a control on the form?
 
the controls are displayed with your code:
 
Bitmap temp_bmp = new Bitmap(bitmap);
foreach (Control ctrl in this.Controls)
{
                    ctrl.DrawToBitmap(temp_bmp, ctrl.Bounds);
}

SuggestionCan you give use link of whole projectmemberMember 793369229 Jun '11 - 6:16 
Can you give use link of whole project with *.csproj or sln files,, b`coz it is difficult to create and load any form single file in VS 2005
GeneralMy vote of 2memberjoin sam lee25 Jul '10 - 22:51 
good
GeneralMy vote of 5memberKPEBEDKO19 Jul '10 - 2:53 
Great work!
GeneralThank you!memberEduard Keilholz2 Jul '10 - 2:41 
Used your code in my project, works flawless
 
Thank you!
.: I love it when a plan comes together :.
http://www.zonderpunt.nl

AnswerFinding a working image [SOLVED]memberelmernite17 Dec '09 - 5:25 
I made an image in the GIMP and saved it as a transparent png but it isn't working?
I'm getting the "The bitmap must be 32ppp with alpha-channel." exception. How do I check to this? I've never hear of "ppp" before.
 
-Elmernite
 
After editing around I found that I need to set "Save color values from transparent pixels" With that checked it works.
 
modified on Thursday, December 17, 2009 11:37 AM

GeneralRe: Finding a working image [SOLVED]memberGee.1 Mar '10 - 12:13 
I think the author mis-typed bpp (Bits-Per-Pixel)...
Gee

GeneralAnchor Controls doesnt work if using Per Pixel Alpha Blendmemberdamon8812 Dec '09 - 21:03 
I noticed that anchoring doesnt work if you use PPAB,
 
I think its because of the way controls are drawn to bitmap, anchor offsets are not taken into account, Can you help me fix this ?
Generalsmall questionmembernirishere4 Sep '09 - 15:46 
Is it possible to draw on that image ? say a rectangle ?
What is the way to access the pixel buffer and modify it ? rgba-wise
Would it take a double buffer for the drawing to avoid the flickering ?
tx
GeneralAdd ControlsmemberGottdrak9919 Nov '08 - 12:23 
Here is a workaround:
 
...
 
class PerPixelAlphaForm : Form
{
  <code>Timer _RefreshFormTimer = new Timer();</code>
 
  public AlphaForm()
  {
    // This form should not have a border or else Windows will clip it.
    FormBorderStyle = FormBorderStyle.None;
 
    <code>_RefreshFormTimer.Interval = 10;
    _RefreshFormTimer.Tick += new EventHandler(RefreshFormTimer_Tick);
    _RefreshFormTimer.Enabled = true;</code>
  }
 
  <code>private Bitmap _FormBitmap;
  private byte _FormOpacity;
  private void RefreshFormTimer_Tick(object sender, EventArgs e)
  {
    if (_FormBitmap != null)
      SetBitmap(_FormBitmap, _FormOpacity);
  }</code>
 
  /// <para>Changes the current bitmap.</para>
  public void SetBitmap(Bitmap bitmap)
  {
    SetBitmap(bitmap, 255);
  }
 
...
 
...
	/// <para>Changes the current bitmap with a custom opacity level.  Here is where all happens!</para>
	public void SetBitmap(Bitmap bitmap, byte opacity)
	{
		if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
			throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
 
    <code>_FormBitmap = bitmap;
    _FormOpacity = opacity;</code>
 
		// The ideia of this is very simple,
		// 1. Create a compatible DC with screen;
		// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
		// 3. Call the UpdateLayeredWindow.
 
		IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
		IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
		IntPtr hBitmap = IntPtr.Zero;
		IntPtr oldBitmap = IntPtr.Zero;
 
		try {
			<code>
                        foreach (Control ctrl in this.Controls)
                          ctrl.DrawToBitmap(bitmap, ctrl.Bounds);
</code>
                        hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));  // grab a GDI handle from this GDI+ bitmap
 
                        oldBitmap = Win32.SelectObject(memDc, hBitmap);
 
			Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
			Win32.Point pointSource = new Win32.Point(0, 0);
			Win32.Point topPos = new Win32.Point(Left, Top);
			Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
...

 
Gottdrak99
 
modified on Thursday, November 27, 2008 3:22 AM

GeneralRe: Add Controlsmembercolche25 Feb '09 - 19:52 
fantastic! exactly what i was looking for...
 
Small note: I also had to wrap a .Visible check around the .DrawToBitmap method to meet my requirements.
 
foreach (Control ctrl in this.Controls) {
if (ctrl.Visible) {

ctrl.DrawToBitmap(bitmap, ctrl.Bounds);
}
}

GeneralRe: Add Controlsmemberrbrender9 Mar '09 - 17:44 
With this solution (timer) how can you show a blinking caret in a textbox?
GeneralRe: Add ControlsmemberMarko Padjen31 Mar '09 - 13:39 
Great addition!
 
One could also add in front of
_FormBitmap = bitmap;
to do that only if
_FormBitmap
is null.
Thatway, if you change for example Text on Label with transparent background, it won't be mixed.
GeneralRe: Add ControlsmemberLatencyXXX24 Mar '12 - 21:36 
This is not really a workaround. You are just rastering the controls to the background image. There is no independent relationship for any sort of a control. You are relying on the alpha channel to be blended based on its opaqueness rather than the opacity in conjunction with properties of a control itself.
 
I think there needs to be some different means to achieve a more practical solution. Have your alpha form as background and a separate transparent form with all your controls sync'd with the movement of the underlying form. Your going to have to layer the forms accordingly and preserve the ordering amongst other open windows.
 
I just can't believe they still haven't put anything for this in GDI+ or WF in VS11b and expect us to interop with Win32 which doesn't make things easy for many. You do realize that even the resources can't even be imported correctly without losing the alpha channel? Still no fix, M$ cherry picks every single thing known to man and it's not that hard to fix simple issues.
 
Good idea and I like the button images even though they arn't actual buttons or controls. Let us know if you get a fix or update/thread/link that shows us your new research, otherwise now might be a good time to switch to WPF since beta x11/12 is not showing any easy way to do what we really want in WF.
 
Meanwhile, take a look at this recommendation: Here which then leads to the CodeProject found There
-Latency

GeneralExactly what I was looking for. Thanks.membermlavenne18 Aug '08 - 11:36 
Great work.
QuestionGreat but can I use the function also for a control?memberit-bergmann14 Feb '08 - 1:24 
Hi,
 
I'm looking for a function simular as this code.
But I don't need a transparent form - I just need a transparent control inside a form.
Maybe somebody can help.
 
Thanks,
 
Andre
AnswerRe: Great but can I use the function also for a control?memberMember 420280019 May '09 - 4:49 
good idea would be extend form class and put there Your implementation. It inherits Control internally, so there will be no problem.
Generalcoolmemberradioman.lt@gmail.com12 Dec '07 - 22:20 
just great!
 
peace & serenity

Questionwhy doing win32 with c#?memberbkaratte11 Dec '07 - 20:46 
why using old win32 with .net and c#...? get rid of those old gdi handles...
AnswerRe: why doing win32 with c#?membertic8410 Jun '08 - 13:12 
because you cant update a layered window using a gdi+ bitmap....
GeneralVB.Netmemberdawmail33324 Aug '07 - 22:47 
How would I get this working with VB.Net 2005?
GeneralRe: VB.NetmemberDj Den4ik11 Feb '08 - 23:05 
Class:

Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
'Translation from C# by Dj Den4ik
Public Class PerPixelAlphaForm
'Implements Windows.Forms.IWin32Window
 
Public Structure ARGB
Public Blue As Byte
Public Green As Byte
Public Red As Byte
Public Alpha As Byte
End Structure
 
Public Structure BLENDFUNCTION
Public BlendOp As Byte
Public BlendFlags As Byte
Public SourceConstantAlpha As Byte
Public AlphaFormat As Byte
End Structure
 
Public Const ULW_COLORKEY As Int32 = &H1
Public Const ULW_ALPHA As Int32 = &H2
Public Const ULW_OPAQUE As Int32 = &H4
 
Public Const AC_SRC_OVER As Byte = &H0
Public Const AC_SRC_ALPHA As Byte = &H1
 
Public Declare Function UpdateLayeredWindow Lib "user32" Alias "UpdateLayeredWindow" (ByVal hwnd As IntPtr, ByVal hdcDst As IntPtr, ByRef pptDst As Point, ByRef psize As Size, ByVal hdcSrc As IntPtr, ByRef pprSrc As Point, ByVal crKey As Int32, ByRef pblend As BLENDFUNCTION, ByVal dwFlags As Int32) As Boolean
Public Declare Function GetDC Lib "user32" Alias "GetDC" (ByVal hWnd As IntPtr) As IntPtr
Public Declare Function ReleaseDC Lib "user32" Alias "ReleaseDC" (ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Integer
Public Declare Function CreateCompatibleDC Lib "gdi32.dll" Alias "CreateCompatibleDC" (ByVal hDC As IntPtr) As IntPtr
Public Declare Function DeleteDC Lib "gdi32.dll" Alias "DeleteDC" (ByVal hDC As IntPtr) As Boolean
Public Declare Function SelectObject Lib "gdi32.dll" Alias "SelectObject" (ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr
Public Declare Function DeleteObject Lib "gdi32.dll" Alias "DeleteObject" (ByVal hObject As IntPtr) As Boolean
 
Public Sub SetBitmap(ByVal bitmap As Bitmap, ByVal opacity As Byte, ByVal frm As Form)
If bitmap.PixelFormat <> PixelFormat.Format32bppArgb Then Throw New ApplicationException("The bitmap must be 32ppp with alpha-channel.")
 
' The ideia of this is very simple,
' 1. Create a compatible DC with screen;
' 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
' 3. Call the UpdateLayeredWindow.
 
Dim screenDc As IntPtr = GetDC(IntPtr.Zero)
Dim memDc As IntPtr = CreateCompatibleDC(screenDc)
Dim hBitmap As IntPtr = IntPtr.Zero
Dim oldBitmap As IntPtr = IntPtr.Zero
 
Try
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)) ' grab a GDI handle from this GDI+ bitmap
oldBitmap = SelectObject(memDc, hBitmap)
Dim size As New Size(bitmap.Width, bitmap.Height)
Dim pointSource As New Point(0, 0)
Dim topPos As New Point(frm.Left, frm.Top)
Dim blend As New BLENDFUNCTION
blend.BlendOp = AC_SRC_OVER
blend.BlendFlags = 0
blend.SourceConstantAlpha = opacity
blend.AlphaFormat = AC_SRC_ALPHA
UpdateLayeredWindow(frm.Handle, screenDc, topPos, size, memDc, pointSource, 0, blend, ULW_ALPHA)
Finally
ReleaseDC(IntPtr.Zero, screenDc)
If hBitmap <> IntPtr.Zero Then
SelectObject(memDc, oldBitmap)
DeleteObject(hBitmap)
End If
DeleteDC(memDc)
 
End Try
End Sub

 
Form:

Dim ppaf As New PerPixelAlphaForm
Dim bmp As New Bitmap("D:\Audio\FL Studio 7\Artwork\FL Studio Producer Edition\Title_.png")
 
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim SecPerm As New Security.Permissions.SecurityPermission(Security.Permissions.PermissionState.Unrestricted)
SecPerm.Demand()
 
' Extend the CreateParams property of the Button class.
Dim cp As System.Windows.Forms.CreateParams = MyBase.CreateParams
' Update the button Style.
cp.ExStyle = &H80000
 
Return cp
End Get
End Property
 
Private Sub frmTest_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
Me.TopMost = True
For Each ctrl As Control In Me.Controls
ctrl.DrawToBitmap(bmp, ctrl.Bounds)
Next
Me.Region = New Region()
ppaf.SetBitmap(bmp, 240, Me)
End Sub
 

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
 
Const WM_MOUSEMOVE As Int32 = &H200
Const WM_NCLBUTTONDOWN As Int32 = &HA1
Const HTCAPTION As Int32 = 2
 
'// ????????????? WM_MOUSEMOVE
If m.Msg = WM_MOUSEMOVE Then
 
'// ???????? ReleaseCapture
MyBase.Capture = False
 
'// ??????? ???? ?????????
Dim message As New Message
With message
.HWnd = Me.Handle
.Msg = WM_NCLBUTTONDOWN
.WParam = HTCAPTION
.LParam = 0&
End With
 
'// ?????????? ???? ????????? ????
MyBase.WndProc(message)
End If
 
MyBase.WndProc(m)
 
End Sub

Smile | :)
GeneralRe: VB.NetmemberJason Newland25 Apr '12 - 19:31 
lol, use a C# to VB.NET converter such as developerfusion
 
http://www.developerfusion.com/tools/convert/csharp-to-vb/[^]
 
or telerik
 
http://converter.telerik.com/[^]
GeneralRe: VB.NetmemberJason Newland25 Apr '12 - 19:33 
personally, however, I'd drop VB.NET and move to C# like most eventually do ... including me
GeneralGreat!!!!!!!!!!!!!membershp-coding9 Aug '07 - 7:44 
Simply the BEST!! Many thanks, Rui!!! Big Grin | :-D Big Grin | :-D Big Grin | :-D

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 4 Jun 2004
Article Copyright 2002 by Rui Lopes
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid