Using DirectDraw in Document/View Architecture



Click here for a larger image.

Environment: VC 6, DirectX 7.0 or above, Windows 2000/XP

Preface

I know there are a bunch of classes and related articles written in this DirectX section of www.codeguru.com. So this article is a simple tutorial for people who just started programming in DirectDraw. I hope you guys will enjoy this tutorial.

I was working on some design for Galloping Ghost Productions when I discovered there is a way to integrate a DirectDraw interface into Document/View architecture. People might ask, why add an DirectDraw interface into the view object when a full-screen DirectDraw application can be done? Yes, doing a windowed DirectDraw application is not as cool as doing a full-screen hardcore DirectDraw application. However, you have some advantages when designing a windowed application:

  • It is easier for debugging.
  • It can create an interface for using DirectShow interfaces.
  • It can be used to design windowed games using DirectDraw and Direct3D. KOEI's "Romance of Three Kingdoms" used windowed applications.
  • The concept of this tutorial can integrate into other concepts in www.codeguru.com. For example, there is a small class that can be used for designing screen savers. You can integrate the stuff in this tutorial into that to create DirectDraw-based screen savers.
  • ......

Anyway, let's start the tutorial.

Setup

We all know the CView class is a derivative of the CWnd class. Thus it has all the physical attributes of a window. So it is really not hard at all to add a DirectDraw interface into the view class derived from CView. I find it is much easier to program than doing full-screen DirectDraw. I used to spend hours correcting problems with SetDisplayMode() or SetCooperationLevel(), or problems with off-screen surfaces. All these problems are due to the problem of debugging. It is really hard to debug under full-screen mode. This problem can be eliminated by using windowed mode DirectDraw and with the debugging tools provided by MFC classes. I am wasting time here; sorry.

  1. First, you have to make some slight modifications to Visual C++ 6.0. I am certain that when you installed DirectX 7.0 SDK or above, the environmental variables in Visual C++ are automatically set up correctly. If, after you download my code and compile it and there are errors during linking or compiling, you need to do some tuning:
    1. Go to Tools, Options.
    2. Select the Directories tab.
    3. Select "Include files" in the "Show directories for" list box.
    4. Add the INCLUDE directory of DirectX on top of all directories. For example, if you installed DirectX 7.0 in C:\DXSDK, there is a directory inside called "include", which is C:\DXSDK\INCLUDE; put this directory on top of all other directories shown in the list box. See the following figure:



    5. Do the same thing with the LIB directory by selecting "Library files" in "Show directories for"; then find the LIB directory in your DirectX SDK directory, For example, if you installed DirectX 7.0 in C:\DXSDK, then there is a directory inside called "include", which is C:\DXSDK\LIB, put this directory on top of all other directories shown in the list box. See the following figure:



      Then, click OK.
  2. If there still are problems in linking, add some more things to the project settings:
    1. Go to Project, Settings.
    2. Select the Link tab.
    3. In "Object/library modules", add "ddraw.lib" and "dxguid.lib". See the following figure:



      Click here for a larger image.

      Then click OK.
  3. Now you can download my code, compile it, and run.

My Code

I wrote this CDDrawSystem class (located in DDrawSystem.h and DDrawSystem.cpp files) to display graphics using DirectDraw. Basically it has:

  1. Constructor, Destructor.
  2. Init() function that creates the IDirectDraw7 interface, a primary surface buffer, a back surface buffer, and a clipper for the primary surface buffer. It is self explanatory. I grabbed the code from DDUTIL.H and DDUTIL.CPP, which are from Microsoft, along with the rest of the DirectX SDK.
  3. Terminate() function that terminates all objects using the COM method release().
  4. Clear() function that clears the primary surface and the back surface buffer by blit color 0 (black) to these two surfaces.
  5. Display() function will blit the back surface buffer to the primary surface and thus the graphic will be displayed. The problem with Display is that you cannot blit using absolute coordinates. You have to know exactly where your view (which is the client window) is, and blit the graphics to this view area. So this is how I designed the function:
  6. void CDDrawSystem::Display()
    {
      HRESULT hRet;
    
      RECT rt;
      POINT p = {0, 0};
    
      ClientToScreen(hWnd, &p);
    // get the client area on
    // the desktop by using this line.
      rt.left = 0 + p.x;
      rt.top = 0 + p.y;
      rt.right = 800 + p.x;
      rt.bottom = 600 + p.y;
    
    // to ensure the drawing is complete, we use loops to continue
    // the drawing until the blitting returns DD_OK or it is not
    // DDERR_WASSTILLDRAWING
    
      while( 1 )
        {
        hRet = m_pddsFrontBuffer->Blt(&rt,
        m_pddsStoreBuffer,
        NULL,
        DDBLT_WAIT,
        NULL);
    
        if (hRet == DD_OK)
          break;
        else if(hRet == DDERR_SURFACELOST)
        {
          m_pddsFrontBuffer->Restore();
          m_pddsStoreBuffer->Restore();
        }
        else if(hRet != DDERR_WASSTILLDRAWING)
        return;
      }
    }
    
  7. In addition to these functions, you can add new functions such as load bitmap files, bit blit bitmaps from off-screen surfaces, and draw text, primitive geometric shapes, and lock surface and do crazy stuff.

