Smart DirectDraw Classes

Environment: VC6 NT4

These wrapper classes are an easy way to use DirectDraw in you applications. It makes it easier for you by doing the following things:

  1. It initialises almost everything for you.
  2. Its very easy to change videomodes and device drivers
  3. It solves the problem of surface being bigger than screen by automatically dividing into regions of appropriate size.
  4. It automatically loads/reloads lost surfaces.
  5. It automatically clips at the edge of screen (in fullscreen mode. DirectX 5.0 made the whole picture disapear if it crossed boundaries).
  6. Easy to load bitmap into surface.
  7. Less defensive coding needed as a result of 1-6.

Example of use:


g_DDHolder.InitializeDirectDraw(0,ghWnd);
g_DDHolder.SetMode(800,600,16,FALSE);
RECT rc;
SetRect(&rc,0,0,0,0);//Load whole pictures
g_pDSPicture1=g_DDHolder.MakeSurface(“dx.bmp”,rc,RGB(255,255,255));
g_pDSPicture2=g_DDHolder.MakeSurface(“codeguru.bmp”,rc,RGB(0,0,0));
g_DDHolder.ClearBackSurface();
SetRect(&rc,0,0,0,0);//Draw whole surfaces
g_DDHolder.Draw(g_pDSPicture1,0,0,rc,1,TRUE);//True=>transparent
g_DDHolder.Draw(g_pDSPicture2,200,300,rc,1,FALSE);
g_DDHolder.Show();//uses pageflipping in fullscreen otherwise standard doublebuffering

History:

I was making a videogame (That I didnt finish) and when studying Inside DirectX I realised that there is alot of overhead when initialising DirectDraw. And I realised that I would propaply always have the same settings, therefore I wrote these classes so I could very easily with very little work use directdraw instead of regular GDI functions in my applications.

Note:

There may be that other settings suitable for your needs, I think the best way to look at these classes is to look at them as a framework. You can very easily change values or add parameters. f.eks. you could make ClearBackSurface() draw white instead of black on surface.

If you see an error in this code or even if you have some critisism in my coding style then please let me know what to improve. I aim to be a C++ master so I welcome every advice.

Thanks for reading this article and thanks to codeguru for this great site. You always give me some extra knowledge into my cup.

This framework is divided into 3 classes. CDirectDrawHolder,CDDSurfaceHolder & CDDSurfaceList.


class CDDirectDrawHolder {
void SetScreenRect(int x0,int y0,int x1,int y1);
BOOL Draw(CDDSurfaceHolder *pDSHolder,int ix,int iy,
RECT rc,double dscale=1.0,BOOL btransparent=FALSE) const;
void ClearBackSurface();
BOOL Show() const;
void GetAvailDisplayModes(
LPDDENUMMODESCALLBACK lpEnumModesCallback,LPVOID lpContext) const;
CDDSurfaceHolder *MakeSurface(char szBuffer[],
RECT rc,DWORD dwcolor=0);
BOOL InitializeDirectDraw(LPGUID,HWND);
void ReleaseAllResources();
LPDIRECTDRAW2 GetDrawObject() const;
HWND GetWindowHandle() const;
BOOL SetMode(int iWidth,int iHeight,int iBpp,BOOL bFull);
BOOL FlipToGDI();
RECT GetScreenRect() const { return m_cScreenRect; }
LPDIRECTDRAWSURFACE GetBackSurface() const;
};

The other classes dont have interfaces. Theyre all automatic.
I made a little application just to demonstrate their ease of use. It and the project is in the zip.
Now lets get into the code shall we.

First I want to make CDDirectDrawHolder:


