Click here to Skip to main content
15,794,475 members
Articles / Multimedia / DirectX

DirectDraw extension

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
27 Nov 19996 min read 162.5K   3K   29   17
A DirectDraw Framework made of template classes
  • Download demo executable - 9 Kb
  • Download source files - 32 Kb
  • This article provides the following content :

    • A DirectDraw Framework made of template classes
    • A versatile blit routine which encompasses all pixel formats available
    • As a gift, a full-speed fullscreen video player under DirectShow, using this framework

    1. DirectDraw framework through Template classes

    It is known that all DirectDraw objects and surfaces are handled through the standard COM method call, ie QI, AddRef (hidden), and Release. The AddRef() call is mostly hidden because any time you query for an interface, the COM component will automatically call AddRef() for you. Thus, while it's also good to remember that the developer should use AddRef() any time a pointer is given by copy, it must be said that having to manage Release() is quite boring. The ATL framework comes with a solution to this, in name of CComPtr and CComQIPtr template classes. Those classes hide Release() calls for us to concentrate on important stuff, rather on that AddRef()/Release() count issue. Pretty good.

    Because it is important in this article, what follows is the source code from the ATL library (© Microsoft) for the CComPtr class (#include "atlbase.h"):

    template <class T>
    class CComPtr
    	typedef T _PtrClass;
    	CComPtr() {p=NULL;}
    	CComPtr(T* lp)
    		if ((p = lp) != NULL)
    	CComPtr(const CComPtr<T>& lp)
    		if ((p = lp.p) != NULL)
    	~CComPtr() {if (p) p->Release();}
    	void Release() {if (p) p->Release(); p=NULL;}
    	operator T*() {return (T*)p;}
    	T& operator*() {_ASSERTE(p!=NULL); return *p; }
    	//The assert on operator& usually indicates a bug.  If this is really
    	//what is needed, however, take the address of the p member explicitly.
    	T** operator&() { _ASSERTE(p==NULL); return &p; }
    	T* operator->() { _ASSERTE(p!=NULL); return p; }
    	T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
    	T* operator=(const CComPtr<T>& lp)
    		return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
    #if _MSC_VER>1020
    	bool operator!(){return (p == NULL);}
    	BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
    	// pointer member
    	T* p;

    The article is entitled DirectDraw extensions, so why the hell do I talk about ATL? In fact, the main thing that came to my mind is that I wanted to overcome inherant DirectDraw BLIT limitations. See next section. And the most straight forward solution to add functionalities to an interface that clearly can't be replaced, is to wrap all functionalities in another class. And in this class, calls to the Blit() function will be either delegated to the standard IDirectDrawSurface->Blt() functionality, or will go through our own blit processing.

    Why templates? Though the article focuses only on that IDirectDrawSurface interface, because Blit() is made through it only (DirectDraw doesn't support aggregation), in fact the concept of wrapper classes can be generalized to all DirectDraw interfaces : IDirectDraw, IDirectDrawSurface, IDirectDrawClipper, IDirectDrawPalette, not to talk about IDirectDraw2, IDirectDraw3, IDirectDraw4, and so on......In fact, such set of template classes is what I like to call a framework and can be utterly substituted to a standard DirectDraw application written in C++. Not to say that this object also applies to ANY COM interface.

    The name of the game is not only to use wrapper template classes as well or instead of standard DirectDraw COM manipulation. The name of the game is to provide a framework which is thought so that those classes integrate seamlessly into "old-style" DirectDraw applications. That's where it comes to be interesting.

    Now let me give you my source code for the root template class, and the CDDSurface class.

    #include "atlbase.h"
    template <class T>
    class CDDObject
    public :
    	// --- member
    	CComPtr<T> m_pObject;
    	// --- methods
    	// operator &
    	T **operator&() { return &m_pObject; }
    	// operator T* : cast from CComPtr::T* to T*
    	operator T*() { return m_pObject; }
    	// operator ! : check whether the object is initialized
    	BOOL operator!() { return ((T*)m_pObject==NULL)  ? TRUE : FALSE; }
    	// force destroy
    	HRESULT Release() {	m_pObject.Release();
    				return S_OK; }

    The couple of operators I have implemented provide in fact easy access to the hidden pointer, and facilitates seamless integration to standard DirectDraw applications. The operators are used for example any time you query an interface (& operator), or if you want to downcast, or again if you want to check your pointer (! operator). At the end of this article, I give a full source code using this class.

    #ifndef _CDDSURFACE
    #define _CDDSURFACE
    #include <ddraw.h>
    #include "CDDObject.h"
    class CDDSurface : public CDDObject<IDirectDrawSurface>
    public :
    	// the main method
    	HRESULT Blt(RECT *destRect, IDirectDrawSurface *pSourceSurface, RECT *sourceRect, 
    		DWORD dwFlags, LPDDBLTFX lpDDBltFx);

    As you can see, the CDDSurface inherits the "core" CDDObject template class of type T=IDirectDrawSurface. This class provides implementation to our blit routine.

    Note that only the Blit is delegated, all other standard IDirectDrawSurface functionalities are accessed by an explicit cast such like :

    CDDSurface my_surface;
    HDC my_dc;

    2. A versatile blit routine

    In this section we are going to implement the blit routine.

    Now what's the matter with that? Let's say to begin that the GDI performs blits through two WIN32 methods, namely ::BitBlit() and ::StretchBlit(). The StretchBlit method is the most general method since it can scale and blit at the same time. But it's slow. When you know by far that you just want to perform a simple copy blit, then BitBlit is faster. Moreover, whether your buffer is a bitmap or a DIB Section, there are a couple of other methods.

    In the context of DirectDraw, there's a single BLT method which acts like the ::StretchBlit() method. DirectDraw is much more powerful than the GDI in that HAL/HEL abstraction can take advantage of your hardware.

    But, because there is a true drawback, DirectDraw doesn't know how to blit between DirectDraw surfaces not having the same pixel format. Say between a 16-bit surface and a 24-bit surface. Very often, you load a 256 colors bitmap, and then you wish to make it a true DirectDraw surface to be blitted to the primary surface. Since the primary surface has the depth of the desktop, that is 24 or 32 bits mostly, this doesn't work that easy !!

    Now for the solution. The GDI not only blits, it also performs color mapping as needed. Thus the idea, remaining trivial as you can see, is to delegate the blit to the DirectDraw HAL/HEL blit engine any time the surfaces are of the same color depth, and delegate the blit to a simple ::StretchBlit from the GDI any time the surfaces are of different color depths.

    Enough words, now for the code :

    #include "CDDSurface.h"
    // Blt extension method : performs a color stretch blit
    // The blit operation copies the content of a direct draw surface
    // to another direct draw surface, without care of the size and color format
    // of the source and destination surfaces. Hence this blit encompasses
    // both approaches.
    HRESULT CDDSurface::Blt(RECT *destRect, IDirectDrawSurface *pSourceSurface, RECT *sourceRect, 
    			DWORD dwFlags, LPDDBLTFX lpDDBltFx)
       // blit void surfaces : just kidding or....
       if (pSourceSurface==NULL || !m_pObject || !destRect || !sourceRect)
    	return E_FAIL;
       if (destRect->bottom-destRect->top==0 || destRect->right-destRect->left==0 ||
    	sourceRect->bottom-sourceRect->top==0 || sourceRect->right-sourceRect->left==0)
          return S_OK;
       IDirectDrawSurface *pDestSurface=(IDirectDrawSurface*)m_pObject;
       DDSURFACEDESC ddsdDest,ddsdSource;
       ::ZeroMemory(&ddsdDest, sizeof(ddsdDest));
       ddsdDest.dwSize = sizeof(ddsdDest);    
       ::ZeroMemory(&ddsdSource, sizeof(ddsdSource));
       ddsdSource.dwSize = sizeof(ddsdSource);    
       HRESULT hr = pDestSurface->GetSurfaceDesc(&ddsdDest);
       if (SUCCEEDED(hr))
          hr = pSourceSurface->GetSurfaceDesc(&ddsdSource);
          if (SUCCEEDED(hr))
             // check the two pixel formats (surface type & bit count)
             BOOL bSamePixelFormat=
    		(ddsdDest.ddpfPixelFormat.dwFlags==ddsdSource.ddpfPixelFormat.dwFlags) &&
             if (!bSamePixelFormat)
    	    // perform the color blit using the GDI (implicitely will remap colors)
    	    HDC dcDest,dcSource;
    	    int nOldStretchMode = ::SetStretchBltMode( dcDest, COLORONCOLOR);
    	    ::StretchBlt(dcDest, 0,0, 
    	       destRect->right-destRect->left,  destRect->bottom-destRect->top,
    	       dcSource, 0,0, 
    	       sourceRect->right-sourceRect->left, sourceRect->bottom-sourceRect->top,
    	    ::SetStretchBltMode( dcDest, nOldStretchMode);
    	    return S_OK;
    	   // common blit allowed between clonable surfaces
    	   return m_pObject->Blt(destRect, pSourceSurface, sourceRect, dwFlags, lpDDBltFx);
    	return hr;
       else return hr;

    Pheeww !!! Quite a hack indeed...On one hand, because the routine does perform stretchs, it's independent of the size of what you blit, and especially whether the source and destination buffers have the same size. On the other hand, because the routine also checks color depths and use explicitely color mapping from the GDI any time it is required, we can say that the routine is independent of the color. All in all, this blit routine is independent of size and color. Quite useful !! I believe this can help some people. Probably a lot of coders have already made such wrapper classes.

    Now what about alpha blend? First of all, I want to point out that this means quite a lot of things to me, and not a precise thing. Adding commonly known alpha blend support to this blit routine would make it almost the most general ever possible, hence the most useful. It's a good exercise for you to try to do it. Let me remember you that DirectX run-times up to 6.1 don't allow alpha blits, thus most things will have to be carried by hand. Poor us !!! I hope DirectX 7.0 comes with better features.

    3. A fullscreen video player using this framework

    An article starts helping people only when a full source code and executable is provided. It seems that the article is itself just a wrapper, very often not even read. Poor me.

    But because I didn't want to miss the opportunity to show a useful piece of code, I give you here a fullscreen video player under DirectShow.

    I hope you make good use of it.

    The framework is used for the primary surface and the two backbuffers. Note that conceptually we don't need two backbuffers, one is enough. But it's better to prepare code for two backbuffers so it's easy in the future to add versatile in-between overlay effects and so.


    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    Written By
    United States United States
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

    GeneralDirectX 9.0 Pin
    Chang Sen22-Dec-03 22:03
    Chang Sen22-Dec-03 22:03 
    Generalmulticast support for directx Pin
    Tapan D27-Jan-03 9:16
    Tapan D27-Jan-03 9:16 
    GeneralDirectShow sample app - PROBLEM Pin
    john john mackey1-Oct-01 14:04
    john john mackey1-Oct-01 14:04 
    Generala compliling problem Pin
    27-Jul-01 18:10
    suss27-Jul-01 18:10 
    Generalcompiling problem Pin
    13-Nov-00 8:52
    suss13-Nov-00 8:52 
    GeneralRe: compiling problem Pin
    13-Nov-00 10:10
    suss13-Nov-00 10:10 
    GeneralStupid idea to talk about speed in here Pin
    Arvind2321-Aug-00 9:10
    Arvind2321-Aug-00 9:10 
    GeneralYour is incorrect Pin
    subzero6-Aug-00 22:46
    subzero6-Aug-00 22:46 
    GeneralRe: Your is incorrect Pin
    subzero6-Aug-00 22:48
    subzero6-Aug-00 22:48 
    GeneralMissing < and > for templates Pin
    Arvind235-Aug-00 9:26
    Arvind235-Aug-00 9:26 
    GeneralRe: Missing < and > for templates Pin
    DirectXWannabe20-Aug-00 0:30
    sussDirectXWannabe20-Aug-00 0:30 
    GeneralAlpha Channel will be screwed Pin
    rehanahmad5-Aug-00 5:35
    rehanahmad5-Aug-00 5:35 
    GeneralRe: Alpha Channel will be screwed Pin
    Arvind235-Aug-00 9:23
    Arvind235-Aug-00 9:23 
    GeneralMessage Closed Pin
    13-Jan-00 4:11
    Member 966985313-Jan-00 4:11 
    GeneralRe: DirectX was supposed to circumvent GDI Pin
    Arvind2322-Feb-00 9:10
    Arvind2322-Feb-00 9:10 
    GeneralRe: DirectX was supposed to circumvent GDI Pin
    riza7-May-00 14:19
    riza7-May-00 14:19 
    GeneralRe: DirectX was supposed to circumvent GDI Pin
    Sweex18-Jun-00 23:10
    Sweex18-Jun-00 23:10 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.