Graphic drwan by GDI keep flashing....

I am trying to make the rectangle drawn by GDI will move with mouse cursor when user clicked the rectangle. And i am using this source as guide,

http://gfxguru.org/prog/kk/windows_gdi/windows_gdi.php

Its example don't have that flashing bug, and look pretty smooth, but mine keep flashing, and i think because of this flashing my dragging code is not working.


here is my code:
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
///main.cpp

#include <windows.h>
#include "obj.h"

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);


    Obj obj1(hwnd,0,0,25,25),obj2(hwnd,200,0,225,25);

    while (true)
    {
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (PeekMessage (&messages,NULL,0,0,PM_REMOVE))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
        RECT r;
        GetWindowRect(hwnd, &r);
        bool LBDown = 0;

        if (messages.message == WM_QUIT) break;
        if (messages.message == WM_LBUTTONDOWN){if (LBDown == false){LBDown = true;}}
        if (messages.message == WM_LBUTTONUP){if (LBDown == true){LBDown = false;}}

        if (LBDown == true)
        {
            obj1.ObjCheckMouseEnter(r.left, r.top);
            obj1.ObjMoveWithMouse(r.left, r.top);
        }

        obj1.ObjUpdate(hwnd);
        obj2.ObjUpdate(hwnd);

        InvalidateRect(hwnd, NULL, 1);

    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}


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
//obj.h


#include <iostream>
using namespace std;

class Obj
{
    HDC hdc;
    POINT coord1;
    POINT coord2;
    int width, height;
    bool MEnter;
    POINT m;
    //char StrData[10] = "abc\0";

    public:
        Obj(HWND h, int x1, int y1, int x2, int y2);
        void ObjUpdate(HWND h);
        void ObjChangeCoord1(int x, int y);
        void ObjChangeCoord2(int x, int y);
        void ObjMoveWithMouse(int rx, int ry);
        void ObjLButtonDown();
        void ObjCheckMouseEnter(int rx, int ry);

        void ObjMoveToMouse();
};

Obj::Obj(HWND h, int x1, int y1, int x2, int y2)
{
    hdc = GetDC(h);
    ObjChangeCoord1(x1,y1);
    ObjChangeCoord2(x2,y2);
    Rectangle(hdc, coord1.x, coord1.y, coord2.x, coord2.y);
    MEnter = false;
}
void Obj::ObjUpdate(HWND h)
{
    GetDC(h);
    Rectangle(hdc, coord1.x, coord1.y, coord2.x, coord2.y);
}

void Obj::ObjChangeCoord1(int x, int y)
{
    coord1.x = x;
    coord1.y = y;
}
void Obj::ObjChangeCoord2(int x, int y)
{
    coord2.x = x;
    coord2.y = y;
    width = coord2.x - coord1.x;
    height = coord2.y - coord1.y;
}
void Obj::ObjMoveToMouse()
{
    GetCursorPos(&m);
    coord1.x = m.x - 8;
    coord1.y = m.y  - 31;
    coord2.x = m.x  - 8 + width;
    coord2.y = m.y  - 31 + height;
}
void Obj::ObjCheckMouseEnter(int rx, int ry)
{
    if ( (coord1.x < m.x-rx-8)&(coord2.x > m.x-rx-8)&(coord1.y < m.y-ry-31)&(coord2.y > m.y-ry-31) )
    {
        MEnter = true;
    }
    else{MEnter = false;}

}
void Obj::ObjMoveWithMouse(int rx, int ry)
{
    GetCursorPos(&m);
    if (MEnter == true)
    {

        coord1.x = m.x-rx-8;
        coord1.y = m.y-ry-31;
        coord2.x = m.x-rx-8+width;
        coord2.y = m.y-ry-31+height;

    }
}



this is the code of one of the example (gdi_Box2BoxCollision):
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
154
#include<windows.h>      //header file for window applications
#include<math.h>
#include<stdio.h>
/*
Program Name:- gdi_Box2BoxCollision
Compiler Used:- MS VC++ 2005 Express
Website:-Gfxguru.Net
Programmer:-krishx007
E-MAIL: krishx007@yahoo.com
*/



//function which processes all the windows messages
LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
HDC hDC;  
RECT ClientRect;

