views:

644

answers:

3

I'm trying to bitblt bitmap to mouse cursor position with the mouse movemont.but with flickering problems.

I've read about double buffering to reduce flicker but I'm not sure how to ... this causes extreme flickering. I've read about double buffering to reduce flicker but I'm not sure how to implement it in this example. Please can you help? Thanks

here's code below .thanks for your help!

// screen blinks.trying to use double buffer so solve this problem.
#include <windows.h>
HDC bufferDC = NULL;
HDC           hdc=GetWindowDC(NULL) ;
HDC hammerDC = NULL; 
HBITMAP hammer1BMP = NULL;
HBITMAP bufferBMP = NULL;
POINT cursorpoint;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("DigClock") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName, TEXT ("Digital Clock"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;


     bufferDC=CreateCompatibleDC(hdc);
     hammerDC=CreateCompatibleDC(hdc);
     hammer1BMP=(HBITMAP)LoadImage(NULL,"star.bmp",IMAGE_BITMAP,160,160,LR_LOADFROMFILE);
     SelectObject(hammerDC,hammer1BMP);

     while (GetMessage (&msg, NULL, 0, 0))
          {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
          }
     return msg.wParam ;
     }

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static BOOL   f24Hour, fSuppress ;
     static HBRUSH hBrushRed ;
     static int    cxClient, cyClient ;
     HDC           hdc ;
     PAINTSTRUCT   ps ;
     TCHAR         szBuffer [2] ;

     switch (message)
     {
     case WM_CREATE:
          hBrushRed = CreateSolidBrush (RGB (255, 0, 0)) ;
          SetTimer (hwnd, ID_TIMER, 1000/24,NULL) ;//1000

                                                  // fall through

     case WM_SETTINGCHANGE:

          InvalidateRect (hwnd, NULL, TRUE) ;
          return 0 ;

     case WM_SIZE:
          cxClient = LOWORD (lParam) ;
          cyClient = HIWORD (lParam) ;
          return 0 ;

     case WM_TIMER:
          InvalidateRect (hwnd, NULL, TRUE) ;
          return 0 ;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          bufferBMP=CreateCompatibleBitmap(hdc,cxClient,cyClient);
          SelectObject(bufferDC,bufferBMP);
          // SelectObject(bufferDC,hammer1BMP); 
       GetCursorPos(&cursorpoint);
          BitBlt(bufferDC,0,0,cxClient,cyClient,hammerDC,0,0,SRCCOPY);
          BitBlt(hdc,cursorpoint.x,cursorpoint.y,cxClient,cyClient,bufferDC,0,0,SRCCOPY);


          EndPaint (hwnd, &ps) ;
          return 0 ;
     case WM_LBUTTONDOWN:
      // GetCursorPos(&cursorpoint);
       //BitBlt(hdc,cursorpoint.x,cursorpoint.y,cxClient,cyClient,hammerDC,0,0,SRCCOPY);
       return 0;
     case WM_DESTROY:
          KillTimer (hwnd, ID_TIMER) ;
       DeleteDC(hammerDC);
          DeleteObject (hBrushRed) ;
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
+2  A: 

Remove your timer. The timer is causing you to invalidate even when there is no change to the window. Furthermore, you are erasing the window with each timer elapse.

Basically , you got the concept correct. When you need to WM_PAINT, copy your back buffer. When you need to update the graphics, update your back buffer and then only invalidate once.

Another tip is to use GetUpdateRect() to get the update region in WM_PAINT. Only copy the region which is dirty from your back buffer. This further optimizes your double buffering

Andrew Keith
For animation, the timer may be needed, but as you suggest the third parameter to InvalidateRect should always be FALSE. The flickering is because of the time between wiping the rectangle on screen and blitting over it, and there's no point wiping when you'll overwrite the whole thing anyway. The clearing should be to the backbuffer as part of the off-screen drawing.
Steve314
MSDN may be to blame for this too - take a look at the InvalidateRect in http://msdn.microsoft.com/en-us/library/ms969905.aspx - true, it's only on WM_SETTEXT, but it's still a misleading example.
Steve314
+4  A: 

In this case it looks like you don't really need double buffering -- in fact, it probably won't help you at all.

The primary cause of your flickering is erasing the background, then immediately drawing over it. Since you're apparently drawing the whole client area of your window in WM_PAINT, just add a handler for WM_ERASEBKGND that does nothing but return TRUE to indicate that the background has been erased.

Edit (in response to comments):

To be more complete, flickering results (almost) anytime you paint an area in one color, then quickly repaint it in another color. Double buffering helps when/if your foreground has a number of overlaying elements of different colors. You draw (at least the) overlaying areas into the back buffer, then only when you have the right colors, you draw them to the screen. In this case, the original code does double buffering, but it's still drawing the background, then the foreground, and you still get flickering.

Another answer mentioned passing false as the second parameter to InvalidateRect. This will help a lot, as it won't re-draw the background in response to that InvalidateRect. Only he foreground will be drawn, so it'll be flicker-free. Unfortunately, when (at least part of) the Window's rectangle is invalidated for any other reason, you'll still get flicker, because it'll still draw the background followed by the foreground.

Jerry Coffin
Erm - he's already doing double buffering. Just not quite got it perfect yet. Nice idea with WM\_ERASEBACKGROUND, though.
Steve314
Damn - WM\_ERASEBKGND - I wish I could edit my comments.
Steve314
@Steve314 - you can delete your comment, then resubmit an edited version
Phil Nash
A: 

The cause of the flicker (and eventual program crash) is multifold:

  1. There is the background brush - set hbrBackground to inhibit the generation of WM_ERASEBKGND messages.
  2. You are creating (and leaking) a bufferBMP per WM_PAINT.
  3. You are not painting the window correctly - why are you painting to the cursor position? If you want to have the 'hammer' track the mouse, you would paint the hammer onto the offscreen bitmap at the appropriate location, and then blit the offscreen bitmap to cover the client area, i.e. to 0,0,cx,cy
Chris Becke