PDA

View Full Version : New Layered Window Example



rombust
10-05-2009, 12:54 PM
For upcoming ClanLib 2.0.5

A new layered window example

See attached

rombust
10-06-2009, 02:48 PM
Updated the example, so it's more useful :)

See attached image.

You can drag the rock around the screen

The rotating penguin moves around the rock

It demonstrates, that you can have smooth animation with opengl using layered windows. Although, it would be better to use GDI - If opengl is not required

This could be used as a splash screen, or a fancy "set options screen"

rombust
10-08-2009, 12:22 PM
And yet another example...

A different technique at using layered windows...

OpenGL displaying 256 sprites

It creates 5 windows as follows

AAAAA
B###C
B###C
DDDDD

A,B,C,D are layered windows
# is the main window.

rombust
10-09-2009, 08:54 AM
We have a little problem with layered windows.

At the monent, we have:


CL_Win32Window::create_new_window()
{
...
SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 0, LWA_COLORKEY);
...
}

This is very limiting for 2 reasons.

First, if we translucent windows:


SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 128, LWA_ALPHA);

There is no API to set it

Secondly, this would cause UpdateLayeredWindow() to fail ( http://msdn.microsoft.com/en-us/library/ms633556(VS.85).aspx )
because it is not compatible with SetLayeredWindowAttributes() unless the layering style bit is cleared and set again.

Also it maybe faster to use UpdateLayeredWindow() than WM_PAINT - For partial or full update of the window.

Finally. UpdateLayeredWindow() can use the alpha channel.

Anyone want to explore????

See attached image of "128, LWA_ALPHA"

Magnus Norddahl
10-10-2009, 01:27 AM
I actually thought that it was using UpdateLayeredWindow for the rendering.

At least with the GDI target, I'd assume this is both faster and better than using the other function. The UpdateLayeredWindow function has the advantage that you can use the alpha channel of the backbuffer as transparency, instead of using a color key or fixed global transparency.

The only real catch about it is that the image blitted to the bitmap DC (using SetDIBitsToDevice) must be in pre-multiplied ARGB format.

clanGDI already uses the ARGB format for its backbuffer, but the alpha channel is not premultiplied, so that change would have to be applied before calling SetDIBitsToDevice for that target.

I have some old code that shows how to use this function with pre-multiplied alpha, and it looks like this:


void LayeredWindow::update_layered(const CL_PixelBuffer &image, const CL_Color &colorkey, int window_alpha, bool use_colorkey)
{
// Convert pixel buffer to DIB compatible format:
CL_PixelFormat argb8888(
32,
0x00ff0000,
0x0000ff00,
0x000000ff,
0xff000000);

int pitch = image.get_width() * 4;
CL_PixelBuffer bmp_image(image.get_width(), image.get_height(), pitch, argb8888);

image.convert(
bmp_image.get_data(), bmp_image.get_format(), pitch,
CL_Rect(0, 0, bmp_image.get_width(), bmp_image.get_height()));

// Note that the APIs use pre-multiplied alpha, which means that the red,
// green and blue channel values in the bitmap must be pre-multiplied with
// the alpha channel value. For example, if the alpha channel value is x,
// the red, green and blue channels must be multiplied by x and divided by
// 0xff prior to the call.
int w = bmp_image.get_width();
int h = bmp_image.get_height();
cl_uint32 *p = (cl_uint32 *) bmp_image.get_data();
for (int y = 0; y < h; y++)
{
int index = y * w;
cl_uint32 *line = p + index;
for (int x = 0; x < w; x++)
{
cl_uint32 a = ((line[x] >> 24) & 0xff);
cl_uint32 r = ((line[x] >> 16) & 0xff);
cl_uint32 g = ((line[x] >> 8) & 0xff);
cl_uint32 b = (line[x] & 0xff);

r = r * a / 255;
g = g * a / 255;
b = b * a / 255;

line[x] = (a << 24) + (r << 16) + (g << 8) + b;
}
}

BITMAPV5HEADER bmp_header;
memset(&bmp_header, 0, sizeof(BITMAPV5HEADER));
bmp_header.bV5Size = sizeof(BITMAPV5HEADER);
bmp_header.bV5Width = bmp_image.get_width();
bmp_header.bV5Height = -bmp_image.get_height();
bmp_header.bV5Planes = 1;
bmp_header.bV5BitCount = 32;
bmp_header.bV5Compression = BI_RGB;

HDC hdc = GetDC(hwnd);
HDC bitmap_dc = CreateCompatibleDC(hdc);
HBITMAP bitmap = CreateDIBitmap(hdc, (BITMAPINFOHEADER *) &bmp_header, CBM_INIT, bmp_image.get_data(), (BITMAPINFO *) &bmp_header, DIB_RGB_COLORS);
HGDIOBJ old_bitmap = SelectObject(bitmap_dc, bitmap);

SIZE size = { bmp_image.get_width(), bmp_image.get_height() };
POINT point = { 0, 0 };
COLORREF rgb_colorkey = RGB(colorkey.get_red(), colorkey.get_green(), colorkey.get_blue());
BLENDFUNCTION blend;
memset(&blend, 0, sizeof(BLENDFUNCTION));
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = (BYTE) window_alpha;
blend.AlphaFormat = AC_SRC_ALPHA;
if (use_colorkey)
UpdateLayeredWindow(hwnd, 0, 0, &size, bitmap_dc, &point, rgb_colorkey, &blend, ULW_COLORKEY);
else
UpdateLayeredWindow(hwnd, 0, 0, &size, bitmap_dc, &point, rgb_colorkey, &blend, ULW_ALPHA);

SelectObject(bitmap_dc, old_bitmap);
DeleteObject(bitmap);
DeleteDC(bitmap_dc);
ReleaseDC(hwnd, hdc);
}


I personally do not see the advantage in using SetLayeredWindowAttributes, unless that somehow magically would allow OpenGL to use wglSwapBuffers, but I don't think it works that way.

Magnus Norddahl
10-10-2009, 01:28 AM
Needless to say, this pre-multiplied step could be done with some SSE code. :)

rombust
10-10-2009, 08:48 AM
Yes, that's the best way to do it.

And because, GL, GL1 and GDI produce a CL_PixelBuffer ready for the layered window, we could simply add a CL_Win32Window::update_layered() using your code.

I was worried about the speed with creating the pre-multiplied alpha,, and relying on WM_PAINT doing it automatically. But, initial tests showed that is was about the same speed.

Looking it http://www.codeproject.com/KB/GDI/pxalphablend.aspx ... It's a must :)


I personally do not see the advantage in using SetLayeredWindowAttributes, unless that somehow magically would allow OpenGL to use wglSwapBuffers, but I don't think it works that way.

No, it does not perform magic. You still have to have the opengl shadow window that is rendered to.

rombust
10-10-2009, 08:57 AM
Also, remember to remove
SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 0, LWA_COLORKEY);
from CL_Win32Window::create_new_window()

I forgot, and wasted an hour trying to work out why the UpdateLayeredWindow() returned a parameter is incorrect error
:o

rombust
10-12-2009, 03:24 PM
Done!

2 Problems:

1) At the moment window.update() forces the entire window to update for layered windows.

2) GDI layered window support is not implemented
(Use CL_Win32Window::update_layered() )

See attached