Redraw Desktop WinAPI

Pages: 12
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 :>
Last edited on
@andy:
in the link you provided for MSDN about Direct2D:
While both GDI and GDI+ continue to be supported in Windows, Direct2D and DirectWrite are recommended for new programs. In some cases, a mix of technologies might be more practical. For these situations, Direct2D and DirectWrite are designed to interoperate with GDI.



this makes it pretty clear i guess, new code should be using Direct2D and as much as possible avoid the use of GDI, that means i MUST learn Direct2D.

but note that:
Direct2D and DirectWrite are designed to interoperate with GDI.



that means i must learn GDI to get the full potential of the new Direct2D, and also so i can read older code.

@SGH:
sorry if i miss-read your name. :)
and ehh... did i say (i'm a beginner) yet ???
I wrote:
i'm really so new to all this GDI thing, i don't even know what is the difference between a DIB and a normal bitmap.


what i meant was : i don't know anything.
my whole experience in WinAPI is just a bit over a week.


EDIT :
so SGH, you suggest i make a small window in the size of the bitmap and move that window wherever i want to draw the bitmap, right?
if this is the case, i can use this to apply another effect.
the old design couldn't possibly recognize when the mouse is over the bitmap or even close to it, with this new design i can catch the mouse-over event to make the ball slide by the mouse instead of going through it.
Last edited on
Well, I'll explain it a bit differently:
Every time you create a standard Window, a new "Bitmap" is assigned to it.
To this bitmap, you can draw whatever you want: Text, Images...
But, the default Bitmap is an optimized one.
You cannot think every default bitmap is able to display transparent images.
So, we have to FORCE the creation of a DIB bitmap, which, in my example, is allowed to have transparent images.

By the way you should play with simpler things first, before jumping into transparent windows.

Firstly, make sure you understand a bit how OOP works (If you didn't already).
Then, make some windows, some Simple ones, where there's no transparency involved.
Add some buttons, try to understand which key the user is currently pressing (Without a console).
Then, I'd suggest you to make a DOUBLE-BUFFERED window.
This will allow you to make very fast window draws, with less flickering.

Once you've "gone doublebuffering", you could try to "get back" into the Layered Windows topic.

If you go for the learning of those, feel free to bump this once you're ready.
Otherwise, you can still keep asking, but I'm almost sure the answers will be vague.
@SGH:
before i started with API my whole focus was on the standard features of the language, so now i have good enough understanding of OOP and how to apply it efficiently.

i did some really simple windows, some contained menus, others didn't.
i did some dialog box operations.
however,i didn't do any window that contains edits, buttons, combo boxes ..etc.

i'm sure this should be my focus point for the next few weeks in order to feel comfortable with the API.

about bitmaps, i did some simple projects that prints bitmaps into a normal window.
the first contained normal bitmap, the second contained a transparent one.
the third project applied double buffering.

there's one little problem, i just don't want to get really deep into graphics programming, because in the end, being a game programmer is my second goal.
my first goal is to discover lots of really tricky in's and out's of the Operating System, so i can manipulate it as i desire.
focusing on graphics might forbid me this goal.

for now, i'll stick with basic windows, maybe create GUI for my old console apps.
maybe two weeks later i could try layered windows to give my project some better look.

my great thanks to you SGH and andywestken for your support.
Best Wishes.
Well, as I know which kind of issues I had with game development, I cannot do more than wish you Good Luck.
Topic archived. No new replies allowed.
Pages: 12