In this example, I'll demonstrate how to render a texture with GDI. You may wonder why I need to do that anyway as texture is generally used to render shapes in DirectX. The idea here is if I use the layered window with a transparent key color, the color in the texture, when finally rendered using GDI, will be transparent so I see everything behind my program window. Using only DirectX will not produce the same effect.
Let's start with a quick introduction to the layered window. Layed window is created by calling CreateWindowEx with label WS_EX_LAYERED. After the window is created, I can use either SetLayeredWindowAttributes or UpdateLayeredWindow to actually show the window. By calling SetLayeredWindowAttributes, I need to process the rending in the WM_PAINT message. As I'm going to use GDI mixed with DirectX, I want to have total control on the rending process, I'll use the UpdateLayeredWindow instead. This function saves me from the WM_PAINT message. I can call it whenever I finished the drawing.
Next I'll do the DirectX initialization, where I'll initialize the divece and especially, I'll create the texture. For simplicity, I create the texture from a bmp file using D3DXCreateTextureFromFile. The bmp file I'm using contains in the part where I want it transparent a special color, e.g. RGB(255, 128, 64). I'll specify the same color as the color key when calling the UpdateLayeredWindow.
After the texture is created, I'll retrieve the level 0 surface associated to it. This is done by simply calling the texture's GetSurfaceLevel method.
As I'm going to render the texture with GDI, I need two DC handles to call the UpdateLayeredWindow, one is the source and the other is the destination. I already have a surface ready to be presented on the screen, I need to get the DC associated with it. This is done by calling the surface's GetDC method. If the call succeeds, the source is ready. Then I still need to prepare the destination. That is simply a call to global GetDC.
There are still some parameters to prepare in order to call UpdateLayeredWindow. The BLENDFUNCTION sturcture contains information about the blend I want to do with the contents to be rendered. I give the following values:
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 255;
I need to specify also the new position, size of the new window. Be careful here, I need to retrieve the size of the bmp file I have loaded as it will be my entire window. What's more, give a size larger than the actual size will not work. Currently, I load the bitmap and get the size information from it, I'm not aware the way to get this information from the texture which is created from the bmp also. So I actually load twice the bmp file.
Next, the location of the layer in the source DC, which is usually (0,0).
Finally, I set the transparent color as the color key and the ULW_COLORKEY as the flag and call the UpdateLayeredWindow.
The whole process is:
- Create a layered window.
- Load bmp into texture.
- Get surface 0 of the texture.
- Get DC of the surface.
- Get DC of the destination (Desktop)
- UpdateLayeredWindow with source DC as the DC of the surface, destination DC as the Desktop.
Something I need to note, with D3DXCreateTextureFromFile, I can always get the DC of the surface 0 associated. But with a texture created by CreateTexture, I can fail sometimes when get the DC. This is due to the usage and memory pool used by the methods. D3DXCreateTextureFromFile always uses D3DPOOL_MANAGED. (Cited from DirectX Doc for this method: "
Note that a resource created with this function will be placed in the memory class denoted by D3DPOOL_MANAGED.) And the GetDC method of the surface has several limitations and especially, it will fail on D3DPOOL_DEFAULT unless the usage is dynamic (D3DUSAGE_DYNAMIC).
I'll investigate more on this issue. But the idea is clear, once I have surface from which I can get its device context, I'll be able to render it to a layered window. On the other side, I need also be able to render on the surface, obviously. So I'll need a surface that I can render on it and can get DC from it. I need to see if this is actually possible, otherwise, maybe I can render on on surface and put the result to another one from which I can get the DC.