views:

122

answers:

1

I have created an animation which works fine, but it flicks. I need help with double-buffering since I don't know anything about it.

This is the code in my onPaint():

VOID onPaint(HDC hdc)
{
    Graphics graphics(hdc);
    Pen      pen(Color(255, 0, 0, 255));
    graphics.DrawEllipse(&pen, sf , 0, 10, 10);
}

It works fine but with flicker. I tried this code but it didn't work:

VOID onPaint(HDC hdc,HWND hWnd)
{
    HDC hDC=GetDC(hWnd);;
    HDC memDC = CreateCompatibleDC(hDC);
    HBITMAP hMemBmp = CreateCompatibleBitmap(hDC,10,10);
    HBITMAP hOldBmp =  (HBITMAP)SelectObject(memDC,hMemBmp);
    BitBlt(hDC, 0, 0, 10, 10, memDC, 0, 0, SRCCOPY);
    Graphics graphics(memDC);
    Pen      pen(Color(255, 0, 0, 255));
    graphics.DrawEllipse(&pen, sf , 0, 10, 10);

    // Always select the old bitmap back into the device context
    SelectObject(memDC, hOldBmp);
    DeleteObject(hMemBmp);
    DeleteDC(memDC);
}
+6  A: 

It looks like you're just prematurely copying the offscreen DC to the display. Try moving the call to BitBlt down four lines, to make it the last line before you start the clean-up, like so:

VOID onPaint(HDC hdc,HWND hWnd)
{
    // this line looks a little odd :
    HDC hDC = GetDC(hWnd);
    // .. usually the hdc parameter passed to onPaint would already refer to
    // the on-screen DC that windows wants updated. Also worth noting is that
    // when you use GetDC(), you should have a matching ReleaseDC()
    // As a quick test, you might just replace the above line with
    //     HDC hDC = hdc;

    HDC memDC = CreateCompatibleDC(hDC);
    HBITMAP hMemBmp = CreateCompatibleBitmap(hDC,10,10);
    HBITMAP hOldBmp =  (HBITMAP)SelectObject(memDC,hMemBmp);

    // draw to the off-screen map ..
    Graphics graphics(memDC);
    Pen      pen(Color(255, 0, 0, 255));
    graphics.DrawEllipse(&pen, sf , 0, 10, 10);

    // now that you've drawn on the offscreen map, go ahead
    // and put it on screen.
    BitBlt(hDC, 0, 0, 10, 10, memDC, 0, 0, SRCCOPY);

    // Always select the old bitmap back into the device context
    SelectObject(memDC, hOldBmp);
    DeleteObject(hMemBmp);
    DeleteDC(memDC);
}

Another thing about this code, you've passed the constant '10' as the width and height of your off-screen bitmap, as well as using it for the width and height params to the BitBlt() that does the copy. Chances are the window client area being updated is very much larger than that. The 'black square' is a consequence of blitting the 10x10 off-screen map onto the window client area. Instead of hard-coding 10 there, you might try using another GDI function to obtain the dimensions of the on-screen bitmap, or at the very least you could #define width and height values, and use these in the params.

The other thing killing you is probably the 'sf' in the line "graphics.DrawEllipse(&pen, sf , 0, 10, 10)" -- since you've created an incredibly tiny 10x10 map, if the value of 'sf' is anything outside of 0..10, the DrawEllipse() call will place the ellipse entirely outside of the available pixels in your offscreen map.

So, bottom line, you probably want to make the offscreen map the same size as the window client area, and be sure to move the BitBlt() call down so that it happens after all the drawing ops on the off-screen map.

JustJeff
i have the same result a black square
Ramiz Toma
i got an error can't convert HWND to HDC
Ramiz Toma
If this onPaint function is called in response to WM_PAINT, then you should be using BeginPaint to get the DC, and EndPaint. You also need to get the correct sizes for the memory bitmap and the blit, probably GetClientRect. It looks like you're not initializing the memory bitmap, so you'll have random data except where the ellipse paints.
Adrian McCarthy
@Adrian McCarthy - indeed. Using GetClientRect will make the code do the right thing even if the user resizes the window. He could also use PatBlt(memDC,0,0,<width>,<height>,WHITENESS), or something to ensure the background is intialized.
JustJeff
ok guys the black square now the same size of the window :) but it doesn't flick that much, it still flicks one time or two times ;/
Ramiz Toma
you don't have something somewhere else that's clearing the client area in between calls of your onPaint(), do you?
JustJeff
i doInvalidateRect(hWnd, NULL, true);//force window to repaint
Ramiz Toma
that would probably do it. Amongst other things it sends WM_ERASEBKGND - which is probably throwing a blank white area in there for an observable length of time. You don't want that. In my stuff I use ValidateRect(hwnd,0) to ensure that my code is the only thing updating that client area.
JustJeff
i tried it but it didn't work,animation didn't work
Ramiz Toma
i got it working thanks to you for giving me the tip aobut wm_erasebkgnd, i just changed this InvalidateRect(hWnd, NULL, true);//force window to repaint to thisInvalidateRect(hWnd, NULL, false);//force window to repaint
Ramiz Toma