class CDDirectDrawHolder {
LPDIRECTDRAWSURFACE m_lpDDPrimarySurface;//Primary surface
LPDIRECTDRAWSURFACE m_lpDDBackSurface;//Back surface
LPDIRECTDRAW2 m_lpDD2;
HWND m_hWnd;//Window handle
BOOL m_bFullScreen;//indicates fullscreen or not
/*******************************************
*used when in windowmode, to clip inside window
*******************************************/
LPDIRECTDRAWCLIPPER m_lpClipper;
/*******************************************
*Used to contain screen dimension to clip against in
*fullscreen mode and used by CDDSurfaceHolder to
*divide each surface into smaller regions if necessary
*******************************************/
RECT m_cScreenRect;
//So CDDSurfaces get notified of device change
vector< CDDSurfaceHolder* > m_cSurfaces;
public:
//Need I say more
void SetScreenRect(int x0,int y0,int x1,int y1);
/**********************************************
*Draws the rc part of image in CDDSurfaceHolder at pos ix,iy,
*it is scaled at dscale and if transparent is on
*then it doesn’t draw the color specified in colorkey
***********************************************/
BOOL Draw(CDDSurfaceHolder *pDSHolder,int ix,int iy,RECT rc,
double dscale=1.0,BOOL btransparent=FALSE) const;
/*****************************************
*Makes back surface black.
*Note:Easy to modifiy so it uses another color
*or take as a parameter,black is classic
*****************************************/
void ClearBackSurface();
/*****************************************
*Flips surfaces if in fullscreen, else uses
*standard double buffering.
*Note: If you haven’t realised it already
*everytime you use Draw you draw on the back-
*surface and it isn’t visible until you call show
*****************************************/
BOOL Show() const;
/******************************************
*This function takes a callback function and sends
*it the modes. Neccessary to see what mode
*your driver supports.
******************************************/
void GetAvailDisplayModes(
LPDDENUMMODESCALLBACK lpEnumModesCallback,LPVOID lpContext) const;
/******************************************
*It fills the CDDSurfaceHolder with the data
*so it can load/reload from resource/disk
*pszBuffer,rc stores the part of the image that you
*want into the surface. {0,0,0,0} makes rc equal to
*the picture size. dwcolor sets the colorkey. f.eks.
*Setting dwcolor equal to RGB(0,0,0) and drawing
*with btransparent=TRUE makes black transparent
******************************************/
CDDSurfaceHolder *MakeSurface(char szBuffer[],
RECT rc,DWORD dwcolor=0);
//Constructor
CDDirectDrawHolder();

/******************************************
*Initializes the directdraw object,sets
*cooperative level and screen rect
*******************************************/
BOOL InitializeDirectDraw(LPGUID,HWND);
/****************************************************
*Set the modes,f.eks. 800,600,16,TRUE for fullscreen
*in 800×600 16 bit colors.
*Note: If bFull=FALSE then other parameters are ignored
****************************************************/
BOOL SetMode(int iWidth,int iHeight,int iBpp,BOOL bFull);
/***************************************
*Release all surfaces and Resources.
*Clipper,directdraw etc,etc
***************************************/
void ReleaseAllResources();
// A backdoor to directdraw,just in case
LPDIRECTDRAW2 GetDrawObject() const;
//Get the window handle
HWND GetWindowHandle() const;
/***************************************************
*Call this when you use GDI without drawing on your dx
*surfaces. f.eks. display a dialog,menu,etc. Otherwise
*you dialog may be drawn into the backbuffer and is
*therefor not visible
***************************************************/
BOOL FlipToGDI();
RECT GetScreenRect() const { return m_cScreenRect; }
/****************************************************
*Helpfull if you want to draw on the backsurface
****************************************************/
LPDIRECTDRAWSURFACE GetBackSurface() const;
virtual ~CDDirectDrawHolder();
};

////////////////////////
//DirectDrawHolder.cpp

CDDirectDrawHolder::CDDirectDrawHolder()
:m_lpDDPrimarySurface(NULL),
m_lpDDBackSurface(NULL),
m_lpDD2(NULL),
m_hWnd(NULL),
m_bFullScreen(FALSE),
m_lpClipper(NULL)
{}

CDDirectDrawHolder::~CDDirectDrawHolder()
{
//doubt this is neccessary but just in case
m_cSurfaces.erase(m_cSurfaces.begin(),m_cSurfaces.end());
if(m_lpDD2) ReleaseAllResources();
}

//////////////////////////////////////
//bmp picture path,rectangle of the picture
//to be copied into a surface,0,0,0,0 causes
//all the pictures to be loaded, color
//value of the transparent color.
//////////////////////////////////////
CDDSurfaceHolder *CDDirectDrawHolder::MakeSurface(char *pszBuffer,
RECT rc,DWORD dwcolor)
{
CDDSurfaceHolder *pDDSurfHolder;
pDDSurfHolder=new CDDSurfaceHolder(*this);
pDDSurfHolder->LoadBitmap(pszBuffer,
rc.left,rc.top,
rc.right,rc.bottom,dwcolor);
m_cSurfaces.push_back(pDDSurfHolder);//Store on list
return pDDSurfHolder;
}
////////////////////////
//Initializes directdraw,
//If GUID == null => Uses default directdraw driver
////////////////////////

BOOL CDDirectDrawHolder::InitializeDirectDraw(LPGUID lpguid,HWND hwnd)
{
LPDIRECTDRAW lpDD;
BOOL bSucceeded;

if(FAILED(DirectDrawCreate(lpguid,&lpDD,NULL))) return FALSE;

if(SUCCEEDED(lpDD->QueryInterface(IID_IDirectDraw2,
(LPVOID*)&m_lpDD2))) bSucceeded=TRUE;
else bSucceeded=FALSE;

lpDD->Release();

m_hWnd=hwnd;//store windowhandle

if(FAILED(m_lpDD2->SetCooperativeLevel(m_hWnd,
DDSCL_EXCLUSIVE|
DDSCL_FULLSCREEN|
DDSCL_NOWINDOWCHANGES))) {
m_lpDD2->Release();
m_lpDD2=NULL;
bSucceeded=FALSE;
}

return bSucceeded;
}

LPDIRECTDRAW2 CDDirectDrawHolder::GetDrawObject() const
{
return m_lpDD2;
}

HWND CDDirectDrawHolder::GetWindowHandle() const
{
return m_hWnd;
}

//////////////////////////////////////
// Gets available display modes
// and sends them to a callback function
// of type EnumModesCallback
// Used to enumerate display modes
//////////////////////////////////////
void CDDirectDrawHolder::GetAvailDisplayModes(
LPDDENUMMODESCALLBACK lpEnumModesCallback,LPVOID lpContext) const
{
m_lpDD2->EnumDisplayModes(0,NULL,
lpContext,lpEnumModesCallback);
}
//////////////////////////////////
// Releases everything.
//////////////////////////////////
void CDDirectDrawHolder::ReleaseAllResources()
{
//Notify outstanding surfaces
vector< CDDSurfaceHolder* >::iterator CItr;
for(CItr=m_cSurfaces.begin();CItr!=m_cSurfaces.end();CItr++)
(*CItr)->ClearMem();

if(m_lpClipper) {
m_lpClipper->Release();
m_lpClipper=NULL;
}
if(m_lpDDBackSurface) {
m_lpDDBackSurface->Release();
m_lpDDBackSurface=NULL;
}

if(m_lpDDPrimarySurface) {
m_lpDDPrimarySurface->Release();
m_lpDDPrimarySurface=NULL;
}
m_lpDD2->Release();
m_lpDD2=NULL;
}

////////////////////////////////////////
// if bfull is high => iwidth,iheight,ibpp will
// be used else current res.
////////////////////////////////////////
BOOL CDDirectDrawHolder::SetMode(int iwidth,
int iheight,
int ibpp,BOOL bfull)
{
DDSURFACEDESC ddsd;
m_bFullScreen=bfull;
DWORD dWord;

if(m_lpClipper) {
m_lpClipper->Release();
m_lpClipper=NULL;
}
if(m_lpDDBackSurface) {
m_lpDDBackSurface->Release();
m_lpDDBackSurface=NULL;
}
if(m_lpDDPrimarySurface) {
m_lpDDPrimarySurface->Release();
m_lpDDPrimarySurface=NULL;
}

if(bfull)
dWord=DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN;
else dWord=DDSCL_NORMAL;
if(FAILED(m_lpDD2->SetCooperativeLevel(m_hWnd,dWord))) {
m_lpDD2->Release();
m_lpDD2=NULL;
return FALSE;
}

if(bfull) {
m_lpDD2->SetDisplayMode(iwidth,iheight,ibpp,0,0);
ZeroMemory(&ddsd,sizeof(ddsd));
ddsd.dwFlags=DDSD_CAPS| DDSD_BACKBUFFERCOUNT;
ddsd.dwSize = sizeof( ddsd );
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount=1;
if(FAILED(m_lpDD2->CreateSurface(&ddsd,&m_lpDDPrimarySurface,NULL))) {
return FALSE;
}
DDSCAPS ddsCaps;
ddsCaps.dwCaps=DDSCAPS_BACKBUFFER;
m_lpDDPrimarySurface->GetAttachedSurface(&ddsCaps,&m_lpDDBackSurface);
SetRect(&m_cScreenRect,0,0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
}
else {
ZeroMemory(&ddsd,sizeof(ddsd));
ddsd.dwFlags=DDSD_CAPS;
ddsd.dwSize = sizeof(ddsd);
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if(FAILED(m_lpDD2->CreateSurface(&ddsd,&m_lpDDPrimarySurface,NULL))) {
return FALSE;
}
m_lpDDPrimarySurface->GetSurfaceDesc(&ddsd);
ddsd.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
if(FAILED(m_lpDD2->CreateSurface(&ddsd,&m_lpDDBackSurface,NULL)))
return FALSE;
GetWindowRect(m_hWnd,&m_cScreenRect);

if(FAILED(m_lpDD2->CreateClipper(0,&m_lpClipper,NULL))) return FALSE;
m_lpClipper->SetHWnd(0,m_hWnd);
m_lpDDPrimarySurface->SetClipper(m_lpClipper);
m_lpClipper->Release();
}
return TRUE;
}

///////////////////////////////
//Fills backbuffer with black//
///////////////////////////////
void CDDirectDrawHolder::ClearBackSurface()
{
DDBLTFX ddbltfx;
ZeroMemory(&ddbltfx,sizeof(ddbltfx));
ddbltfx.dwSize = sizeof( ddbltfx );
m_lpDDBackSurface->Blt(&m_cScreenRect,NULL,NULL,
DDBLT_COLORFILL|DDBLT_WAIT,&ddbltfx);
}

//////////////////////////////////////////
// Draws the rc part of surface at
// position ix,iy on the scale dscale
// (expands down & right). if btransparent
// is high the colorkey is used.
//////////////////////////////////////////
BOOL CDDirectDrawHolder::Draw(CDDSurfaceHolder *pDSHolder,
int ix,int iy,RECT rc,
double dscale,BOOL btransparent) const
{
using namespace std;

RECT rcdest;
if(rc.left==rc.right)
CopyRect(&rc,&pDSHolder->m_cRect);

LPDIRECTDRAWSURFACE lpSurf;

vector< CDDSurfaceList* > *pListVector=pDSHolder->GetSurface();
if(pListVector->empty()) return FALSE;

vector< CDDSurfaceList* >::iterator CItr;

int iOffset;
RECT rcNowSource;
// Iterate throught the list and
// for every iteration draw the visible
// part of the surface
for(CItr=pListVector->begin();CItr!=pListVector->end();CItr++) {
lpSurf=(*CItr)->m_lpSurface;
//rc heildarmyndin sem vill teikna
//rcNowSource source m myndhluta

//Find the part that rc covers
CopyRect(&rcNowSource,&(*CItr)->m_cRect);

rcNowSource.left=max(rc.left,rcNowSource.left);
rcNowSource.top=max(rc.top,rcNowSource.top);
rcNowSource.right=min(rc.right,rcNowSource.right);
rcNowSource.bottom=min(rc.bottom,rcNowSource.bottom);

rcdest.left=rcNowSource.left-rc.left;
rcdest.left=(long)(rcdest.left*dscale);
rcdest.left+=ix;
rcdest.top=rcNowSource.top-rc.top;
rcdest.top=(long)(rcdest.top*dscale);
rcdest.top+=iy;

rcdest.right=rcNowSource.right-rcNowSource.left;
rcdest.right=(long)(rcdest.right*dscale);
rcdest.right+=rcdest.left;
rcdest.bottom=rcNowSource.bottom-rcNowSource.top;
rcdest.bottom=(long)(rcdest.bottom*dscale);
rcdest.bottom+=rcdest.top;

//Clipping
rcNowSource.right-=(*CItr)->m_cRect.left;
rcNowSource.bottom-=(*CItr)->m_cRect.top;
rcNowSource.left-=(*CItr)->m_cRect.left;
rcNowSource.top-=(*CItr)->m_cRect.top;

if(rcdest.left< m_cScreenRect.left) { iOffset=m_cScreenRect.left-rcdest.left; rcNowSource.left+=(long)(iOffset/dscale); rcdest.left+=iOffset; } if(rcdest.top< m_cScreenRect.top) { iOffset=m_cScreenRect.top-rcdest.top; rcNowSource.top+=(long)(iOffset/dscale); rcdest.top+=iOffset; } if(rcdest.right > m_cScreenRect.right) {
iOffset=rcdest.right-m_cScreenRect.right;
rcNowSource.right-=(long)(iOffset/dscale);
rcdest.right-=iOffset;
}
if(rcdest.bottom > m_cScreenRect.bottom) {
iOffset=rcdest.bottom-m_cScreenRect.bottom;
rcNowSource.bottom-=(long)(iOffset/dscale);
rcdest.bottom-=iOffset;
}
if(rcNowSource.left > rcNowSource.right ||
rcNowSource.top>rcNowSource.bottom
|| rcdest.right< 0 || rcdest.bottom< 0 || rcdest.left > m_cScreenRect.right ||
rcdest.top > m_cScreenRect.bottom) continue;

if(btransparent)
m_lpDDBackSurface->Blt(&rcdest,lpSurf,&rcNowSource,
DDBLT_WAIT|DDBLT_KEYSRC,NULL);
else m_lpDDBackSurface->Blt(&rcdest,lpSurf,&rcNowSource,
DDBLT_WAIT,NULL);
}
return TRUE;
}

//////////////////////////////////////////
// Call this function if you want to make
// the gdi surface the current surface
// f.eks. for displaying dialog boxes
//////////////////////////////////////////

BOOL CDDirectDrawHolder::FlipToGDI()
{
if(m_lpDD2) m_lpDD2->FlipToGDISurface();
return TRUE;
}

void CDDirectDrawHolder::SetScreenRect(int x0,int y0,int x1,int y1)
{
SetRect(&m_cScreenRect,x0,y0,x1,y1);
}

inline LPDIRECTDRAWSURFACE CDDirectDrawHolder::GetBackSurface() const
{
return m_lpDDBackSurface;
}

///////////////////////
// If in fullscreen mode then use
// page flipping else use standard blitting
///////////////////////

BOOL CDDirectDrawHolder::Show() const
{
return m_bFullScreen?
SUCCEEDED(m_lpDDPrimarySurface->Flip(NULL,DDFLIP_WAIT)):
SUCCEEDED(m_lpDDPrimarySurface->Blt(NULL,
m_lpDDBackSurface,NULL,DDBLTFAST_WAIT,NULL));
}

And now the other two:

This class represents regions of each surface
f.eks. if you load a 800×400 picture in a
600×400 resolution mode. The CDDSurfaceHolder makes
two of these to represent the surface.

class CDDSurfaceList {
friend class CDDSurfaceHolder;
friend class CDDirectDrawHolder;
LPDIRECTDRAWSURFACE m_lpSurface;
RECT m_cRect; // Holds list of surfaces
CDDSurfaceList(LPDIRECTDRAWSURFACE lpsurf,RECT rc)
: m_lpSurface(lpsurf),m_cRect(rc){}
virtual ~CDDSurfaceList() {
if(m_lpSurface) m_lpSurface->Release();
}
};

class CDDSurfaceHolder {
//ahh friendsship. C++ I don’t know what I would do
//without you.
friend class CDDirectDrawHolder;
// Picturepath; needed for reloading
char m_szPicturePath[_MAX_PATH];
RECT m_cRect;//Size of the whole Picture
/************************************
*Stores regions of this surface. When
*You change resolution this gets update
************************************/
vector< CDDSurfaceList* > m_cDividedList;
DWORD m_dwColor;//Color of Transparent
/*******************************************
*Fill surface with rect of image
********************************************/
LPDIRECTDRAWSURFACE MakeFilledSurface(HDC hdcImage,RECT rc);
CDDirectDrawHolder &m_cRefHolder;
CDDSurfaceHolder(CDDirectDrawHolder &pholder);
/*******************************************
*I’m lying doesn’t load, only sets m_szPicturepath,
*m_cRect & m_dwColor
*******************************************/
BOOL LoadBitmap(char *szpath,int x0=0,
int y0=0,int x1=0,int y1=0,DWORD dwcolor=0);
// Gets holder
const CDDirectDrawHolder &GetHolder() const { return m_cRefHolder; }
vector< CDDSurfaceList* > *GetSurface();
/******************************************
*This loads the picture & divides it into surfaces
******************************************/
BOOL PutIntoMem();//Load bitmap into memory
//Clears memory of regions…
//Now that i think of it CDDSurfaceRegions would be a much
//better name that CDDSurfaceList.
void ClearMem();
DWORD DDColorMatch(LPDIRECTDRAWSURFACE lpSurf,DWORD dwrgb);
public:
virtual ~CDDSurfaceHolder();
};

And now the implementation of CDDSurfaceHolder:


CDDSurfaceHolder::CDDSurfaceHolder(CDDirectDrawHolder &refholder)
: m_cRefHolder(refholder),m_dwColor(0){}

CDDSurfaceHolder::~CDDSurfaceHolder()
{
//Remember to update CDDirectDrawholders mailing list You
//the will not be awakened from the dead.
vector< CDDSurfaceHolder* >::iterator CItr;
for(CItr=m_cRefHolder.m_cSurfaces.begin();
CItr!=m_cRefHolder.m_cSurfaces.end();CItr++)
if(*CItr==this) {
m_cRefHolder.m_cSurfaces.erase(CItr,CItr);
break;
}
ClearMem();
}

void CDDSurfaceHolder::ClearMem()
{
vector< CDDSurfaceList* >::iterator CItr;
if(!m_cDividedList.empty()) {
for(CItr=m_cDividedList.begin();CItr!=m_cDividedList.end();CItr++)
delete *CItr;
m_cDividedList.erase(m_cDividedList.begin(),m_cDividedList.end());
}
}

/////////////////////////////////
// LoadBitmap
// Sets the members ready to load bitmap when it
// should be loaded into memory.
// szPath : Path to the bitmap.
// x0,y0,x1,y1 sets rect of the bitmap to be loaded.
/////////////////////////////////
BOOL CDDSurfaceHolder::LoadBitmap(char *szpath,
int x0,int y0,
int x1,int y1,
DWORD dwcolor)
{
SetRect(&m_cRect,x0,y0,x1,y1);//Breytt m putintomem ef x0==x1
strcpy(m_szPicturePath,szpath);
m_dwColor=dwcolor;
return TRUE;
}

//This function I saw in the samples. It’s pretty neat.
//It’s very diffircult to estimate f.eks. that value of a
//particular blue because it has different values depending on
//mode. This function makes GDI write to the surface and
//then we read from it.
DWORD CDDSurfaceHolder::DDColorMatch(LPDIRECTDRAWSURFACE lpSurf,DWORD dwrgb)
{
COLORREF rgbT;
HDC hdc;
DWORD dw = CLR_INVALID;
DDSURFACEDESC ddsd;
HRESULT hres;

if (dwrgb != -1 && lpSurf->GetDC(&hdc) == DD_OK)
{
rgbT = GetPixel(hdc, 0, 0); // save current pixel value
SetPixel(hdc,0,0,dwrgb); // set our value
lpSurf->ReleaseDC(hdc);
}

ddsd.dwSize = sizeof(ddsd);
hres=lpSurf->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL);

if (hres == DD_OK)
{
dw = *(DWORD *)ddsd.lpSurface; // get DWORD
dw &= (1 < < ddsd.ddpfPixelFormat.dwRGBBitCount)-1; // mask it to bpp lpSurf->Unlock(NULL);
}
if (dwrgb != CLR_INVALID && lpSurf->GetDC(&hdc) == DD_OK)
{
SetPixel(hdc, 0, 0, rgbT);
lpSurf->ReleaseDC(hdc);
}
return dw;
}