I wrote this TestDraw() function just to show you how to do primitive drawings on back screen surfaces and blit them to the primary surface. It will display "This is a stinky App" at coordinate (20, 20). Then, if you click anywhere in the client area, it will draw a big white circular dot (about 50 pixels in radius).

Now, the Complete Code

// DDrawSystem.h: interface for the CDDrawSystem class.
//
////////////////////////////////////////////////////////////////

#if !defined(AFX_DDRAWSYSTEM_H__1E152EB4_ED1D_4079_BDD4_773383DD98C8
    __INCLUDED_)
#define AFX_DDRAWSYSTEM_H__1E152EB4_ED1D_4079_BDD4_773383DD98C8
    __INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <ddraw.h>

#define _CHARACTORBUILDER_
#include "../GameLib/Image.h"

class CDDrawSystem
{
public:
  CDDrawSystem();
  virtual ~CDDrawSystem();

  BOOL Init(HWND hWnd);
  void Terminate();
  void Clear();
  void TestDraw(int x, int y);
  void Display();

protected:
  LPDIRECTDRAW7 m_pDD;
  LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer;
  LPDIRECTDRAWSURFACE7 m_pddsStoreBuffer;
  LPDIRECTDRAWCLIPPER pcClipper;

  HWND hWnd;
};

#endif

//!defined(AFX_DDRAWSYSTEM_H__1E152EB4_ED1D_4079_BDD4_
773383DD98C8__INCLUDED_)
// DDrawSystem.cpp: implementation of the CDDrawSystem class.
//
////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DDrawSystem.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


////////////////////////////////////////////////////////////////
// Construction/Destruction
////////////////////////////////////////////////////////////////

CDDrawSystem::CDDrawSystem()
{
  m_pDD = NULL;
  m_pddsFrontBuffer = NULL;
  m_pddsStoreBuffer = NULL;
  pcClipper = NULL;
}

CDDrawSystem::~CDDrawSystem()
{
  Terminate();
}

