Double Buffered window?
Double Buffered window?
Hi to all...!
Is anyone of you try to create double buffered window in FreeBasic ?
Is anyone of you try to create double buffered window in FreeBasic ?
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.
As for fb's built-in gfxlib, it's very easy: Just create two screen pages and flip between them in your drawing 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 ' <-- 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
Code: Select all
ScreenRes 800, 600, 32, ,2 '' <-- default value for buffer count, 2 means OpenGL window
Do
'' Draw here
Flip
Loop
Yes, under fbgfx, there is already an internal double buffering.Merick wrote:Actually, fbgfx is double buffered by default, you just need to use screenlock and screenunlock around the graphics drawing commands
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
And you're on a wrong path, my friend.aurelVZAB wrote:Yes Michael you on a right track......thank you very much.
I wanna use only native win32 api nothing extra.
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.
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