//Loads a part of picture into a surface
LPDIRECTDRAWSURFACE CDDSurfaceHolder::MakeFilledSurface(HDC hdcImage,RECT rc)
{
HDC hdcSurf=NULL;
DDSURFACEDESC ddsd;
ZeroMemory(&ddsd,sizeof(DDSURFACEDESC));
ddsd.dwSize=sizeof(ddsd);
ddsd.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
ddsd.dwHeight=rc.bottom-rc.top;
ddsd.dwWidth=rc.right-rc.left;

LPDIRECTDRAWSURFACE lpSurf;

if(FAILED(m_cRefHolder.GetDrawObject()->CreateSurface(&ddsd,&lpSurf,NULL)))
return NULL;
lpSurf->Restore();

lpSurf->GetDC(&hdcSurf);
BitBlt(hdcSurf,0,0,ddsd.dwWidth,
ddsd.dwHeight,hdcImage,rc.left,rc.top,SRCCOPY);
lpSurf->ReleaseDC(hdcSurf);

if(m_dwColor!=-1) {
DDCOLORKEY ddck;
ddck.dwColorSpaceLowValue =DDColorMatch(lpSurf,m_dwColor);
ddck.dwColorSpaceHighValue=ddck.dwColorSpaceLowValue;
lpSurf->SetColorKey(DDCKEY_SRCBLT, &ddck);
}

return lpSurf;
}

