Double Buffered window?

Windows specific questions.
Post Reply
aurelVZAB
Posts: 681
Joined: Jul 02, 2008 14:55
Contact:

Double Buffered window?

Post by aurelVZAB »

Hi to all...!
Is anyone of you try to create double buffered window in FreeBasic ?
aurelVZAB
Posts: 681
Joined: Jul 02, 2008 14:55
Contact:

Post by aurelVZAB »

Nobody have nothing to say....?
come one experts...!!!
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Post by TJF »

I don't understand your question.

To create windows I use GTK. All widgets are double buffered in GTK by default. Does this help?
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

Because this is in the Windows forum I’m guessing that you mean using only the Windows API, and not fbgfx. This (not very good) falling-snow simulation demonstrates two methods, one using a device-dependent (compatible) bitmap as the back buffer, and one using a device-independent bitmap. Using a device-independent bitmap is more cumbersome, but has the advantage that you can get a direct pointer to the bitmap bits, so you can use a faster routine to draw the pixels. SeeMSDN: About Bitmaps.

Code: Select all

''=============================================================================
#include "windows.bi"
''=============================================================================

#define FLAKE_COUNT       4000
#define UPDATE_DELAY      0
#define USE_FASTSETPIXEL  1

''=============================================================================

#define FPU_RC_NEAREST  0     '' or to even if equidistant (initialized state)
#define FPU_RC_DOWN     &h400 '' toward -infinity
#define FPU_RC_UP       &h800 '' toward +inifinity
#define FPU_RC_TRUNCATE &hc00 '' toward zero

'---------------------------------------------------
'' This macro sets the rounding control bits in the
'' FPU Control Word to one of the above values.
'---------------------------------------------------

#macro FPU_SETRC(rc)
  asm
    push eax
    fstcw [esp]
    pop eax
    and eax, not 0xc00
    or eax, rc
    push eax
    fldcw [esp]
    pop eax
  end asm
#endmacro

''=============================================================================

type FLAKE
    ix  as integer
    iy  as integer
    fi  as single
    fy  as single
end type

''=============================================================================

dim shared as FLAKE ptr g_pFlakes
dim shared as HDC g_hdcDDB
dim shared as HBITMAP g_hDDB
dim shared as BITMAPINFO ptr g_pBMI
dim shared as any ptr g_pDIBits
dim shared as integer g_width, g_height, g_frameCount

''=============================================================================

''------------------------------------------------------------------
'' To keep it simple and fast, this procedure supports 32-bpp only.
''------------------------------------------------------------------

sub FastSetPixel naked ( byval pBuff as any ptr, _
                         byval pitch as integer, _
                         byval x as integer, _
                         byval y as integer, _
                         byval clr as integer )
    asm
        mov ecx, [esp+16]
        mov eax, [esp+8]
        mul ecx
        mov ecx, eax
        mov edx, [esp+4]
        mov eax, [esp+12]
        add ecx, eax
        mov eax, [esp+20]
        mov [edx+ecx*4], eax
        ret 20
    end asm

end sub

''=============================================================================