//Every Win32 application(not Console) starts with the execution of WinMain
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
	WNDCLASSEX wc;  //every window is created from windows class
	MSG msg;      //message structure variable
	HWND hWnd;    // handle to window

	// clear out the window class for use
	ZeroMemory(&wc, sizeof(WNDCLASSEX));


	// fill in the struct with the needed information
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW  ;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground =  (HBRUSH)(GetStockObject(BLACK_BRUSH));
	wc.lpszClassName = L"MyClass1";
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

	//registering the class
	RegisterClassEx(&wc);

	//creating the window
	hWnd=CreateWindowEx(NULL,L"MyClass1",L"krishx007@yahoo.com          www.Gfxguru.Net",WS_SYSMENU,
		10,10,800,600,NULL,NULL,hInstance,NULL);
	//SetBkColor(hDC,RGB(0,0,0));
	

	ShowWindow(hWnd,nShowCmd);

	hDC=GetDC(hWnd);
	GetClientRect(hWnd,&ClientRect);

	
	POINT MousePos;

	HBRUSH hRedBrush,hGreenBrush;
	hRedBrush = CreateSolidBrush(RGB(255,0,0));
	hGreenBrush = CreateSolidBrush(RGB(0,200,0));

	int x1,y1,x2=150,y2=200;
	int a1=300,b1=300,a2=500,b2=500;

	//Message Loop which detects all the messages and sends to ProcessMessagefn
	while(TRUE)
	{

		//if any message is sent by windows
		while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if(msg.message==WM_QUIT) break;

//-------------------------------------------
			SetBkMode(hDC,TRANSPARENT);

			GetCursorPos(&MousePos);
			int mousex=MousePos.x;
			int mousey=MousePos.y;
		

			x1=mousex;
			y1=mousey;
			x2=x1+100;
			y2=y1+150;
			
			
			Rectangle(hDC,a1,b1,a2,b2);//Rectangle 1 guided by mouse
			Rectangle(hDC,x1,y1,x2,y2);//Rectangle 2
			
			TextOut(hDC, 0, 0,L"Bring the smaller rectangle guided by mouse pointer near static rectangle to check collision",93);
				
				
			
				//This is the algo to check collision between two Rectangles having coordinates
				//a1,b1,a2,b2 and x1,y1,x2,y2 respectively.

				if(x2>a1 && y2>b1  && x1<a2 && y1<b2)//if Collision detected
				
				{
					
					TextOut(hDC, 0, 30,L"Collision detected", 20);

					SelectObject(hDC,hRedBrush);// select the pen into context
					SetTextColor(hDC,RGB(255,0,0));//setting TextColor

				}

				else//if Collision not  detected
				{
					
					TextOut(hDC, 0, 30,L"Collision NOT detected",22);

					SelectObject(hDC,hGreenBrush);
					SetTextColor(hDC,RGB(0,0,255));//setting TextColor
				}
	

		InvalidateRect(hWnd,NULL,1);//Clear the previous drawing on Device Context
		Sleep(100);//wait for 1000 milliseconds
	}

	return(msg.wParam);
}



LRESULT CALLBACK WindowProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{

	switch(message)
	{
		//this message is sent by the window whenever the window is destroyed
	case WM_DESTROY:


		ReleaseDC(hWnd,hDC);//releasing the device context
		PostQuitMessage(0);
		break;

		//default execution for all the other messages
	default:
		return DefWindowProc(hWnd,message,wParam,lParam);

	}

	return(0);
}


just figured why the the box can't be dragging around, because of the LBDown keep reseting to 0 on every loop, but the image is still flashing...
Instead of drawing directly to the window, draw to an off-screen DC and then draw the entire DC to the window, and InvalidateRect should be unnecessary since you're updating the window in the loop.
I skimmed and didn't look too close. But here is my input based on some common mistakes I'm seeing in your code:

#1 Using 'GetDC' locks the window's DC and it stays locked until you release the DC with 'ReleaseDC'. It's like new/delete. Every new should be matched with a delete... and every GetDC should be matched with a ReleaseDC. Failure to do this can cause very strange drawing behavior on some systems, and can lead to other issues like memory leaks.

#2 You should only have the window DC 'gotten' for the shortest time necessary to draw. You should not GetDC once and then hang onto the DC for the life of the program. You should Get, quickly draw, then Release. The longer you have the DC 'gotten', the longer the OS has to have it locked.

#3 Windows will send a WM_ERASEBACKGROUND message prior to sending a WM_PAINT message. So whenever the window is invalidated, it will erase what's on the window, then draw over it. This causes flicker... as you will momentarily see the blank window between updates.

Solutions to this problem are below (any 1 will work.. you do not need all 3):
1) Don't provide a background HBRUSH when you create the window (give it NULL instead)
2) Handle the WM_ERASEBACKGROUND message and have it do nothing instead of actually erasing the background.
3) Don't invalidate the window when you want to redraw... and instead just redraw by Get/Release'ing the window DC (you might already be doing this... but you are also invalidating?)

