You don't have to worry about differences between a DIB and a normal bitmap.
DIB = Device Indipendent Bitmap.
The regular bitmaps are Device-Dependent (Can only be used on a single HDC), where the DIB ones are Device-Independent (Can be used on every HDC).
If you want to keep using GDI, discard the layered window idea.
If you want the layered window, discard GDI (The only things you still need of GDI are the ones I wrote above).
GDI is old and is mostly for 24bit bitmaps (Where, for layered windows you need 32bit bitmaps, as I created above).
My suggestions are:
1. Draw to a buffer of pixels:
You're actually writing to the bitmap's memory, this means you're probably doing everything in software and you will have to deal yourself with sizes.
I don't suggest this unless your resolution is fixed, you can easily access by mistake a wrong memory address, so I'll skip this.
2. Draw with D2D1 instead of GDI:
You're going to be hardware-accelerated: This means you will have very few troubles with memory access, but a device failure and you need to rebuild every resource you created.
Actually, I do suggest this, so I'll provide you with a snippet which extends the old one (You need the DirectX SDK):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
#include <d2d1.h>
// If you're on a MS compiler, link your program to the required libraries
// Otherwise see your IDE's instructions on how to link a library
#ifdef _MSC_VER
# pragma comment(lib, "d2d1.lib")
# pragma comment(lib, "dwrite.lib")
# pragma comment(lib, "d3d9.lib")
# pragma comment(lib, "d3dx9.lib")
#endif
typedef IDWriteTextFormat ID2D1Font;
// Required for DWriteFactory
#ifndef IID_IDWriteFactory
GUID IID_IDWriteFactory = {
0x25BA9269, 0x9AB0, 0x4B2F,
{
0xA7, 0x0E, 0x77, 0xE9, 0x06, 0x72, 0x42, 0x26
}
};
#endif
// Stores per-application data
// It can also be per-window data if you're too lazy to split them,
// or if you only have one window.
struct GlobalData {
GlobalData() : pD2DFactory(0), pDWFactory(0) {}
ID2D1Factory * pD2DFactory;
IDWriteFactory * pDWFactory;
};
// Stores per-window data
struct WindowData {
// initialize them
WindowData() : MemDC(0), DIBSectionBitmap(0), OldDC(0), Handle(0), pRT(0) {}
ID2D1RenderTarget * pRT;
HWND Handle;
HDC MemDC;
HBITMAP DIBSectionBitmap;
HDC OldDC;
};
void FreeGlobalData(GlobalData& gd)
{
if(gd.pD2DFactory)
{
gd.pD2DFactory->Release();
gd.pD2DFactory = 0;
}
if(gd.pDWFactory)
{
gd.pDWFactory->Release();
gd.pDWFactory = 0;
}
}
bool Initialize(HWND Handle, GlobalData& gd, WindowData& wd)
{
if(!gd.pD2DFactory || !gd.pDWFactory)
{
D2D1_FACTORY_OPTIONS options;
options.debugLevel =
// Activate debug informations depending on project settings
#ifdef _DEBUG
D2D1_DEBUG_LEVEL_INFORMATION;
#else
D2D1_DEBUG_LEVEL_NONE;
#endif
if(D2D1CreateFactory<ID2D1Factory>(D2D1_FACTORY_TYPE_SINGLE_THREADED,
options, &(gd.pD2DFactory)) != S_OK || !gd.pD2DFactory)
return 0;
IUnknown * pStore = 0;
if(FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,IID_IDWriteFactory,
&pStore)) || !pStore)
return 0;
gd.pDWFactory = (IDWriteFactory*)pStore;
}
wd.Handle = Handle;
RECT r = {0};
GetWindowRect(Handle,&r);
HDC scrDC = GetDC(0);
wd.MemDC = CreateCompatibleDC(scrDC);
ReleaseDC(0,scrDC);
if(!wd.MemDC)
return 0;
BITMAPINFO bmi = {0};
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biWidth = r.right-r.left;
bmi.bmiHeader.biHeight = r.bottom-r.top;
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
wd.DIBSectionBitmap = CreateDIBSection(MemDC,&bmi,DIB_RGB_COLORS,0,0,0);
if(!wd.DIBSectionBitmap)
return 0;
wd.OldBmp = (HBITMAP)SelectObject(wd.MemDC,wd.DIBSectionBitmap);
ID2D1HwndRenderTarget * hwndRT = 0;
D2D1_RENDER_TARGET_PROPERTIES props;
props.dpiX = 0.f;
props.dpiY = 0.f;
props.minLevel = D2D1_FEATURE_LEVEL_9; //Dx9 features
props.type = D2D1_RENDER_TYPE_DEFAULT;
props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
D2D1_HWND_RENDER_TARGET_PROPERTIES hrtprops;
hrtprops.hwnd = Handle;
hrtprops.presentOptions = D2D1_PRESENT_OPTIONS_IMMEDIATELY;
RECT rcc = {0};
GetClientRect(Handle,&rcc);
hrtprops.pixelSize.width = rcc.right-rcc.left;
hrtprops.pixelSize.height = rcc.bottom-rcc.top;
if(gd.pD2DFactory->CreateHwndRenderTarget(props,hrtprops,&hwndRT) != S_OK ||
!hwndRT)
return 0;
wd.pRT = hwndRT;
return 1;
}
// function Paint has no changes
void Free(WindowData& wd)
{
if(wd.pRT)
wd.pRT->Release();
if(wd.MemDC && wd.OldBmp)
SelectObject(wd.MemDC,wd.OldBmp);
if(wd.DIBSectionBitmap)
DeleteObject(wd.DIBSectionBitmap);
if(wd.MemDC)
DeleteDC(wd.MemDC);
memset(&wd,0,sizeof(wd));
}
ID2D1Font * CreateD2D1Font(GlobalData& gd, const wchar_t * FontName, float Size,
DWRITE_FONT_WEIGHT wgt, DWRITE_FONT_STYLE sty,
DWRITE_FONT_STRETCH str, const wchar_t * Locale)
{
if(!gd.pDWFactory)
return 0;
ID2D1Font * pDWTF = 0;
if(FAILED(gd.pDWFactory->CreateTextFormat(FontName,0,wgt,sty,str,Size,Locale,&pDWTF)) || !pDWTF)
return 0;
return pDWTF;
}
LRESULT OnMessage(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if(Msg == WM_NCHITTEST)
return HTNOWHERE; // ignore clicks
else if(Msg == WM_PAINT)
{
WindowData * yourWindowData = GetWindowData(Window); // just get it with your own function
yourWindowData->pRT->BeginDraw();
yourWindowData->pRT->Clear(D2D1::ColorF(0.f,0.f,0.f,0.f)); // clear window to 0 alpha
// use yourWindowData->pRT to draw here
yourWindowData->pRT->EndDraw();
// now call "Paint" function I wrote above
}
// ... your window proc's other stuff
}
|
@andy: Never draw to the desktop, I don't think Windows likes that, and as you said, it flickers.
UpdateLayeredWindow fixes the flickering and draws on your desktop for you in the correct (and version-portable) way.
I've also provided you a CreateD2D1Font and set you up for creation of fonts (It's not as straight-forward: By default you don't need a DirectWrite factory, but you need it for fonts, and as it took me a while to set myself up with it, I wrote it there).
If you don't know how COM works:
Every time you want to free an object, call COM_Object->Release().
Usually you would check for EndDraw's return value, to see if you have to rebuild all rendertarget-dependent things.
But for testing purposes, there's no need.
This code, as the last one, was written on the fly.
If it has errors, tell me and I'll solve them.
If you want to see how I actually work with my projects, that's a different way of doing it (as it's MFC-ish).
If you're interested:
/!\ THESE FILES ARE EXECUTABLES! ALWAYS PERFORM A FULL ANTIVIRUS SCAN BEFORE OPENING THEM
/!\
https://dl.dropboxusercontent.com/u/83943521/Apps/layered.rar
(Pick an AUDIO file, just MP3, OGG or WAV)
I can give you the full source code, but I'm sure it will only work on MS's VS (I use VS10).
But it's completely native C++, it doesn't use MFC/ATL/.NET and should compile on the Express editions too.
Oh, and I'm not a geisha, I'm "SGH", as extended: Ess Gee Eich :>