Jump to content
  • Advertisement
Sign in to follow this  
NextEpisode1

"C++" double buffering mess

This topic is 2871 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hy , im pretty new to the windows programing, i was tying to implement some sort of bitmap double buffering (dont know why , anyway bitmaps are not loaded directly to the screen as far as i'm aware but anyway).

My problem is that the BackBuffer::Present function doesn't output the bitmap onto the correct coords (of the client window) but instead i think it outputs onto another window's DC(hunch).here is my code , il appreciate if someone can tell me what i'v missed and maybe give me some "code formating tip" or tip of any sort thanks in advance.

P.S: the window is can not be resized ,and this is supposed to be just a trivial emplementation in training purposes.


//BackBuffer.h
#pragma once
#include <windows.h>
const int ciSwidth=600; //<---window size
const int ciSheight=600; //<---window size

class BackBuffer
{
public:
HDC bbDC;
HBITMAP hBmp;
HWND hWindow;
HBITMAP hBBsurface;
int iBBWidth; //<--the loaded bmp's size
int iBBHeight; //<--the loaded bmp's size
BackBuffer(HWND hWindow);
~BackBuffer();
void Present(int x, int y);
void Load(int iBmpID, HINSTANCE ghAppInst,int iWidth,int iHeight);//<--loads the bmp onto the babkbuffer,iBmpId is the resource.h image's id
};


//BackBuffer.cpp
#include "BackBuffer.h"

HDC hWndDC;
BackBuffer::BackBuffer(HWND hWindow)
{
this->hWindow=hWindow;
HDC hWndDC=GetDC(hWindow);
bbDC = CreateCompatibleDC(hWndDC);
ReleaseDC(hWindow, hWndDC);
}

BackBuffer::~BackBuffer()
{
SelectObject(bbDC, hBmp);
DeleteDC(bbDC);
}

void BackBuffer::Load(int iBmpID,HINSTANCE ghAppInst,int iWidth,int iHeight)
{
this->iBBWidth=iWidth;
this->iBBHeight=iHeight;
(HBRUSH)::GetStockObject(WHITE_BRUSH);
Rectangle(bbDC,0,0,ciSwidth,ciSheight);
hBBsurface=LoadBitmap(ghAppInst,MAKEINTRESOURCE(iBmpID));
/*BITMAP oldBM = */(HBITMAP)SelectObject(bbDC, hBBsurface);
}

void BackBuffer::Present(int x, int y)
{
HDC hWndDC=GetDC(hWindow);
BitBlt(hWndDC,x,y,x+iBBWidth,y+iBBHeight,bbDC,0,0,SRCCOPY);
ReleaseDC(hWindow,hWndDC);
}

//WinMain.cpp
#include "BackBuffer.h"

BackBuffer *Buffer=NULL;
HINSTANCE ghAppInst=0;
HWND hWindow=0;
RECT rClientRect;




bool InitMainWindow();
int Run();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);



int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
if( !InitMainWindow() )
{
MessageBox(0, "Window Creation Failed.", "Error", MB_OK);
return 0;
}
return Run();
}

bool InitMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)::GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "MyWndClassName";
RegisterClass( &wc );
hWindow = ::CreateWindow("MyWndClassName","Anim", WS_OVERLAPPED | WS_SYSMENU,200, 200, ciSwidth, ciSheight, 0,0, ghAppInst, 0);
//char a[10];
//MessageBox(hWindow,itoa((int)hWindow,a,10)," ",IDOK);
if(hWindow == 0)
{
::MessageBox(0, "CreateWindow - Failed", 0, 0);
return false;
}
ShowWindow(hWindow, SW_NORMAL);
UpdateWindow(hWindow);
return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
//GetClientRect(hWindow,&rClientRect);
//char a[10];
//MessageBox(hWindow,itoa(rClientRect.bottom,a,10)," ",IDOK);
Buffer= new BackBuffer(hWindow);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}


int Run()
{
//GetClientRect(hWindow,&rClientRect);
//char a[10];
//MessageBox(hWindow,itoa(rClientRect.right,a,10)," ",IDOK);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else

{
Buffer->Load(101,ghAppInst,200,200);
Buffer->Present(rClientRect.left,rClientRect.left);//if i put 0,0 the image is suposed to be in the top left corner of the window , instead it shown in the left top corner of the desktop
Sleep(200);
}



}
return 0;
}

Share this post


Link to post
Share on other sites
Advertisement
Ok, here are all 3 files in source tags. Took me roughly a minute. I've included my own commentary in form of comments.

//BackBuffer.h
#pragma once
#include <windows.h>

/* The meaning of these constants could have been more meaningful. For example:
const int WND_WIDTH
const int WND_HEIGHT
It's a common convention to make constants in all caps. Besides, why do you need these? You store them in the back buffer anyway so just pass them as an argument to the constructor */

const int ciSwidth=600; //<---window size
const int ciSheight=600; //<---window size