// old DirectDraw Initialization stuff.
// Set a window mode DirectDraw Display.
BOOL CDDrawSystem::Init(HWND hWnd)
{
  HRESULT hRet;

  this->hWnd = hWnd;

  hRet = DirectDrawCreateEx(NULL, (VOID**)&m_pDD,
    IID_IDirectDraw7, NULL);

  if(hRet != DD_OK)
  {
    AfxMessageBox("Failed to create directdraw object.");
    return FALSE;
  }

  hRet = m_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
  if(hRet != DD_OK)
  {
    AfxMessageBox("Failed to set directdraw display behavior.");
    return FALSE;
  }

  HRESULT hr;

  DDSURFACEDESC2 ddsd;
  ZeroMemory( &ddsd, sizeof( ddsd ) );
  ddsd.dwSize = sizeof( ddsd );
  ddsd.dwFlags = DDSD_CAPS;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

  if(FAILED(hr = m_pDD->CreateSurface(&ddsd,
    &m_pddsFrontBuffer, NULL)))
  {
    AfxMessageBox("Failed to create primary surface.");
    return FALSE; 
    }


  // Create the backbuffer surface
  ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; 
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | 
    DDSCAPS_3DDEVICE;
  ddsd.dwWidth = 800;
  ddsd.dwHeight = 600;

  if(FAILED(hr = m_pDD->CreateSurface(&ddsd, 
    &m_pddsStoreBuffer, NULL)))
  {
    AfxMessageBox("Failed to create back buffer surface.");
    return FALSE; 
  }

  if(FAILED(hr = m_pDD->CreateClipper(0, &pcClipper, NULL)))
  {
    AfxMessageBox("Failed to create clipper.");
    return FALSE; 
  }

  if(FAILED(hr = pcClipper->SetHWnd(0, hWnd)))
  {
    pcClipper->Release();
    AfxMessageBox("Failed to create primary surface.");
    return FALSE; 
  }
if(FAILED(hr = m_pddsFrontBuffer->SetClipper(pcClipper))) { pcClipper->Release(); AfxMessageBox("Failed to create primary surface."); return FALSE; } return TRUE; } // make sure all things are terminated and set to NULL // when application ends. void CDDrawSystem::Terminate() { if (m_pDD != NULL) { if (m_pddsFrontBuffer != NULL) { if (m_pddsStoreBuffer != NULL) { m_pddsStoreBuffer->Release(); m_pddsStoreBuffer = NULL; } if (pcClipper != NULL) { pcClipper->Release(); pcClipper = NULL; } m_pddsFrontBuffer->Release(); m_pddsFrontBuffer = NULL; } m_pDD->Release(); m_pDD = NULL; } } // clear both off csreen buffer and primary buffer. void CDDrawSystem::Clear() { HRESULT hRet; DDBLTFX fx; fx.dwSize = sizeof(fx); fx.dwFillColor = 0x000000; while (1) { hRet = m_pddsFrontBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &fx); if (hRet == DD_OK) break; else if (hRet == DDERR_SURFACELOST) { m_pddsFrontBuffer->Restore(); } else if (hRet != DDERR_WASSTILLDRAWING) break; } while (1) { hRet = m_pddsStoreBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &fx); if (hRet == DD_OK) break; else if (hRet == DDERR_SURFACELOST) { m_pddsStoreBuffer->Restore(); } else if (hRet != DDERR_WASSTILLDRAWING) break; } } // a test: // The conclusion is: Under no circumstance, draw directly to // primary Surface! // It doesn't work that way. // ... // ... // This is just a simple test function. It has shit use in this // project. void CDDrawSystem::TestDraw(int x, int y) { HRESULT hRet; HDC dc; hRet = m_pddsStoreBuffer->GetDC(&dc); if (hRet != DD_OK) return; POINT p = {0 + x, 0 + y}; ClientToScreen(hWnd, &p); SetTextColor(dc, RGB(255, 0, 0)); TextOut(dc, 20, 20, "This is a stinky App", lstrlen("This is a stinky App")); Ellipse(dc, x-50, y-50, x+50,y+50); m_pddsStoreBuffer->ReleaseDC(dc); } // Load images from offscteen buffer to primary buffer // and for display. void CDDrawSystem::Display() { HRESULT hRet; RECT rt; POINT p = {0, 0}; ClientToScreen(hWnd, &p); rt.left = 0 + p.x; rt.top = 0 + p.y; rt.right = 800 + p.x; rt.bottom = 600 + p.y; while(1) { hRet = m_pddsFrontBuffer->Blt(&rt, m_pddsStoreBuffer, NULL, DDBLT_WAIT, NULL); if (hRet == DD_OK) break; else if(hRet == DDERR_SURFACELOST) { m_pddsFrontBuffer->Restore(); m_pddsStoreBuffer->Restore(); } else if(hRet != DDERR_WASSTILLDRAWING) return; } }

Downloads

Download source - 39 Kb