//Gets surface, prepares it if it isn’t ready.
vector< CDDSurfaceList* > *CDDSurfaceHolder::GetSurface()
{
BOOL bLost=TRUE;
if(!m_cDividedList.empty())
bLost=((*m_cDividedList.begin())->m_lpSurface->IsLost()==DDERR_SURFACELOST);
if(bLost) PutIntoMem();
return &m_cDividedList;
}

///////////////////////////////////////
// Load the Bitmap into a list of
// directdrawbuffers.
///////////////////////////////////////
BOOL CDDSurfaceHolder::PutIntoMem()
{
// Free memory if used
ClearMem();

HBITMAP hbm;
HDC hdcImage=NULL;

//Load bitmap
hbm=(HBITMAP) LoadImage(NULL,m_szPicturePath,IMAGE_BITMAP,
0,0,
LR_LOADFROMFILE|LR_CREATEDIBSECTION);

if(!hbm) return FALSE;

// If zero rect then make rect equal to picture.
if(m_cRect.right==m_cRect.left) {
BITMAP bm;
GetObject(hbm,sizeof(BITMAP),(LPVOID)&bm);
SetRect(&m_cRect,0,0,bm.bmWidth,bm.bmHeight);
}

hdcImage=CreateCompatibleDC(NULL);
SelectObject(hdcImage,hbm);

int iScreenWidth,iScreenHeight,iPicWidth,iPicHeight;
RECT rcScreen;

// Get the resolution
rcScreen=m_cRefHolder.GetScreenRect();
//Get the picture rectangle height
iPicHeight=m_cRect.bottom-m_cRect.top+1;
iPicWidth=m_cRect.right-m_cRect.left+1;
//Get the screen rectangle height
iScreenWidth=rcScreen.right-rcScreen.left;
iScreenHeight=rcScreen.bottom-rcScreen.top;

LPDIRECTDRAWSURFACE lpSurf;
RECT rcNow;
int ix,iy;

// Store in memory as a list of surfaces
// Because DD 5.0 doesn’t support images that are
// bigger than screen.

iy=m_cRect.top;
while(iy < iPicHeight) { ix=m_cRect.left; while(ix< iPicWidth) { rcNow.left=ix-m_cRect.left; rcNow.top=iy; rcNow.right=min(ix+iScreenWidth-m_cRect.left,iPicWidth); rcNow.bottom=min(iy+iScreenHeight-m_cRect.top,iPicHeight); lpSurf=MakeFilledSurface(hdcImage,rcNow); m_cDividedList.push_back(new CDDSurfaceList(lpSurf,rcNow)); ix+=iScreenWidth-1; } iy+=iScreenHeight-1; } DeleteDC(hdcImage); DeleteObject(hbm);//Delete lose ends. return TRUE; }

It wouldn’t be very wise to have a
framework like this if it weren’t easy to use. We all know that
this is the aim of OO developement. Off course the downside is
that it is harder to make specialised things. But this framework suit
me. I hope someone else can benefit from it. If not for just to learn from it.

Download demo project – 78 Kb

Download source – 8 Kb

Download a small demo application – 57 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read