/* Since you are using a class, the general idea behind the design of these is
to abstract the functionality so that the user doesn't have to deal with it. If
you're going to make everything fully available, the purpose of having one is
eliminated. You could have used a struct and that would've spared you the
public: statement at the least. Frankly, none of the variables are needed to be
public, unless they are often needed outside, but, in the given example, this
isn't the case. Make your variables private and let the class handle everything
on it's own. */

class BackBuffer
{
public:
// from here
HDC bbDC;
HBITMAP hBmp;
HWND hWindow;
HBITMAP hBBsurface;
int iBBWidth; //<--the loaded bmp's size
int iBBHeight; //<--the loaded bmp's size
// to here should be private. All the rest is public
BackBuffer(HWND hWindow);
~BackBuffer();
void Present(int x, int y); // Why does this function take x and y arguments?
/* This function seems impractical. It's better to load the bitmap somewhere
else and let the BackBuffer handle the drawing with a single function. */

void Load(int iBmpID, HINSTANCE ghAppInst,int iWidth,int iHeight);//<--loads the bmp onto the babkbuffer,iBmpId is the resource.h image's id
};


//BackBuffer.cpp
#include "BackBuffer.h"

HDC hWndDC; /* <-- why is this here? */
BackBuffer::BackBuffer(HWND hWindow)
{
/* There is no need to use this-> over here. Why is the argument the same name
as the member variable? Change this. Append an m to the member hWindow or
something so you can distinguish it from the arguments. */

this->hWindow=hWindow;

/* I cannot believe you haven't got a compile error for this. This variable is
defined right outside of this function. Programming is like a ninja -.- You
think you know it, and it strikes you when you least expect it. */

HDC hWndDC=GetDC(hWindow);

/* Now what is your program supposed to do? You've got two of the same
variables redefined and now you're trying to use them. That doesn't seem safe */

bbDC = CreateCompatibleDC(hWndDC);
ReleaseDC(hWindow, hWndDC);
}

BackBuffer::~BackBuffer()
{
/* Why are you selecting hBmp into the backbuffer before deleting it? */
SelectObject(bbDC, hBmp);
DeleteDC(bbDC);
}

void BackBuffer::Load(int iBmpID,HINSTANCE ghAppInst,int iWidth,int iHeight)
{
/* Again, this-> is not necessary here. I'm beginning to think that you would
want to create a Back Buffer for all images separately which isn't the idea
behind a back buffer */

this->iBBWidth=iWidth;
this->iBBHeight=iHeight;

(HBRUSH)::GetStockObject(WHITE_BRUSH); /* What? Where does that brush go? */
Rectangle(bbDC,0,0,ciSwidth,ciSheight);
hBBsurface=LoadBitmap(ghAppInst,MAKEINTRESOURCE(iBmpID));
/*BITMAP oldBM = */(HBITMAP)SelectObject(bbDC, hBBsurface); /* Unfinished? */
}

void BackBuffer::Present(int x, int y)
{
/* The same mistake. Have you compiled this yet? I cannot believe this went
through a compiler without problems. This variable is defined in a global scope
and it's redefined in 2 places. O.o?! */

HDC hWndDC=GetDC(hWindow);
/* The 3rd and 4th arguments to BitBlt are the width and height of the bitmap
to be drawn, not the x and y of the bottom right corner. This is drawing the
bitmap into oblivion depending what x, y, iBBWidth, and iBBHeight are. Take out
the x+ and y+ and leave the width and height on their own. */

BitBlt(hWndDC,x,y,x+iBBWidth,y+iBBHeight,bbDC,0,0,SRCCOPY);
ReleaseDC(hWindow,hWndDC);
}

//WinMain.cpp
#include "BackBuffer.h"

BackBuffer *Buffer=0;
HINSTANCE ghAppInst=0;
HWND hWindow=0;
RECT rClientRect;

bool InitMainWindow(); /* <-- Why does this exist? Just merge it with WinMain
or at least put it all the way at the bottom. */


/* Put these two functions up here. There is no reason why they should be on
the bottom. You do not need access to WinMain or the InitMainWindow functions
therefore they only create superfluous code that you have to skip over in order
to get to the cogs and wheels of your program. This isn't helping you, it's
only slowing you down. Replace these two up here. */

int Run();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
if( !InitMainWindow() )
{
MessageBox(0, "Window Creation Failed.", "Error", MB_OK);
return 0;
}
return Run();
}

bool InitMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)::GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "MyWndClassName";
RegisterClass( &wc );
hWindow = ::CreateWindow("MyWndClassName","Anim", WS_OVERLAPPED | WS_SYSMENU,200, 200, ciSwidth, ciSheight, 0,0, ghAppInst, 0);
//char a[10];
//MessageBox(hWindow,itoa((int)hWindow,a,10)," ",IDOK);
if(hWindow == 0)
{
::MessageBox(0, "CreateWindow - Failed", 0, 0);
return false;
}
ShowWindow(hWindow, SW_NORMAL);
UpdateWindow(hWindow);
return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_CREATE:
//GetClientRect(hWindow,&rClientRect);
//char a[10];
//MessageBox(hWindow,itoa(rClientRect.bottom,a,10)," ",IDOK);
Buffer= new BackBuffer(hWindow);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}