#4 WinGDI is kind of crappy for this kind of work. Consider getting a graphic lib like SFML/SDL. On top of being easier and having less "gotchas", it'll also be more portable.


#5 You're using the wrong character type.

http://www.cplusplus.com/forum/windows/106683/


#6 knn9 is correct. If you want to avoid flicker, the best way is to draw to an offscreen DC, then Blit that DC to the display. That way there is only one draw and you do not see the individual steps involved with drawing.


#7 If you are invalidating the window, you should also be handling the WM_PAINT message and validating the window with BeginPaint/EndPaint. Failure to do this will cause Windows to repeatedly send WM_PAINT messages (because it thinks the screen is constantly dirty). You should also handle WM_PAINT messages anyway, as they are sent when the screen needs to be redrawn for other reasons (like if another window gets dragged over yours).

The way I would approach it is like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Your_WM_PAINT_Handler(HWND wnd)
{
    DoBackBufferDrawing();

    PAINTSTRUCT ps;
    HDC screenDC = BeginPaint(wnd,&ps);
    DrawToScreen( screenDC );
    EndPaint( wnd, &ps );
}

void Any_Other_Time_You_Want_To_Update_The_Screen(HWND wnd)
{
    DoBackBufferDrawing();

    HDC screenDC = GetDC(wnd);
    DrawToScreen( screenDC );
    ReleaseDC( wnd, screenDC );
}


Where...
- DoBackBufferDrawing draws what you want to your offscreen DC
and
- DrawToScreen blits (BitBlt) the contents of the offscreen DC to the given screenDC
Last edited on
I am trying to implement the double buffer method with the most simplest way possible in the first hand. And i am trying to make some motion in the window, by drawing a rectangle with one of its vertex folo my mouse cursor, but the rectangle seem too be static. It can be lagging for sometimes, i think it is because that long pile of code keep recycling in the cpu. How do we draw or apply things on the memhdc or the offscreen-buffer?

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
case WM_MOUSEMOVE:
            {
                x = HIWORD(lParam);
                y = LOWORD(lParam);
            }
            break;
        case WM_PAINT:
            {
                hdc = GetDC(hwnd);
                GetClientRect(hwnd,&rect);
                memhdc = CreateCompatibleDC(hdc);
                bmpbuffer = CreateCompatibleBitmap(hdc,rect.right, rect.bottom);
                HBITMAP transfer = (HBITMAP)SelectObject(memhdc, bmpbuffer);

                //?????how to apply cahnge on memhdc ???////
                FillRect(memhdc, &rect, (HBRUSH) (COLOR_WINDOW+2));
                COLORREF clr = RGB(30,30,30);

                HPEN pen = CreatePen(PS_SOLID,2,clr);
                SelectObject(memhdc, pen);
                Rectangle(memhdc, 10,10,x,y);

                BitBlt(hdc, 0,0,rect.right ,rect.bottom ,memhdc,0,0,SRCCOPY);

                SelectObject(memhdc, transfer);
                DeleteDC(memhdc);
                ReleaseDC(hwnd,hdc);
                EndPaint(hwnd, &ps);
            }
            break;

EDIT : I start off with new win32 gui code with CODEBLOCK template, i only show the part i changed in the code. Not verysure if i have really release everything to prevent memory leak.
Last edited on
You don't need to create and destroy the backbuffer every time. Create it once when the application starts and destroy it when the application ends. Other than I can't really see what's wrong besides the call to EndPaint without a corresponding BeginPaint.
Just notice it!! A stupid error that i always make, int x,y ; in the loop cause the x and y keep pointing at the initial value everytime the wndprocedure loop... The flickering issue solved.


1
2
3
 SelectObject(memhdc, transfer);
                DeleteDC(memhdc);
                


Why do we need that "SelectObject(..)" line? Is it because the DeleteDc will able to free the "transfer"?
Good question. MSDN says you're supposed to do that but not WHY. I don't know if it's correct but I usually do this:

DeleteObject(SelectObject(mydc, mybmp));

... so I don't bother with the original bitmap, and I never notice a memory leak doing this.

EDIT: Well I did a quick google search and it looks like the DC would have a "hold" on your bitmap preventing you from releasing the handle until you select the original bitmap back into the DC. Apparently that was once the case but not anymore.
Last edited on
Ok, Thanks very much Disnch and knn9, now i am going to mark this post as solved
Topic archived. No new replies allowed.