Thread: getting problems in double buffering code :(

  1. #1
    Registered User
    Join Date
    Aug 2013
    Posts
    451

    getting problems in double buffering code :(

    i'm trying avoiding the flickers on forms. for that i did:
    Code:
    case WM_ERASEBKGND:
                        return (LRESULT)1;
    return true is the same for windows that the background was cleaned backcolor reapinted and call the WM_PAINT message.
    in WM_PAINT i cread a memory DC(do a double buffer) and
    add the backcolor(using the FillRect() API function(maybe i need change the Pen color too for the Brush and Pen been the same) and an image from the Paint() function and then i use BitBlt() for draw it(all in 1) on form.
    Code:
    case WM_PAINT:
                    {
    
                        if (inst->Paint==NULL) break;
                        PAINTSTRUCT  ps;
                        HDC hdcimage = CreateCompatibleDC(NULL);
                        int width=ps.rcPaint.right-ps.rcPaint.left;
                        int height =ps.rcPaint.bottom-ps.rcPaint.top;
                        HBITMAP hbitmap=CreateCompatibleBitmap(hdcimage,width,height);
                        int f=width;// GetLastError();
                        SelectObject(hdcimage, hbitmap);
                        //int f= GetLastError();
                        FillRect(hdcimage,&ps.rcPaint,CreateSolidBrush(inst->clrBackColor));
                        inst->Paint(inst->hwnd,hdcimage);
                        HDC hdc = BeginPaint(inst->hwnd, &ps);
                        BitBlt(ps.hdc,0,0,ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,hdcimage,0,0,SRCCOPY);
    
                        MessageBox(NULL,to_string(f).c_str(),"erro",MB_OK);
                        EndPaint(inst->hwnd, &ps);
                        DeleteObject(hbitmap);
                        DeleteDC(hdcimage);
                    }
                    break;
    i have seen the error, but i odn't understand why
    see the width and height arent correctly calculated or something
    the GetLastError() give me 87: Invalid parameter
    (the image isn't drawed)
    what i'm doing wrong?

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    PAINTSTRUCT ps;
    HDC hdcimage = CreateCompatibleDC(NULL);
    int width=ps.rcPaint.right-ps.rcPaint.left;
    int height =ps.rcPaint.bottom-ps.rcPaint.top;
    So where in this do you initialise ps, to calculate width and height?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Aug 2013
    Posts
    451
    Quote Originally Posted by Salem View Post
    PAINTSTRUCT ps;
    HDC hdcimage = CreateCompatibleDC(NULL);
    int width=ps.rcPaint.right-ps.rcPaint.left;
    int height =ps.rcPaint.bottom-ps.rcPaint.top;
    So where in this do you initialise ps, to calculate width and height?
    thanks for that question
    and i have another error for do the bitmap:
    Code:
    case WM_ERASEBKGND:
                        return (LRESULT)1;
    case WM_PAINT:
                    {
                        if (inst->Paint==NULL) break;
                        PAINTSTRUCT  ps;
                        HDC hdc = BeginPaint(inst->hwnd, &ps);
                        HDC hdcimage = CreateCompatibleDC(NULL);
                        int width=ps.rcPaint.right-ps.rcPaint.left;
                        int height =ps.rcPaint.bottom-ps.rcPaint.top;
                        HBITMAP hbitmap=CreateBitmap(width,height,1,32,NULL);
                        SelectObject(hdcimage, hbitmap);
                        FillRect(hdcimage,&ps.rcPaint,CreateSolidBrush(inst->clrBackColor));
                        inst->Paint(inst->hwnd,hdcimage);
                        BitBlt(ps.hdc,0,0,width,height,hdcimage,0,0,SRCCOPY);
                        EndPaint(inst->hwnd, &ps);
                        DeleteObject(hbitmap);
                        DeleteDC(hdcimage);
                    }
                    break;
    now the image is showed.
    i read several tutorials... now i have 1 question: what i'm doing wrong for avoid the flicker?

  4. #4
    Registered User
    Join Date
    Aug 2013
    Posts
    451
    yah.. the child controls continue with flickers

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    That is because each paint message you are creating, using and destroying the memory DC.

    You should;

    Create the memory DC once, when the form is created.

    Destroy the memory DC (and/or its selected GDI objects) as few times as possible, when the form closes and resizes.

    Most importantly your paint should only be a BitBlt(), of the memory DC to the DC from the paintstruct using the rect in the paintstruct.

    When some event (timer, user input, etc) requires a screen change you redraw the memory DC and generate a paint message.

    The paint message should be the minimum size / rect possible and should be sent directly to the forms callback (using UpdateWindow()).

    I have previously posted full code.

    EDIT: Not sure if it still applies to latest .NET IDEs...

    When you create a DC it has default GDI objects (ie BITMAP)

    When you SelectObject() your created BITMAP it pushes out the default one (as the return from SelectObject()).

    You should catch this return from SelectObject() because the created BITMAP (and other created GDI objects) may not delete while selected into a DC .

    It is good practice to return the created DC to its default state by using SelectObject() to return the default GDI objects you pushed out (with created ones).
    Last edited by novacain; 10-11-2014 at 02:01 AM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    Registered User
    Join Date
    Aug 2013
    Posts
    451
    Quote Originally Posted by novacain View Post
    That is because each paint message you are creating, using and destroying the memory DC.

    You should;

    Create the memory DC once, when the form is created.

    Destroy the memory DC (and/or its selected GDI objects) as few times as possible, when the form closes and resizes.

    Most importantly your paint should only be a BitBlt(), of the memory DC to the DC from the paintstruct using the rect in the paintstruct.

    When some event (timer, user input, etc) requires a screen change you redraw the memory DC and generate a paint message.

    The paint message should be the minimum size / rect possible and should be sent directly to the forms callback (using UpdateWindow()).

    I have previously posted full code.

    EDIT: Not sure if it still applies to latest .NET IDEs...

    When you create a DC it has default GDI objects (ie BITMAP)

    When you SelectObject() your created BITMAP it pushes out the default one (as the return from SelectObject()).

    You should catch this return from SelectObject() because the created BITMAP (and other created GDI objects) may not delete while selected into a DC .

    It is good practice to return the created DC to its default state by using SelectObject() to return the default GDI objects you pushed out (with created ones).
    i did what you said:
    Code:
    static LRESULT CALLBACK WndProcForm(HWND HandleWindow, UINT msg, WPARAM wParam, LPARAM lParam)
            {
                static POINT PreviousLocation, Location;
                static bool Tracking = false;
                static MouseButtons MBButtons;
                static bool blControl = false;
                static bool blShift = false;
                static bool blResize = false;
                static int xPos = 0;
                static int yPos = 0;
                static UINT_PTR timerid=0;
                static UINT_PTR JoystickTimer=1;
                static bool blnDrag = false;
                static bool KeyPressed=false;
                static int KeyDownCount=0;
                HDC hdcimage = CreateCompatibleDC(NULL);
                HBITMAP hbitmap=NULL;
                //UINT JoystickCount =0;
    //..............
    //Working with messages
                switch(msg)
                {
                    case WM_ERASEBKGND:
                        return (LRESULT)1;
    case WM_PAINT:
                    {
                        if (inst->Paint==NULL) break;
                        PAINTSTRUCT  ps;
                        HDC hdc = BeginPaint(inst->hwnd, &ps);
    
                        int width=ps.rcPaint.right-ps.rcPaint.left;
                        int height =ps.rcPaint.bottom-ps.rcPaint.top;
                        hbitmap=CreateBitmap(width,height,1,32,NULL);//create the bitmap with icon size
    
                        SelectObject(hdcimage, hbitmap);
    
                        FillRect(hdcimage,&ps.rcPaint,CreateSolidBrush(inst->clrBackColor));
                        inst->Paint(inst->hwnd,hdcimage);
    
                        BitBlt(ps.hdc,0,0,ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,hdcimage,0,0,SRCCOPY);
    
                        EndPaint(inst->hwnd, &ps);
    
    
                    }
    break;
    case WM_DESTROY:
                    {
                        DeleteObject(hbitmap);
                        DeleteDC(hdcimage);
                        joyReleaseCapture(JOYSTICKID1);
                        --FormCount;
                        if(FormCount == 0)
                            End();
                    }
                    break;
    }}
    the flicker continues with child controls

  7. #7
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    What is this calling?

    Code:
    inst->Paint(inst->hwnd,hdcimage);
    is it generating another WM_PAINT message?

    if so, that could be the problem.

    -

  8. #8
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Do not add any lines to the WM_PAINT handler!

    If you think you have to add lines to the WM_PAINT then your design is wrong.

    Code:
    static LRESULT CALLBACK WndProcForm(HWND HandleWindow, UINT msg, WPARAM wParam, LPARAM lParam)
            {
                static POINT PreviousLocation, Location;
                static bool Tracking = false;
                static MouseButtons MBButtons;
                static bool blControl = false;
                static bool blShift = false;
                static bool blResize = false;
                static int xPos = 0;
                static int yPos = 0;
                static UINT_PTR timerid=0;
                static UINT_PTR JoystickTimer=1;
                static bool blnDrag = false;
                static bool KeyPressed=false;
                static int KeyDownCount=0;
    	    
    	    //our GDI objects
                static HDC hdcimage = NULL;
                static HBITMAP hbitmap=NULL;
    
    	    statid bool state = false;
    
                //UINT JoystickCount =0;
    //..............
    //Working with messages
                switch(msg)
                {
                    case WM_ERASEBKGND:
                        return (LRESULT)1;
    		break;
    		// when form created WM_INITDIALG or WM_CREATE
    		case WM_INITDIALOG:
    			//create your DC and BITMAP and selectobject
    
    			//make the window redraw every 10 sec
    			SetTimer(HandleWindow, 12345, 10000, NULL);
    			
    			return ?
    		break;
    		case WM_TIMER:
    			IF(wParam == 12345)
    			{
    				return DrawScreen(HandleWindow, state);
    			}
    
    			return ?
    		break;
    		case WM_PAINT:
                    {
                        PAINTSTRUCT  ps;
                        HDC hdc = BeginPaint( HandleWindow, &ps);       
    
                        BitBlt(ps.hdc,0,0,ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,hdcimage,0,0,SRCCOPY);
    
                        EndPaint( HandleWindow, &ps);
    
                    }
    		break;
    		case WM_DESTROY:
                    {
    		
                        DeleteObject(hbitmap);
                        DeleteDC(hdcimage);
                        joyReleaseCapture(JOYSTICKID1);
                        --FormCount;
                        if(FormCount == 0)
                            End();
                    }
                    break;
    }}
    
    bool DrawScreen(HWND hWnd, bool drawState)
    {
    	IF(drawState)
    		//create white brush
    	ELSE
    		//create black brush
    
    	//fill the hdcimage 
    	
    	//generate paint msg
    	InvalidateRect()
    	UpdateWindow()
    
    	return ?
    }
    Last edited by novacain; 10-11-2014 at 10:13 PM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  9. #9
    Registered User
    Join Date
    Aug 2013
    Posts
    451
    Quote Originally Posted by novacain View Post
    Do not add any lines to the WM_PAINT handler!

    If you think you have to add lines to the WM_PAINT then your design is wrong.

    Code:
    static LRESULT CALLBACK WndProcForm(HWND HandleWindow, UINT msg, WPARAM wParam, LPARAM lParam)
            {
                static POINT PreviousLocation, Location;
                static bool Tracking = false;
                static MouseButtons MBButtons;
                static bool blControl = false;
                static bool blShift = false;
                static bool blResize = false;
                static int xPos = 0;
                static int yPos = 0;
                static UINT_PTR timerid=0;
                static UINT_PTR JoystickTimer=1;
                static bool blnDrag = false;
                static bool KeyPressed=false;
                static int KeyDownCount=0;
            
            //our GDI objects
                static HDC hdcimage = NULL;
                static HBITMAP hbitmap=NULL;
    
            statid bool state = false;
    
                //UINT JoystickCount =0;
    //..............
    //Working with messages
                switch(msg)
                {
                    case WM_ERASEBKGND:
                        return (LRESULT)1;
            break;
            // when form created WM_INITDIALG or WM_CREATE
            case WM_INITDIALOG:
                //create your DC and BITMAP and selectobject
    
                //make the window redraw every 10 sec
                SetTimer(HandleWindow, 12345, 10000, NULL);
                
                return ?
            break;
            case WM_TIMER:
                IF(wParam == 12345)
                {
                    return DrawScreen(HandleWindow, state);
                }
    
                return ?
            break;
            case WM_PAINT:
                    {
                        PAINTSTRUCT  ps;
                        HDC hdc = BeginPaint( HandleWindow, &ps);       
    
                        BitBlt(ps.hdc,0,0,ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,hdcimage,0,0,SRCCOPY);
    
                        EndPaint( HandleWindow, &ps);
    
                    }
            break;
            case WM_DESTROY:
                    {
            
                        DeleteObject(hbitmap);
                        DeleteDC(hdcimage);
                        joyReleaseCapture(JOYSTICKID1);
                        --FormCount;
                        if(FormCount == 0)
                            End();
                    }
                    break;
    }}
    
    bool DrawScreen(HWND hWnd, bool drawState)
    {
        IF(drawState)
            //create white brush
        ELSE
            //create black brush
    
        //fill the hdcimage 
        
        //generate paint msg
        InvalidateRect()
        UpdateWindow()
    
        return ?
    }
    thanks for correct me.
    but i'm confused with 2 things:
    1 - i always use the InvalidateRect(), but why the UpdateWindow()?
    2 - why you used the drawState? seems to be, always, false

    so i did a mistake and the best is call my Paint() lambda function with a timer or something, right?
    (in these case the Paint() lambda is for show an image to hdcimage nothing more)
    anothing: i use the TransparentBlt() on my image class method, but i know about the memory leak, can be avoid for another function?

  10. #10
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    >>1 - i always use the InvalidateRect(), but why the UpdateWindow()?

    UpdateWindow() sends the paint msg directly to the apps callback. It speeds up the paint msg delivery. If you don't use UpdateWindow() the paint msg is sent to the OS msg queue, where it has the lowest priority and can be ignored.

    >>2 - why you used the drawState? seems to be, always, false

    It was an example to show when you generate a paint msg.
    I assumed you would understand that it was not full code and would ahve to have parts filled in to make it work (ie flip the status in the DrawScreen() function)
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  11. #11
    Registered User
    Join Date
    Aug 2013
    Posts
    451
    Quote Originally Posted by novacain View Post
    >>1 - i always use the InvalidateRect(), but why the UpdateWindow()?

    UpdateWindow() sends the paint msg directly to the apps callback. It speeds up the paint msg delivery. If you don't use UpdateWindow() the paint msg is sent to the OS msg queue, where it has the lowest priority and can be ignored.

    >>2 - why you used the drawState? seems to be, always, false

    It was an example to show when you generate a paint msg.
    I assumed you would understand that it was not full code and would ahve to have parts filled in to make it work (ie flip the status in the DrawScreen() function)
    thanks
    anotherthing: the GdiTransparentBlt() can be used instead of TransparentBlt() for avoid the memory leak?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. GDI+ and double buffering
    By george7378 in forum Windows Programming
    Replies: 3
    Last Post: 04-28-2012, 10:45 PM
  2. Double buffering problem
    By george7378 in forum Windows Programming
    Replies: 5
    Last Post: 01-31-2012, 12:22 PM
  3. WS_EX_COMPOSITED style (double buffering) problems
    By JasonD in forum Windows Programming
    Replies: 2
    Last Post: 10-12-2004, 11:21 AM
  4. double buffering for allegro
    By Leeman_s in forum C++ Programming
    Replies: 6
    Last Post: 09-12-2002, 02:45 PM
  5. double buffering in MFC
    By Unregistered in forum Windows Programming
    Replies: 0
    Last Post: 03-07-2002, 12:29 PM