int Run()
{
//GetClientRect(hWindow,&rClientRect);
//char a[10];
//MessageBox(hWindow,itoa(rClientRect.right,a,10)," ",IDOK);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Buffer->Load(101,ghAppInst,200,200);
/* Look into the BackBuffer implementations and fix everything. I'm willing to bet that the problem is there */
Buffer->Present(rClientRect.left,rClientRect.left);//if i put 0,0 the image is suposed to be in the top left corner of the window , instead it shown in the left top corner of the desktop
Sleep(200);
}
}

return 0;
}


Everything that I could find that looks fishy I've pointed out with /* */ style comments. They appear in gray in the above source listing. Good luck fixing it :D

Share this post


Link to post
Share on other sites
Put a break point in the WM_CREATE handler and check the value of the global variable hWindow. I think that the problem you're talking about is caused by the fact that hWindow is NULL at that time. The global hWindow is being filled with the value returned by CreateWindow() but the WM_CREATE handler will get called before CreateWindow() returns. Use the local hWnd in there instead.

Anyway, this code has lots of problems. You should be handling WM_PAINT and blitting from the offscreen buffer there, shouldn't be sleeping in the message loop, etc. If it's any help I talk about doing this sort of thing at the end of this thread from a couple of weeks ago.

Share this post


Link to post
Share on other sites
off-topic:

It can does largely improve code quality when instead of

const int ciSwidth=600; //<---window size


you give meaningful, non-encrypted/-mnenomic names for all your entities:

const int windowWidth=600;


Don't use comments when the same a better effect can be achieved by meaningful names.

Share this post


Link to post
Share on other sites
Quote:
Original post by boogyman19946
Ok, here are all 3 files in source tags. Took me roughly a minute. I've included my own commentary in form of comments.

*** Source Snippet Removed ***

Everything that I could find that looks fishy I've pointed out with /* */ style comments. They appear in gray in the above source listing. Good luck fixing it :D


Thank vm for the effort, how did u do that? :))

Share this post


Link to post
Share on other sites
Quote:
Original post by phresnel
off-topic:

It can does largely improve code quality when instead of

*** Source Snippet Removed ***

you give meaningful, non-encrypted/-mnenomic names for all your entities:

*** Source Snippet Removed ***

Don't use comments when the same a better effect can be achieved by meaningful names.


true , i commented to be clear for the eventual code reader, but thats a very good piece of advice , thanks

Share this post


Link to post
Share on other sites
Quote:
Original post by boogyman19946
Ok, here are all 3 files in source tags. Took me roughly a minute. I've included my own commentary in form of comments.

*** Source Snippet Removed ***

Everything that I could find that looks fishy I've pointed out with /* */ style comments. They appear in gray in the above source listing. Good luck fixing it :D


P.S i understand what you mean in those comments. Here is my point of view , why i did things like that:

1) the redefinition of the Window's DC: iv read in... Microsoft - Programming Windows API 5th
that a Dc should be released right after you did the operations with it (painting or in this example creating a compatible dc) lots of resources lost otherwise.

2) the actual redefinition of the same DC : "HDC hwndDC" since i get the dc again and again for every function it made no sense to make it global , so i defined it in the functions, and since it's local when the function ends , it goes out of scope , thats why that was redifined that way.

3) the splitting of the window cration process and message handling into functions: i did that because my programing is still very messy (im sure you figured that out already :P )and it makes things a little more clear , when a certain task is performed by appropriate function (at least for me).

4)the Prototypes in win main: I gues it was in K&R that was said that it's a good habit to write the actual functions at the end of the cpp file (and i believed it with no other questions asked , like ppl read the bible :p)

5) oh and the encapsulation problem, i got lazy and i didnt make functions for returning the private data so i made all data public, by bad.

6) and that thing compiles , builds and even outputs the right thing... but in the wrong place





Share this post


Link to post
Share on other sites
Quote:
Original post by jwezorek
Put a break point in the WM_CREATE handler and check the value of the global variable hWindow. I think that the problem you're talking about is caused by the fact that hWindow is NULL at that time. The global hWindow is being filled with the value returned by CreateWindow() but the WM_CREATE handler will get called before CreateWindow() returns. Use the local hWnd in there instead.

Anyway, this code has lots of problems. You should be handling WM_PAINT and blitting from the offscreen buffer there, shouldn't be sleeping in the message loop, etc. If it's any help I talk about doing this sort of thing at the end of this thread from a couple of weeks ago.


that sounds the most reasonable thing i heard so far (not only here , but everywhere i asked)
,that went throw my mind also (like a hunch as i said in the post) il try fixing that up .

("Anyway, this code has lots of problems." told ya its not a real application, i'm learning)

thanks

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!