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 800x600 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 800x400 picture in a 600x400 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



Comments

  • Please help me on Directshow +MFC

    Posted by nvnoi76 on 12/01/2004 09:45pm

    Hi, I write a program to play Video files using Directshow and MFC ( Visual C++ 6.0 , DirectX 9.0 ) I compile in DEBUG mode, It runs well and correctly. But when I compile in RELEASE mode. It reports error : "Unhandle exception in Playwnd.exe (MFC42.dll)0xC0000005: Access Violation" Is there any problem on MFC42.dll ? Please help me to correct it. Thank you very much. Noi - nvnoi76@yahoo.com

    Reply
  • How about load bitmap from VC resource?

    Posted by Legacy on 10/30/2003 12:00am

    Originally posted by: Dengting

    ??

    Reply
  • DirectX7 on NT/2k/XP??

    Posted by Legacy on 12/30/2001 12:00am

    Originally posted by: Paddybaer

    Have a problem:
    
    Everytime when I start creating other Surfaces than the normal PrimarySurface and BackpufferSurface the application is being terminated without any message. When I worked on Windows Me I didn't have this problem, but since I'm on Win2k or WinXP I do!
    Doesn't DirectDraw7 work on 2k or XP?

    Thanks,
    Patrick

    Reply
  • Sorry guys

    Posted by Legacy on 12/27/2001 12:00am

    Originally posted by: J�sep

    I'm sorry guys but I haven't been following up on my article since my interests have guided me elsewhere( java ) and since I wrote this article I've become a little rusty in directdraw.

    I still hope you found them usefull.

    Best wishes
    J�sep Valur Gu�laugsson

    Reply
  • Movie

    Posted by Legacy on 12/25/2001 12:00am

    Originally posted by: INC

    Hi,

    I am currently doing a project which needs me to get a user input...

    The user input will specify the picture frames the user wants to view... eg: from frame 1 to frame 200.
    After that, my GUI is supposed to display all these frames in running order... just like a movie....

    Can your code be used for loading many picture frames....
    so that when I open a file, the frames will automatically
    load itself and subsequently show themselves... so that they run like a movie....(the file is not avi or mpeg file)

    If possible, please direct me how I can go about doing that... thank you...

    Reply
  • improvement suggestions

    Posted by Legacy on 09/04/2001 12:00am

    Originally posted by: meynaf

    More stuff to add :

    - Since directdraw does not support color remapping on the fly, you could add some custom Blt() function to do this.
    (gdi's BitBlt() can do this, but it's too slow)

    - LoadImage() can't load anything else than BMP, but the wrapper classe might add other formats to make surfaces from

    Reply
  • Monitor on or off

    Posted by Legacy on 07/06/2001 12:00am

    Originally posted by: Adrian

    Hi
    This has nothing to do with directx (I think) but I thought I might get some of your knowledge.

    I need to be able to determine programmatically whether my monitor is on or off. Do you know of a way to do this?

    Thanks
    Adrian

    Reply
  • Capture with direct draw

    Posted by Legacy on 05/02/2001 12:00am

    Originally posted by: Penina

    Is there a way to capture video (not from a file)
    with direct draw? I need to get the movie frame
    by frame, in order to proccess it.

    Thank you,
    Penina.

    Reply
  • How can I use simple GDI Text in(not on) Direct3d window?

    Posted by Legacy on 03/15/2001 12:00am

    Originally posted by: Nikita Saprykin

    How can I use simple GDI Text in(not on) Direct3d window?

    Reply
  • IID_IDirectDraw2???

    Posted by Legacy on 07/27/2000 12:00am

    Originally posted by: Jesper

    From which libery is IID_IDirectDraw2. (I have a linkage error)
    

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • It's time high-level executives and IT compliance officers recognize and acknowledge the danger of malicious insiders, an increased attack surface and the potential for breaches caused by employee error or negligence. See why there is extra emphasis on insider threats.

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds