Click here to Skip to main content
Email Password   helpLost your password?

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

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

12 Feb 2002

Do you have any question or comment? Contact me!

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
AnswerFinding a working image [SOLVED]
elmernite
6:25 17 Dec '09  
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

GeneralAnchor Controls doesnt work if using Per Pixel Alpha Blend
damon88
22:03 12 Dec '09  
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 question
nirishere
16:46 4 Sep '09  
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 Controls
Gottdrak99
13:23 19 Nov '08  
Here is a workaround:

...

class PerPixelAlphaForm : Form
{
Timer _RefreshFormTimer = new Timer();

public AlphaForm()
{
// This form should not have a border or else Windows will clip it.
FormBorderStyle = FormBorderStyle.None;

_RefreshFormTimer.Interval = 10;
_RefreshFormTimer.Tick += new EventHandler(RefreshFormTimer_Tick);
_RefreshFormTimer.Enabled = true;

}

private Bitmap _FormBitmap;
private byte _FormOpacity;
private void RefreshFormTimer_Tick(object sender, EventArgs e)
{
if (_FormBitmap != null)
SetBitmap(_FormBitmap, _FormOpacity);
}


/// Changes the current bitmap.
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}

...


...
/// Changes the current bitmap with a custom opacity level. Here is where all happens!
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");

_FormBitmap = bitmap;
_FormOpacity = opacity;


// 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 {

foreach (Control ctrl in this.Controls)
ctrl.DrawToBitmap(bitmap, ctrl.Bounds);

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 Controls
colche
20:52 25 Feb '09  
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 Controls
rbrender
18:44 9 Mar '09  
With this solution (timer) how can you show a blinking caret in a textbox?
GeneralRe: Add Controls
Marko Padjen
14:39 31 Mar '09  
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.
GeneralExactly what I was looking for. Thanks.
mlavenne
12:36 18 Aug '08  
Great work.
GeneralGreat but can I use the function also for a control?
it-bergmann
2:24 14 Feb '08  
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
GeneralRe: Great but can I use the function also for a control?
Member 4202800
5:49 19 May '09  
good idea would be extend form class and put there Your implementation. It inherits Control internally, so there will be no problem.
Generalcool
radioman.lt@gmail.com
23:20 12 Dec '07  
just great!

peace & serenity


Generalwhy doing win32 with c#?
bkaratte
21:46 11 Dec '07  
why using old win32 with .net and c#...? get rid of those old gdi handles...
GeneralRe: why doing win32 with c#?
tic84
14:12 10 Jun '08  
because you cant update a layered window using a gdi+ bitmap....
GeneralVB.Net
dawmail333
23:47 24 Aug '07  
How would I get this working with VB.Net 2005?
GeneralRe: VB.Net
Dj Den4ik
0:05 12 Feb '08  
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
GeneralGreat!!!!!!!!!!!!!
shp-coding
8:44 9 Aug '07  
Simply the BEST!! Many thanks, Rui!!! Big GrinBig GrinBig Grin
GeneralWay to add some controls
ihess
5:17 30 Jul '07  
Add some controls to overlay transparent form and avoid refreshing it's content with common Refresh()/Invalidate() methods,
use UpdateLayeredWindow() instead. For correct controls rendering add code like

Bitmap temp_bmp = new Bitmap(bitmap);
foreach (Control ctrl in this.Controls)
{
ctrl.DrawToBitmap(temp_bmp, ctrl.Bounds);
}
hBitmap = temp_bmp.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap

to the PerPixelAlphaForm::SetBitmap() method. Every System.Windows.Forms controlhas method DrawToBitmap().

Actually, i used this way to render simple controls - labels, i don't know would it work with more complex
user controls, but i can't see why it should not Smile

GeneralRe: Way to add some controls [modified]
Sp3cial
9:12 24 Aug '08  
Thanks, I was wondering about this.

Works perfectly Big Grin

Edit: Ok, I take that back sorta.. The actual clickable region is MUCH smaller than the visible control (a button in this case)...

modified on Sunday, August 24, 2008 2:37 PM

GeneralHelp
gsampath
21:15 28 May '07  
I want mesure a length of a line by using pixel.Can u send me C# coding for that

fbgsd asgtdfg

GeneralImage problem
darb2
13:57 20 Apr '07  
I've compiled the source by using the bat file but I'm still not able to get the Images to show up. What am I missing?
GeneralRe: Image problem
Daryl Fish
15:58 15 Jan '08  
Check point 1...'Drag & Drop an image file from windows...", just drag one of the images in the sample zip onto the compiled window or call the following when the application is loaded:

SetBitmap(New Bitmap("full path to your image.png"))

Cheers
GeneralProblem when changing the form parent to desktop with dual-monitoring [modified]
Pim-Pom
2:59 7 Oct '06  
(Sorry for the poor English)

Hi,
I got a strange problem.

In my form i add something like :
[DllImport("user32.dll")]
private static extern int SetParent(IntPtr hWndChild, int hwndNewParent);

[DllImport("user32.dll", EntryPoint="FindWindowA")]
private static extern int FindWindow(string lpClassName, string lpWindowName);

private int GetDesktopWnd() {
return FindWindow("Progman", null);
}

private void button1_click(object sender, System.EventArgs e) {
SetParent(pngForm_.Handle, GetDesktopWnd);
}


So when I click button1 my pngForm_ is now drawed on the desktop.

This worked great with 1 monitor.. But now I'm dual-monitoring and I got a bug when I try to move the pngForm_ when it's on the desktop. By example I hit the form when it's on monitor 1, and the form go on monitor 2 at the same position, and after it's impossible to move it.

I have made a lot of test, and i thinks it's due to the cp.ExStyle. In Fact when I remove the WS_EX_LAYERED it works..

Any idea to repair that ?


-- modified at 8:51 Saturday 7th October, 2006
GeneralNice example, but...
moebiusproject
10:35 12 Sep '06  
Isn't it easier to use Forms.Opacity?
GeneralRe: Nice example, but...
Tung Nguyen
18:37 15 Apr '07  
Đm, moebiusproject đúng là thằng ngu chẳng hiểu cái đ*'o gì cả!

Smile

GeneralRe: Nice example, but...
tic84
14:14 10 Jun '08  
No....because that won't give you an alphablended gui. it will just have 1 level of transparency


Last Updated 4 Jun 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010