How to manually draw to a transparent dialog

Edit: Originally this post was to draw to a transparent window... but I have found that my real problem has to do with drawing a bitmap with aplha channel info.

Could anyone point me in the right direction?

-----

I am trying to draw a graphic file to a dialog which is configured to be borderless & has nothing on it (I pretend to "draw" an image to it and frame it with a skin made of several PNG files). I have coded it so it gets layered and it's background is made transparent by means of setting it as a layered window (COLORKEY).

The dialog gets shown correctly but am I unable to "draw" the bitmap image on it... I am doing something like this during

WM_INITDIALOG:

[SIZE=2]Gdiplus::Bitmap *bitmapImage = [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Bitmap(szImageFileName);[/SIZE]
[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (bitmapImage) [/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]
[INDENT]// Change the size of the window
[/INDENT][/COLOR][/SIZE]
[/COLOR][/SIZE]
[INDENT][SIZE=2]UINT nWidth = bitmapImage->GetWidth();[/SIZE]
[SIZE=2]UINT nHeigth = bitmapImage->GetHeight();[/SIZE]
[/INDENT][INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Retrieve the dialog's DC[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2]HDC hDC = GetWindowDC(hDlg);[/SIZE]
[SIZE=2]Gdiplus::Graphics dlgGraphics(hDC);[/SIZE]
[SIZE=2]dlgGraphics.SetInterpolationMode(InterpolationModeHighQuality);[/SIZE]
[SIZE=2]dlgGraphics.SetSmoothingMode(SmoothingModeAntiAlias);[/SIZE]
[/INDENT][INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Configure the background color[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]Gdiplus::Color *backColor = [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Color();[/SIZE]
[SIZE=2]backColor->SetFromCOLORREF(GetSysColor([/SIZE][SIZE=2][SIZE=2][COLOR=black]CTLCOLOR_DLG[/COLOR][/SIZE][/SIZE][SIZE=2]));[/SIZE]
[SIZE=2]Gdiplus::SolidBrush brushBackColor(*backColor);[/SIZE]

[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Draw the image strip[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]dlgGraphics.FillRectangle(&brushBackColor, 35, 35, nWidth, nHeigth); [/SIZE]
[SIZE=2]dlgGraphics.DrawImage(bitmapImage, 35, 35, nWidth, nHeigth);[/SIZE]

[SIZE=2]...[/SIZE]
[/INDENT][SIZE=2]}[/SIZE]

But noting is being shown. Am I missing something?

#516194

Ok, I got the window to draw the image. It seems I have to process the WM_PAINT message and not just the WM_ONINITDIALOG:

I have something similar to this:

[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]case[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] WM_PAINT:[/SIZE]
[SIZE=2]{[/SIZE]
[INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]//[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Init the images[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]//[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]WIN32_FIND_DATA FindFileData;[/SIZE]
[SIZE=2]FindFileData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;[/SIZE]

[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Main image - verify the file exists by searching it[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (!bitmapImage)[/SIZE]
[SIZE=2]{[/SIZE]
[INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Find the comic strip image[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2]HANDLE handle = FindFirstFileEx(L[/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"Logo.png"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2], FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);[/SIZE]
[/INDENT][INDENT][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (handle != INVALID_HANDLE_VALUE)[/SIZE]
[SIZE=2]{[/SIZE]
[INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Close the search handle[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]FindClose(handle);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Get the image[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]bitmapImage= [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Bitmap(L[/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"Logo.png"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]);[/SIZE]
[/INDENT][SIZE=2]}[/SIZE]
[/INDENT][SIZE=2]}[/SIZE]

[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]//[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Draw the images[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]//[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]hDC = BeginPaint(hWnd, &Ps);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Configure the background color[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]Gdiplus::Color *backColor = [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Color();[/SIZE]
[SIZE=2]backColor->SetFromCOLORREF(GetSysColor(CTLCOLOR_DLG));[/SIZE]
[SIZE=2]Gdiplus::SolidBrush brushBackColor(*backColor);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Retrieve the windows' DC[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]HDC hDC = GetWindowDC(hWnd);[/SIZE]
[SIZE=2]Gdiplus::Graphics dlgGraphics(hDC);[/SIZE]
[SIZE=2]dlgGraphics.SetInterpolationMode(InterpolationModeHighQuality);[/SIZE]
[SIZE=2]dlgGraphics.SetSmoothingMode(SmoothingModeNone);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Retrieve the dialog's RECT[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]RECT Rect;[/SIZE]
[SIZE=2]GetWindowRect(hWnd, &Rect);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Draw the comic strip[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (bitmapImage) [/SIZE]
[SIZE=2]{[/SIZE]
[INDENT][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Change the size of the window[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]UINT nWidth = bitmapImage->GetWidth();[/SIZE]
[SIZE=2]UINT nHeight = bitmapImage->GetHeight();[/SIZE]

[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Resize the window accordingly[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]SetWindowPos(hWnd, NULL, 0, 0, nWidth + 70, nHeight + 70, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);[/SIZE]
[SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Draw the image[/COLOR][/SIZE]
[/COLOR][/SIZE][SIZE=2]Gdiplus::RectF dlgRect(Rect.left+35, Rect.top+35, nWidth, nHeight);[/SIZE]
[SIZE=2]dlgGraphics.FillRectangle(&brushBackColor, dlgRect);[/SIZE]
[SIZE=2]dlgGraphics.DrawImage(bitmapImage, dlgRect);[/SIZE]
[/INDENT][SIZE=2]}[/SIZE]
[/INDENT][SIZE=2][SIZE=2]
[INDENT]EndPaint(hWnd, &Ps);
[/INDENT][/SIZE]
[SIZE=2]}[/SIZE]
[SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]break[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2];[/SIZE][/SIZE]

voilá, I got the window to show the image, but I am facing a problem... the image is a PNG file with alfa transparency... I am not sure why it is not being properly drawn. (look at the attached image).

After goolging a bit I think I am skipping the alpha channel info altogether... but I am not sure how to do this.

Again... am I skipping something?

post-79521-1228012300_thumb.png

bad-render.png
bad-render.png

#516278

I tried this... but it is not working either...

[SIZE=2][/SIZE][SIZE=2][COLOR=#008000][SIZE=2][COLOR=#008000]// Get the image
[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]Gdiplus::Bitmap *bitmap = [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Bitmap(L[/SIZE][SIZE=2][COLOR=#a31515][SIZE=2][COLOR=#a31515]"rborder.png"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]);
BitmapData bmp_data;
Gdiplus::Rect bitmapRect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
bitmap->LockBits(&bitmapRect, ImageLockModeWrite, PixelFormat32bppARGB, &bmp_data);
BYTE *bmpPtr = (BYTE *)(bmp_data.Scan0);
rightBorder = [/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] Gdiplus::Bitmap(bmp_data.Width, bmp_data.Height, bmp_data.Stride, bmp_data.PixelFormat, (BYTE *)bmpPtr);
bitmap->UnlockBits(&bmp_data);
[/SIZE][SIZE=2][COLOR=#0000ff][SIZE=2][COLOR=#0000ff]delete[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] bitmap;
[/SIZE]

#516364

Hi, yes you're using key color transparency, you MUST use alpha transparency instead, plus UpdateLayeredWindow ( )

But then, you won't be able to use WM_PAINT (it won't happen anymore, because of ULW_ALPHA).

You'll also need to create a bitmap, and draw all stuff to the bitmap (instead of drawing to window hDC).

Such bitmap must be created with PixelFormat32bppPARGB (instead of ARGB, use PARGB, but only needed in such bitmap, no need to do that in any other bitmaps such as rborder.png).

Then get bitmap's scanlines (LockBits), and create an hBitmap by hand with the scanlines (don't use Gdiplus::Bitmap->GetHBitmap, it won't work). Then use the resulting hBitmap with UpdateLayeredWindow ( ) function.

Hope this helps somewhat. If something is unclear don't doubt to ask ;)

#516441

Thanks matonga... I will try to decipher what you have adviced before asking for more help. This is the very first time I am drawing a window directly... so, it will take some time to understand all this.

Thanks again.

#516451

Ok, hope these help:

C++ Per Pixel Alpha Form:

http://www.codeproject.com/KB/GDI/pxalphablend.aspx

C# Per Pixel Alpha Form:

http://www.codeproject.com/KB/GDI-plus/perpxalpha_sharp.aspx

Because of the code you posted, I suppose you're using C++.

The guy says he's using paintlib to load png files, but you can perfectly use GDI+, Gdiplus::Bitmap->LockBits( ) method and CreateDIBitmap API function to convert the Gdiplus::Bitmap into an HBITMAP while preserving alpha channel information (stupid Gdiplus::Bitmap->GetHBITMAP loses alpha channel info, don't know why).

Maybe CPerPIxelAlphaDlg is exactly what you're looking for (even I wrote a similar class for Delphi, then a friend of mine showed me this stuff in CodeProject... d'oh).

#516465

Thanks again matonga, I will pay a visit to CodeProject!

#516476

Matonga.

I am trying to understand what you told me, right now I am trying to get the bitmap properly loaded to later render it to the windows. so far this is the function I am using:

HBITMAP LoadAlphaChannelBitmap(const WCHAR *pFileName, bool bPreMultiply)

{

HBITMAP hBmp = NULL;

// Configure the background color

Gdiplus::Color backColor(GetSysColor(CTLCOLOR_DLG));

Gdiplus::SolidBrush brushBackColor(backColor);

// Get the bitmap

Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(pFileName);

Gdiplus::BitmapData bmp_data;

Gdiplus::Rect bitmapRect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());

bitmap->LockBits(&bitmapRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppPARGB, &bmp_data);

BYTE *bmpPtr = (BYTE *)(bmp_data.Scan0);

//if (bPreMultiply)

//{

// for (int y = 0; y < bmp_data.Height; y++)

// {

// BYTE *pPixel = (BYTE *)bmpPtr + bmp_data.Width * 4 * y;

// for (int x = 0; x < bmp_data.Width; x++)

// {

// pPixel[0] = pPixel[0] * pPixel[3] / 255;

// pPixel[1] = pPixel[1] * pPixel[3] / 255;

// pPixel[2] = pPixel[2] * pPixel[3] / 255;

// pPixel += 4;

// }

// }

//}

Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(bmp_data.Width, bmp_data.Height, bmp_data.Stride, bmp_data.PixelFormat, (BYTE *)bmpPtr);

bitmap->UnlockBits(&bmp_data);

delete bitmap;

bmp->GetHBITMAP(backColor, &hBmp);

//bitmap->GetHBITMAP(backColor, &hBmp);

delete bmp;

return hBmp;

}

I know the GetHBITMAP function will not preserve alpha channel info... but I expected that this function as is should render a plain copy of the opaque bitmap, but the resulting bitmap is an opaque white rectagle... so I believe I should be doing something wrong.

If I comment all the code and uncomment the bitmap->GetHBITMAP(backColor, &hBmp); line I get the expected result (an opaque bitmap).

Edit: I found the error... I was destoying the bitmaps before getting the HBITMAP back... the correcto code is:

Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(bmp_data.Width, bmp_data.Height, bmp_data.Stride, bmp_data.PixelFormat, (BYTE *)bmpPtr);

bmp->GetHBITMAP(backColor, &hBmp);

bitmap->UnlockBits(&bmp_data);

delete bitmap;

delete bmp;

return hBmp;

This way I am do getting the opaque bitmap... well then I will continue with the CreateDIBitmap API...

#516520

I added the following code to the previous function... it seems to start working but I am getting the image upside-down & a black background... I am not sure why:

This is the code to get the HBITMAP:

HBITMAP LoadAlphaChannelBitmap(const WCHAR *pFileName, bool bPreMultiply)
{
HBITMAP hBmp = NULL;

// Configure the background color
Gdiplus::Color backColor(GetSysColor(CTLCOLOR_DLG));
Gdiplus::SolidBrush brushBackColor(backColor);

// Get the bitmap
Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(pFileName);
Gdiplus::BitmapData bmp_data;
Gdiplus::Rect bitmapRect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
bitmap->LockBits(&bitmapRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppPARGB, &bmp_data);
BYTE *bmpPtr = (BYTE *)(bmp_data.Scan0);

Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(bmp_data.Width, bmp_data.Height, bmp_data.Stride, bmp_data.PixelFormat, (BYTE *)bmpPtr);

BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(bmi));

bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bmp_data.Width;
bmi.bmiHeader.biHeight = bmp_data.Height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;

HDC hDC = GetDC(NULL);
hBmp = CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT, (void *)bmpPtr, &bmi, DIB_RGB_COLORS);

bitmap->UnlockBits(&bmp_data);
delete bitmap;
delete bmp;

return hBmp;
}

and this is the one that actually draws it:

	 HDC	 hMemDC = CreateCompatibleDC(hDC);
HGDIOBJ hOld = SelectObject(hMemDC, hBmp);


HBRUSH hBrush = CreateSolidBrush(RGB(0xFF, 0xFF, 0));
SelectObject(hDC, hBrush);
Ellipse(hDC, x, y, x+width*2, y+height*2);
SelectObject(hDC, GetStockObject(WHITE_BRUSH));
DeleteObject(hBrush);

BitBlt(hDC, x, y, width, height, hMemDC, 0, 0, SRCCOPY);

BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 225, AC_SRC_ALPHA };

AlphaBlend(hDC, x, y+height, width, height, hMemDC, 0, 0, width, height, pixelblend);

BLENDFUNCTION blend50 = { AC_SRC_OVER, 0, 128, 0 };

AlphaBlend(hDC, x+width, y, width, height, hMemDC, 0, 0, width, height, blend50);

SelectObject(hMemDC, hOld);
DeleteObject(hMemDC);

alpha-1 is the bitmap rendered by retrieving HBITMAP from

bitmap->GetHBITMAP(backColor, &hBmp);

alpha-2 is the bitmap as rendered by the previous function.

post-79521-1228283346_thumb.jpg

post-49-1228283346_thumb.jpg

alpha-1.jpg
alpha-1.jpg
alpha-2.jpg
alpha-2.jpg

#516521

First:

Gdiplus::ImageLockModeWrite

You should be using Gdiplus::ImageLockModeRead. You are really lucky you got it working even with the wrong param ;)

About the way you draw it. you shouldn't do it that way as far as I know.

Instead you should use UpdateLayeredWindow, you'll need to do a PInvoke there, or use InteropServices.

The code would look something like this:

HDC hMemDC= CreateCompatibleDC (hDC);
hOld = SelectObject (hMemDC, hBmp);

BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;

UpdateLayeredWindow (hDlg, hDC, NULL, NULL, hMemDC, NULL, 0, bf, ULW_ALPHA);

SelectObject (hMemDC, hOld);
DeleteObject (hMemDC);

Edit

Oh, wait, you're using C++, not C#? Then forget my comment about InteropServices or PInvoke :P. But try UpdateLayeredWindow as told.

#516544

I have changed the code to use ImageLockModeRead, but there is no difference in the output... the bitmap is being rendered upside-down with a black background? How could this be? The original bitmap has a transparent background not black.

Edit: I solved the firs issue (the upside-down problem) by reading the MSDN docs for BITMAPINFOHEADER it seems that I shoud set the bmiHeader.biHeight to a negative value to get a top-down bitmap... odd isn't it? well changing this line to:

bmi.bmiHeader.biHeight = bmp_data.Height;

solves the issue... still trying to figure out why the black background.

#516555

How come? What's wrong with this approach?

About the way you draw it. you shouldn't do it

that way as far as I know.

#516561

For layered per pixel alpha windows (which is what I suppose you're trying to do) you don't draw to a windows' device context.

Instead, you draw to a memory bitmap (which has same dimensions than the window), then you use UpdateLayeredWindow with the memory bitmap.

I have no idea why we can't directly draw to a window device context to achieve per pixel transparency. Microsoft tells us to use WS_EX_LAYERED for exStyle, and UpdateLayeredWindow for stuff.

This is the code I use (but it is in Delphi / pascal, not in C++), you can create a window and call Update ( gdiplusbitmap ) to update the window contents:

Oops, too long, use attachment

I can try to translate this into C++, but I wouldn't know how to test it (well, I would, but with Microsoft Visual Studio 2005 C++)

unitLayeredWindow.pas.txt

Attachment: unitLayeredWindow.pas.txt

#516569

Thanks matonga... you've been so helpful (and patient ;)), I think I will take a look at your code... in my current test app the window is not a layered one, it's just a regular window where I am testing the alpha-blending code... I think that if I set the style to this window to layered then I will have trouble with it (I will test it anyway just to see what happens)...

Again, thank you very much!

For layered per pixel alpha windows (which is what I suppose you're trying to do) you don't draw to a windows' device context.

Instead, you draw to a memory bitmap (which has same dimensions than the window), then you use UpdateLayeredWindow with the memory bitmap.

I have no idea why we can't directly draw to a window device context to achieve per pixel transparency. Microsoft tells us to use WS_EX_LAYERED for exStyle, and UpdateLayeredWindow for stuff.

This is the code I use (but it is in Delphi / pascal, not in C++), you can create a window and call Update ( gdiplusbitmap ) to update the window contents:

Oops, too long, use attachment

I can try to translate this into C++, but I wouldn't know how to test it (well, I would, but with Microsoft Visual Studio 2005 C++)

#516575

You'll have problems if you want to put textboxes, buttons, etc... in the window.

In effect, the window will only be useful to do per pixel alpha blending of an image with other windows behind it (like in docks and widgets).

BTW a little trick to put textboxes, buttons, etc...:

Create two windows, one layered, one not layered, and put the layered window behind the non-layered one.

Then use key-color in the non-layered window to remove the background.

Don't forget to move both windows at the same time when the user tries to move "the window".

Edit

I forgot to mention, just glad to help and don't fear to ask whatever you come with.

#516583

Thanks for the tip... I was about to ask that since I have read several posts stating that having child windows (i.e. controls on a Layered window is a real pain in the b..., but your trick is really helpful).... I'll keep this thread updated, it may come handy for others.

Edit: Just one question... since VM_PAINT will not be raised for a layered window, where am I supposed to call this method? Looking at your Delphi class I noticed the Update method which carries the call to UpdateLayeredWindow, so it's responsibility of the calling code to call Update? If so, how could that code be able to handle the window's life cycle... for example, if another window is taken on top of the layered one who is going to call Update to repaint the window when the other one is moved away... I do not get this.

Edit #2: Im trying to implement the UpdateLayeredWindow stuff but I am getting nothing:

void UpdateDialog(HWND hWnd, Gdiplus::Bitmap *bitmap, BYTE SourceConstantAlpha)
{
if (!(::GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED))
return;

HBITMAP hBmp = GetAlphaChannelBitmap(bitmap, false);

HDC hScreenDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hScreenDC);
HGDIOBJ hOld = SelectObject(hMemDC, hBmp);

BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = SourceConstantAlpha;
bf.AlphaFormat = AC_SRC_ALPHA;

UpdateLayeredWindow(hWnd, hScreenDC, NULL, NULL, hMemDC, NULL, 0, &bf, ULW_ALPHA);

SelectObject(hMemDC, hOld);
DeleteObject(hMemDC);
}

where GetAlphaChannelBitmap does the stuff to retrieve the HBITMAP from a Gdiplus Bitmap object (that is the previous code that was working fine). And I call UpdateDialog just after creating the window:

	WNDCLASSEX  WndCls;
static WCHAR szAppName[] = L"LayeredWindow";

WndCls.cbSize = sizeof(WndCls);
WndCls.style = CS_DBLCLKS; //CS_VREDRAW | CS_HREDRAW;
WndCls.lpfnWndProc = WindProcedure;
WndCls.cbClsExtra = 0;
WndCls.cbWndExtra = 0;
WndCls.hInstance = hInstance;
WndCls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hbrBackground = (HBRUSH)GetStockObject(GetSysColor(CTLCOLOR_DLG));
WndCls.lpszMenuName = NULL;
WndCls.lpszClassName = szAppName;
WndCls.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
RegisterClassEx(&WndCls);

HWND hWnd = CreateWindowEx(0, L"LayeredWindow", L"Layered Window Test",
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 240, NULL, NULL, hInstance, NULL);

SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);

ShowWindow(hWnd, SW_SHOW);

WIN32_FIND_DATA FindFileData;
FindFileData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
HANDLE handle = FindFirstFileEx(L"background.png", FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
Gdiplus::Bitmap *bitmap;
if (handle != INVALID_HANDLE_VALUE)
{
FindClose(handle);
bitmap = new Gdiplus::Bitmap(L"background.png");
}

UpdateDialog(hWnd, bitmap, 255);

RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);

but all I am getting is a big black opaque square.

#516619

Windows stores a copy of the bitmap given in UpdateLayeredWindow. Then Windows "repaints" the window whenever another window uncovers it (it doesn't work exactly like that, though). Layered windows don't work like normal GDI windows. They're not repainted by you, but by Windows itself.

All you need to do is call UpdateDialog whenever you need to change the image.

About the big black square, I'm not sure. Do the hBmp and the window have exactly the same size?

#516652

Actually the window will be bigger... since I will render several bitmaps there... I plan to create a PNG based "frame" so the window will be skinned (take a look at my Daily Dilbert Cartoon thread here http://www.aqua-soft.org/board/showthread.php?t=50775 to see what I mean). So I believe I will be "painting" different PNG's to the window and then show it.

Another thing... I have modified my previous application to do call the previous UpdateDialog method... since right now there will be nothing else to do with the window but to show it I believe that after creating the window is the right place to call UpdateDialog. What I have seen is that the VM_PAINT message is still being sent to the window... it was my understanding that this message would not be raised anymore whenever I ser the WS_LAYERED_EX extended style to the window... is this true or it's that I just need not to process it anymore?

#516653

I have another question... I have returned to the original project and just modified the CreateWindowEx call to include WS_EX_LAYERED style. I found the application still works without further modification, that is WM_PAINT events are being sent, and Draw and the

AlphaBlend method is being used... and no problem occurs. How come?

Edit: For get it... I was not doing setting the WS_EX_LAYERED style properly, so it was not being applied (I checked it out with spy++... after correcting it, the window is not being shown).

#516680

You don't need to process WM_PAINT anymore.

To put many different PNGs in the same window, do this:

Create a bitmap as big as the window (exactly same dimensions).

Copy the PNGs to the bitmap, in the position where they should be displayed (i.e. do as if the bitmap coordinates equals the window client area coordinates).

Then use the bitmap to call UpdateDialog.

I think UpdateLayeredWindow let's you specify regions of the window to update. You should try that instead, adding POINT destination and SIZE size to UpdateDialog and to corresponding parameters in UpdateLayeredWindow and you should be fine.

#516693

If the bitmap is not the same size as the window, would the UpdateLayeredWindow call fail?

#516697

Well I must be doing something wrong, it's just I simply do not kwnow what could that be... maybe id you could take a look at my code you could spot the error...

if I take out the code to make the window transparent and comment out the UpdateDialog call (in InitInstance) then the windows shows as usual with the bitmap rendered (by the VM_PAINT message), but making it a layered window and uncommenting the call to

UpdateWindow (which calls UpdateLayeredWindow) does not show anything... the window is there (spy++ shows it) but no output at all.

alpha.cpp.txt

Attachment: alpha.cpp.txt

#516708

I tried to test your file in Visual Studio but it says stdafx.h is missing.

Tried commenting it, added #include , etc... defining lots of strings :D, adding gdiplus.lib, and it got really hard!

Can you upload the full project? If it contains code not to be published, make a new project with just the code for UpdateLayeredWindow and stuff.

#516726

Sorry my mistake... here you go (it's for VC++ 2005)

If you get in trouble compíling it include this line in StdAfx.h

#define _WIN32_WINNT 0x0500

alphablendtest.zip

Attachment: alphablendtest.zip

#516760

Hi, yes I had to add the #define in StdAfx.h... don't know why.

Here is working alpha.cpp file ;)

Let's see:

In UpdateWindow, I made two important modifications:

1. Resize the window to match the size of the bitmap.

2. I forgot to mention you can't pass NULL to UpdateLayeredWindow parameters. This is my fault because I exampled such usage above... sorry.

alpha.cpp.txt

Attachment: alpha.cpp.txt

#516768