function WindowProc( byval hWnd as HWND,_
                     byval uMsg as uint,_
                     byval wParam as WPARAM,_
                     byval lParam as LPARAM ) as LRESULT

    static as integer frameCount
    static as HGDIOBJ hPrevBmp
    dim as RECT rc
    dim as HDC hdcClient
    dim as PAINTSTRUCT ps
    dim as HBITMAP hDDB
    dim as HGDIOBJ hPrevBM

    select case uMsg

        case WM_CREATE

            ''----------------------------------------------
            '' Get the width and height of the client area.
            ''----------------------------------------------

            GetClientRect( hWnd, @rc )
            g_width = rc.right + 1
            g_height = rc.bottom + 1

            ''-------------------------------------------
            '' Allocate memory for the flake data array.
            ''-------------------------------------------

            g_pFlakes = callocate( g_width * g_height * sizeof(FLAKE) )

            ''-------------------------------------------------------------
            '' Get a DC for the client area and create a compatible memory
            '' DC and compatible bitmap (DDB). The compatible bitmap is
            '' used as a drawing surface for the API SetPixel function and
            '' to receive the bitmap bits from the device-independent
            '' bitmap (DIB) that the FastSetPixel procedure draws on.
            ''-------------------------------------------------------------

            hdcClient = GetDC( hWnd )
            g_hdcDDB = CreateCompatibleDC( hdcClient )
            g_hDDB = CreateCompatibleBitmap( hdcClient, g_width, g_height )

            ''----------------------------------------------------
            '' Select the bitmap into the memory DC and save the
            '' previously selected object.
            ''
            '' The bitmap, by default, will be filled with black.
            ''----------------------------------------------------

            hPrevBmp = SelectObject( g_hdcDDB, g_hDDB )

            ''-------------------------------------------------------------
            '' Allocate memory to store the BITMAPINFO structure for our
            '' DIB and set the biSize member to the size of the structure.
            ''-------------------------------------------------------------

            g_pBMI = callocate( sizeof(BITMAPINFO) )
            g_pBMI->bmiHeader.biSize = sizeof(BITMAPINFO)

            ''--------------------------------------------------------
            '' Call the GetDIBits function with the lpvBits parameter
            '' set to null, so the function will pass the dimensions
            '' and format of the bitmap to the BITMAPINFO structure.
            ''--------------------------------------------------------

            GetDIBits( hdcClient, g_hDDB, 0, 0, null, g_pBMI, DIB_RGB_COLORS )

            ''------------------------------------
            '' Ensure that the display bpp is 32.
            ''------------------------------------

            if g_pBMI->bmiHeader.biBitCount <> 32 then

                MessageBox( hWnd, "Display bpp must be 32", 0, 0 )

                ''-----------------------
                '' Clean up before exit.
                ''-----------------------

                KillTimer( hWnd, 1 )
                SelectObject( g_hdcDDB, hPrevBmp )
                ReleaseDC( null, g_hdcDDB )
                deallocate( g_pFlakes )
                deallocate( g_pBMI )
                deallocate( g_pDIBits )

                DestroyWindow( hWnd )

            end if

            ''----------------------------------------------------------------
            '' To keep access to the DIB bits simple, specify an uncompressed
            '' format and a negative height, so we get a top-down DIB instead
            '' of the normal bottom-up DIB. For a top-down DIB the origin is
            '' at the upper-left corner, instead of at the lower-left corner
            '' as it is for a bottom-up DIB.
            ''----------------------------------------------------------------

            g_pBMI->bmiHeader.biCompression =  BI_RGB
            g_pBMI->bmiHeader.biHeight = -g_pBMI->bmiHeader.biHeight

            ''---------------------------------------------------------
            '' Allocate a buffer to store the bitmap bits for our DIB.
            '' Zero-filling the buffer effectively provides a black
            '' background for the DIB. The FastSetPixel procedure
            '' draws directly to this buffer.
            ''---------------------------------------------------------

            g_pDIBits = callocate( g_pBMI->bmiHeader.biSizeImage )


            ReleaseDC( null, hdcClient )

            ''-----------------------------------------------------
            '' Initialize the flake data array with random X and Y
            '' coordinates, and Y increment values spread over a
            '' small random range (~0.425 to ~0.575)
            ''-----------------------------------------------------

            for i as integer = 0 to FLAKE_COUNT - 1
                g_pFlakes[i].ix = int(rnd * g_width)
                g_pFlakes[i].fy = rnd * g_height
                g_pFlakes[i].fi = rnd * 0.15 + .425
            next

            ''-----------------------------------------------
            '' Create a timer that fires once per 1000 ms to
            '' provide a time base for the frame rate.
            ''-----------------------------------------------

            SetTimer( hWnd, 1, 1000, null )

        CASE WM_PAINT

            ''------------------------------------------------
            '' Display the DDB by copying it to the paint DC.
            ''------------------------------------------------

            BeginPaint( hWnd, @ps )
            hDDB = CreateCompatibleBitmap( ps.hdc, g_width, g_height )
            hPrevBM = SelectObject( ps.hdc, hDDB )
            BitBlt( ps.hdc, 0, 0, g_width, g_height, g_hdcDDB, 0, 0, SRCCOPY )
            SelectObject( ps.hdc, hPrevBM )
            DeleteObject( hDDB )
            EndPaint( hWnd, @ps )

        case WM_TIMER

            ''------------------------
            '' Update the frame rate.
            ''------------------------

            SetWindowText( hWnd, str(g_frameCount) )
            g_frameCount = 0

        case WM_COMMAND

            select case wParam

                case IDCANCEL

                    ''---------------------------------------------
                    '' This allows the user to close the window by
                    '' pressing the escape key, but note that this
                    '' feature depends on the message loop calling
                    '' IsDialogMessage.
                    ''---------------------------------------------

                    ''-----------------------
                    '' Clean up before exit.
                    ''-----------------------

                    KillTimer( hWnd, 1 )
                    SelectObject( g_hdcDDB, hPrevBmp )
                    ReleaseDC( null, g_hdcDDB )
                    deallocate( g_pFlakes )
                    deallocate( g_pBMI )
                    deallocate( g_pDIBits )

                    DestroyWindow( hWnd )

            end select

        case WM_CLOSE

            ''-----------------------
            '' Clean up before exit.
            ''-----------------------

            KillTimer( hWnd, 1 )
            SelectObject( g_hdcDDB, hPrevBmp )
            ReleaseDC( null, g_hdcDDB )
            deallocate( g_pFlakes )
            deallocate( g_pBMI )
            deallocate( g_pDIBits )

            DestroyWindow( hWnd  )

        case WM_DESTROY

            PostQuitMessage( null )

        case else

            return DefWindowProc( hWnd, uMsg, wParam, lParam )

    end select

    return 0

end function

''=============================================================================

dim as WNDCLASSEX wcx
dim as HWND hWnd
dim as MSG wMsg
dim as integer wx, wy, nWidth, nHeight
dim as string className = "test_class"

with wcx
    .cbSize = sizeof( WNDCLASSEX )
    .style = CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
    .lpfnWndProc = cast( WNDPROC, @WindowProc )
    .cbClsExtra = null
    .cbWndExtra = null
    .hInstance = GetModuleHandle( null )
    .hbrBackground = GetStockObject( BLACK_BRUSH )
    .lpszMenuName = null
    .lpszClassName = strptr( className )
    .hIcon = LoadIcon( null, IDI_APPLICATION )
    .hCursor = LoadCursor ( null, IDC_ARROW )
    .hIconSm = 0
end with

RegisterClassEx( @wcx )

nWidth = 400
nHeight = 400
wx = (GetSystemMetrics( SM_CXSCREEN ) / 2) - nWidth / 2
wy = (GetSystemMetrics( SM_CYSCREEN ) / 2) - nHeight / 2

hWnd = CreateWindowEx( 0,_
                       strptr( className ),_
                       "Test",_
                       WS_OVERLAPPED or WS_SYSMENU,_
                       wx, wy, nWidth, nHeight,_
                       null, null,_
                       GetModuleHandle( null ), null )

ShowWindow( hWnd, SW_SHOWNORMAL )
UpdateWindow( hWnd )

''-----------------------------------------------------------
'' Set the FPU to round down, to avoid in the flake-position
'' update code below, having the default rounding (nearest)
'' round the integer y-position up an invalid value, which
'' would cause the FastSetPixel procedure to access outside
'' the allocated buffer and trigger an access-violation
'' exception.
''-----------------------------------------------------------

FPU_SETRC( FPU_RC_DOWN )

do

    if PeekMessage( @wMsg, null, 0, 0, PM_REMOVE ) <> 0 then

        if wMsg.message = WM_QUIT then exit do

        if IsDialogMessage( hWnd,  @wMsg ) = 0 then
            TranslateMessage( @wMsg )
            DispatchMessage( @wMsg )
        end if

    else

        ''---------------------------------------------------------
        '' This code runs whenever there is no message to process.
        ''---------------------------------------------------------

        for i as integer = 0 to FLAKE_COUNT - 1

            ''----------------------------------------
            '' "Erase" the flake at the old position.
            ''----------------------------------------


            #if USE_FASTSETPIXEL

            FastSetPixel( g_pDIBits, g_width, g_pFlakes[i].ix, _
                          g_pFlakes[i].iy, 0 )

            #else

            SetPixel( g_hdcDDB, g_pFlakes[i].ix, g_pFlakes[i].iy, 0 )

            #endif

            ''-------------------------------------------------------
            '' Update the flake position, wrapping to the top of the
            '' window when the flake reaches the bottom.
            ''-------------------------------------------------------

            g_pFlakes[i].fy += g_pFlakes[i].fi
            if g_pFlakes[i].fy >= g_height then g_pFlakes[i].fy = 0
            g_pFlakes[i].iy = g_pFlakes[i].fy

            ''-----------------------------------------
            '' Draw the flake at the updated position.
            ''-----------------------------------------

            #if USE_FASTSETPIXEL

            FastSetPixel( g_pDIBits, g_width, g_pFlakes[i].ix, _
                          g_pFlakes[i].iy, &hffffff )

            #else

            SetPixel( g_hdcDDB, g_pFlakes[i].ix, g_pFlakes[i].iy, &hffffff )

            #endif

        next

        ''-----------------------------------------------
        '' If using the FastSetPixel procedure, copy the
        '' bitmap bits from the DIB to the DDB, so they
        '' can be displayed in the WM_PAINT handler.
        ''-----------------------------------------------

        #if USE_FASTSETPIXEL

        SetDIBits( 0, g_hDDB, 0, g_height, g_pDIBits, g_pBMI, DIB_RGB_COLORS )

        #endif

        ''-------------------------------------
        '' Force a repaint of the client area.
        ''-------------------------------------

        InvalidateRect( hWnd, 0, 0 )

        ''------------------------------------------------------------
        '' Delay to control the update rate and thus the flake speed.
        ''------------------------------------------------------------

        sleep UPDATE_DELAY

        g_frameCount += 1

    end if

loop

''=============================================================================
Last edited by MichaelW on Sep 17, 2011 18:09, edited 3 times in total.
Aave
Posts: 128
Joined: Jun 13, 2008 19:55
Location: Helsinki, Finland

Post by Aave »

As for fb's built-in gfxlib, it's very easy: Just create two screen pages and flip between them in your drawing loop:

Code: Select all

ScreenRes 800, 600, 32, 2 ' <-- 2 for two screen buffers
Dim As Integer workpage = 0

Do
	ScreenSet workpage, workpage Xor 1 ' Set current front and back buffers
	Cls

	'' Do drawing here

	workpage = workpage Xor 1 ' Switch work page, i.e. 1-->0 or 0-->1
Loop
If you use OpenGL window, you get hardware buffer flipping and don't need separate fbgfx buffers:

Code: Select all

ScreenRes 800, 600, 32, ,2 '' <-- default value for buffer count, 2 means OpenGL window
Do
    '' Draw here
    Flip
Loop
Merick
Posts: 1038
Joined: May 28, 2007 1:52

Post by Merick »

Actually, fbgfx is double buffered by default, you just need to use screenlock and screenunlock around the graphics drawing commands
fxm
Moderator
Posts: 12516
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Post by fxm »

Merick wrote:Actually, fbgfx is double buffered by default, you just need to use screenlock and screenunlock around the graphics drawing commands
Yes, under fbgfx, there is already an internal double buffering.
If necessary, you can use a Screenlock/Screenunlock block to avoid flicker (for example due to Cls then drawing instructions in a loop).

But in specific case as very long graphic drawing, you cannot lock the automatic screen refresh during a long time, and you are forced to use an own double buffering with for example two graphics pages (work-page and visible-page).
See previous topic about this:
http://www.freebasic.net/forum/viewtopi ... 092#162092
aurelVZAB
Posts: 681
Joined: Jul 02, 2008 14:55
Contact:

Post by aurelVZAB »

Yes Michael you on a right track......thank you very much.
I wanna use only native win32 api nothing extra.
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Post by TJF »

aurelVZAB wrote:Yes Michael you on a right track......thank you very much.
I wanna use only native win32 api nothing extra.
And you're on a wrong path, my friend.

When asking for help here it's your obligation to provide the necessary informations. When you like to perform something like a TV quiz, you should first tell us how we can benefit if we guess right.
eodor
Posts: 243
Joined: Dec 24, 2005 1:44
Location: Romania
Contact:

Post by eodor »

Try :

Code: Select all


#include once "windows.bi"

function WindowProc(hDlg as hwnd,Msg as uint,wParam as wparam,lParam as lparam) as lresult
    static as HBRUSH Brush
    static as RECT ClientRect
    select case Msg
    case WM_CREATE
        Brush = CreateSolidBrush(&HFFFFFF)
        GetClientRect(hDlg, @ClientRect)
        return false
    case WM_DESTROY
        DeleteObject(Brush)
        return false
    case WM_SIZE
        ClientRect.Right = loword(lParam)
        ClientRect.Bottom = hiword(lParam)
        return false
    case WM_PAINT
        dim as HDC DC, MemDc
        dim as HBITMAP oldBitmap, MemBitmap
        dim as PAINTSTRUCT PS
        DC = GetDC(0)
        MemBitmap = CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom)
        ReleaseDC(0, DC)
        MemDC = CreateCompatibleDC(0)
        OldBitmap = SelectObject(MemDC, MemBitmap)
        DC = BeginPaint(hDlg, @PS)
        SendMessage(hDlg, WM_ERASEBKGND, cint(MemDC), cint(MemDC))
        'Perform all drawing here
        TextOut(MemDC, ClientRect.Right - 200, ClientRect.Bottom -200, "Hello !", Len("Hello !"))
        BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY)
        EndPaint(hDlg, @PS)
        SelectObject(MemDC, OldBitmap)
        DeleteDC(MemDC)
        DeleteObject(MemBitmap)
        return false
    case WM_ERASEBKGND
         if wParam = lParam then
            FillRect(cast(HDC, wParam), @ClientRect, Brush)
         end if
         return true
     case WM_CLOSE
         PostQuitMessage(0)
         return false
    end select
    return DefWindowProc(hDlg,Msg,wParam,lParam)
end function

sub MakeWindow
    dim as WNDCLASSEX Wc
    Wc.cbSize = sizeof(Wc)
    Wc.Style = CS_HREDRAW Or CS_VREDRAW
    Wc.lpszClassName = @"BufferedWindow"
    Wc.lpfnWndProc = @WindowProc
    Wc.hInstance = GetModuleHandle(0)
    Wc.hCursor = LoadCursor(0, IDC_ARROW)
    if RegisterClassEx(@Wc) then
        CreateWindowEx(0,"BufferedWindow","DoubleBufferedWindow",WS_OVERLAPPEDWINDOW Or WS_VISIBLE Or WS_CLIPCHILDREN Or WS_CLIPSIBLINGS,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,GetModuleHandle(0),0)
    end if    
    dim as MSG M
    while GetMessage(@M, 0, 0, 0) > 0
        TranslateMessage(@M)
        DispatchMessage(@M)
    wend
end sub

MakeWindow

aurelVZAB
Posts: 681
Joined: Jul 02, 2008 14:55
Contact:

Post by aurelVZAB »

Thanks Eodor ...i will try your way to.

TJF i dont get it what you mean exactly that im on a wrong way ?????
Becuse i ask only for win32 api ?
or something ....
aurelVZAB
Posts: 681
Joined: Jul 02, 2008 14:55
Contact:

Post by aurelVZAB »

Ohh ups i see what you mean ...
I know that you are experts and question is simple enough...i hope
Post Reply