Comments

  • Learn Game Programming using DirectX9

    Posted by lp_ on 02/01/2007 03:02pm

    Learn Game Programming using DirectX9 visit: http://lightportal.co.nr

    Reply
  • stuck teen

    Posted by rapheal on 05/06/2005 02:32pm

    how can i make a game wit direct x sdk, and win 2005 visual basic c/c++

    Reply
  • Please help me on Directshow and MFC

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

    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
  • Where can I find "/GameLib/Image.h"

    Posted by svahora on 10/18/2004 08:13am

    I am getting compilation error because of above files. fatal error C1083: Cannot open include file: '../GameLib/Image.h': No such file or directory

    • Don't bother

      Posted by petruza on 01/31/2006 01:54pm

      The file isn't needed at all, just delete the line: #include "../GameLib/Image.h"

      Reply
    • Thanks to JRS

      Posted by suyu on 01/12/2006 10:58pm

      I encountered the same problem,but when I followed your suggestion,my computer compiled succeddfully.Thanks a lot!

      Reply
    • Where can I find "/GameLib/Image.h"

      Posted by JRS on 11/09/2004 02:50pm

      I had the same problem. Luckily I found the file using explorer. I copied into the project file, added it to the header files in 'file view' and it worked ok. If you cannot find a copy on your machine, the contents are lested below
      
      
      #ifndef __IMAGE_H__
      #define __IMAGE_H__
      
      typedef struct
      {
          unsigned short imagic;
          unsigned short type;
          unsigned short dim;
          unsigned short sizeX, sizeY, sizeZ;
          char name[128];
      	unsigned char *data;
      } IMAGE;
      
      IMAGE *ImageLoad(char *);
      
      #endif /* !__IMAGE_H__! */

      Reply
    Reply
  • The program does not work if the user changes the resolution

    Posted by chris109 on 04/02/2004 08:29am

    If the user changes the display mode -- for example from 1024x768 pixels to 800x600 pixels -- the application cannot work anymore. But I think I have found the right way to get round this.
    When the user changes the resolution, all the windows receive the WM_DISPLAYCHANGE message.
    
    So I added this declaration in file ddraw_in_mfcwindView.h, class CDdraw_in_mfcwindView:
    
    afx_msg LRESULT OnDisplayChange(WPARAM wParam, LPARAM lParam);
    
    I updated the message map in file ddraw_in_mfcwindView.cpp like this:
    
    BEGIN_MESSAGE_MAP(CDdraw_in_mfcwindView, CView)
    	//{{AFX_MSG_MAP(CDdraw_in_mfcwindView)
    	ON_WM_LBUTTONDOWN()
    	//}}AFX_MSG_MAP
    	ON_MESSAGE(WM_DISPLAYCHANGE, OnDisplayChange)
    END_MESSAGE_MAP()
    
    And I added the code for OnDisplayChange method in file ddraw_in_mfcwindView.cpp like this:
    
    LRESULT CDdraw_in_mfcwindView::OnDisplayChange(WPARAM wParam, LPARAM lParam)
    {
    	LRESULT _lResult;
    	
    	_lResult = CWnd::OnDisplayChange(wParam, lParam);
    
    	ddobj.Terminate();
    	bDDrawActive = FALSE;
    
    	if (!ddobj.Init(GetSafeHwnd()))
    	{
    		AfxMessageBox("Failed to Create DirectDraw Interface.");
    	}
    	else
    	{
    		bDDrawActive = TRUE;
    	}
    
    	return _lResult;
    }
    
    It seems to work pretty well.

    Reply
  • How can I avoid the call to OnDraw procedure of the view class?

    Posted by Legacy on 12/01/2003 12:00am

    Originally posted by: Deutsch

    I've tried to create a simple animation on the basis of this
    tutorial. The problem is, that the function OnDraw in the view class must be called to renew the screen, and because of this the screen is blinking.

    • How can I avoid the call to OnDraw procedure of the view class?

      Posted by JRS on 11/09/2004 03:11pm

      Avoid OnDraw by calling: ddobj.Clear(); ddobj.TestDraw(x, y); ddobj.Display(); from somewhere else (e.g. OnTimer()). Do not invalidate the view This will not stop the flicker. You must take out the blt to the m_pddsFrontBuffer in the CDDrawSystem::Clear() function, then it works fine

      Reply
    Reply
  • Code will not work in some cases

    Posted by Legacy on 09/02/2003 12:00am

    Originally posted by: Ren�

    Nice code, however I can assure you it will not work in an
    activex application. I made an activex and in Ondraw I implemented the blitting of a bmp-file. Code worked perfectely in windows 98 and directx > 7.0. It also works in win2000 directx7 but not in directx8 and 9. So again an example of lousy microsoft testing. The problem in the latter case is that it seems that the blitting will not compensate for the coordinates gotten by clienttoscreen. The drawing can be anywhere in the screen, depending on the location of the picturebox of which the activex is a child.

    Reply
  • where can I find ddutil.h and resource.h

    Posted by Legacy on 05/31/2003 12:00am

    Originally posted by: anita

    hai
    can you tell me where can I foind ddutil.h and resource.h
    thank you

    Reply
  • Works also for dialog-based apps

    Posted by Legacy on 05/11/2003 12:00am

    Originally posted by: Zygoman

    Very good stuff !!!! I used it for a dialog-based application, and there was no problem at all !!!!! Thanks a lot, save a lot amount of time :-)

    Reply
  • Success!!

    Posted by Legacy on 10/19/2002 12:00am

    Originally posted by: Tyrone Deane

    Thanks for the tip about how to include the DirectX lib and include directories. My code now complies. Yippee!!!!

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • The exponential growth of data, along with virtualization, is bringing a disruptive level of complexity to your IT infrastructure. Having multiple point solutions for data protection is not the answer, as it adds to the chaos and impedes on your ability to deliver consistent SLAs. Read this white paper to learn how a more holistic view of the infrastructure can help you to unify the data protection schemas by properly evaluating your business needs in order to gain a thorough understanding of the applications …

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds