Archive for the ‘Direct2D’ Category

Resources for Direct2D

Freitag, Mai 15th, 2009

Currently documentation on Direct2D seems to be a little bit sparse. Only a few samples and not much community content exist. Here I concentrate the sources I ran across ‚til now.

Just like the code the docs also experienced a way of change during the last incarnations. In the Windows 7 Beta SDK there was compiled Help content that disappeared with the March DirectX SDK and was replaced by the online MSDN content. With the Windows 7 RC SDK offline content has reappeared, in the form of Document Explorer Help. I prefer the last-mentioned because it’s quicker as MSDN online and does not collapse sibling branches if you change to another branch.

* MSDN Online Documentation

* PDC 2008 Talk

* Samples: The sample code contained in the SDK installed folders (C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Multimedia\Direct2D) differs from the code in the Document Explorer Help file. Be sure to check both.

* Blogs

DirectX Blog – new, not much content yet

Tom’s Blog – especially the (ongoing) discussion in Introducing the Microsoft Direct2D API is worth noting.

Kam VedBrat’s Blog

* Forum: There is no dedicated MSDN forum yet. Some valuable help was given in the XNA forum, Direct3D subforum.

* Hands-On-Labs from the Windows 7 Developer Trainings: Sorry, can’t post. But reminds of the MSDN samples and the code in the PDC talk.

* Codeproject article: Direct2D: Hardware Acceleration in Windows 7 Plotting

* Some further links in the Wikipedia article

How to grab coordinates from a Direct2D Geometry or Mesh

Freitag, Mai 8th, 2009

Direct2D has a bag full of functions for manipulation of geometries. You can intersect polygons or unite them, test for the relation between two geometries and much more.

Sometimes you may need to get the resulting coordinates from such an operation. Or you want to use D2D as a geometric object manipulation library. This is not as easy as it should be.

From the D2D related blogs I got the hint: ‚You have to implement the sink interface and grab the data from there.‘. As there is not much documentation yet and only a handful of samples I didn’t find code so I tried to implement it myself.

But some background first.

The base interface for geometries in Direct2D is ID2D1Geometry. It has methods to operate on geometries like CombineWithGeometry, ComputeArea or Widen. These methods write the result of the operation to an ID2D1SimplifiedGeometrySink you have to provide. The geometry operation calls the sink’s methods BeginFigure, AddLines, EndFigure, Close and so on.

Normally the sink is used to create contents for a new ID2D1PathGeometry, a geometry derivate that can consist of figures and segments, just like the path objects in GDI, GDI+ or WPF. The path geometry’s Open method creates a new sink and returns it’s interface pointer so the sink can be used to fill the path. Now you can call the sink’s methods or pass the sink through to the geometry operations as mentioned above.

If you imitate an ID2D1SimplifiedGeometrySink you can give its pointer to the geometry operations and AddLines for example may be called by the geometry. As AddLines is your code, you can do what you like with the coordinates. So, let’s implement a sink. I needed a ID2D1TessellationSink because I wanted to grab the triangles created by the ID2D1Geometry Tesselate method. First comes the definition of the class derived from the ID2D1TessellationSink interface in MySink.h:

#pragma once

class MySink : public ID2D1TessellationSink
{
public:
  MySink(ID2D1TessellationSink *pSink);
  ~MySink(void);
   STDMETHOD_(void, AddTriangles)(
       __in_ecount(trianglesCount) CONST D2D1_TRIANGLE *triangles, UINT trianglesCount
       );
   STDMETHOD(Close)();
public:
   unsigned long STDMETHODCALLTYPE AddRef();
   unsigned long STDMETHODCALLTYPE Release();
   HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject);
private:
   LONG volatile m_cRefCount;
   ID2D1TessellationSinkPtr m_spSink;
};

And now the implementation in MySink.cpp:
#include "StdAfx.h"

#include "MySink.h"MySink::MySink(ID2D1TessellationSink *pSink) : m_cRefCount(0)
{
m_spSink = pSink;
m_spSink->AddRef();
}
MySink::~MySink(void)
{
m_spSink->Release();
}
STDMETHODIMP_(void) MySink::AddTriangles(__in_ecount(trianglesCount) CONST D2D1_TRIANGLE *triangles, UINT trianglesCount)
{
// here you can copy the triangles data...
m_spSink->AddTriangles(triangles, trianglesCount);
}
STDMETHODIMP MySink::Close()
{
return m_spSink->Close();
}
STDMETHODIMP_(unsigned long) MySink::AddRef()
{
return InterlockedIncrement(&m_cRefCount);
}

STDMETHODIMP_(unsigned long) MySink::Release()
{
if (InterlockedDecrement(&m_cRefCount) == 0)
  {
  delete this;
  return 0;
  }
return m_cRefCount;
}

STDMETHODIMP MySink::QueryInterface(IID const& riid, void** ppvObject)
{
if (__uuidof(ID2D1TessellationSink) == riid)
  *ppvObject = dynamic_cast(this);
else if (__uuidof(IUnknown) == riid)
  *ppvObject = dynamic_cast(this);
else
  {
  *ppvObject = NULL;
  return E_FAIL;
  }
return S_OK;
}

My class takes an ID2D1TesselationSink pointer in the ctor that is used to pipe through the data to create a mesh (see the ID2D1Mesh interface). You can port the idea to a simplified or a normal geometry sink.

Using Direct2D conditionally

Donnerstag, Mai 7th, 2009

In the Windows 7 Developer training I learned that we should not test the OS version in order to check which new features can be used. We now shall test each feature individually (I think today it’s so confusing that you can’t determine which OS service pack implements which extension).

When I asked to show some examples how the test for individual features can be done I got no answer.

Today I had the problem to implement a C++ DLL that should use Direct2D if it was available. My first idea was to just use delay-load for my D2D1.DLL import. But this would not work under XP because the late load of d2d1.dll would fail.

So I decided to dynamically load Direct2D. The architecture of Direct2D comes to my aid because D2D uses the factory pattern to construct all objects. That leads to the consequence that the D2D1.DLL has few exports. Ideally it only had the D2D1CreateFactory export so it would be very easy to get it’s proc address and forget about it. Direct2D adds some helper functions that ease the handling of frequently-used structures (like D2D1InvertMatrix). In my case I was lucky because I didn’t need them so I just needed to call GetProcAddress once for D2D1CreateFactory.

I declared a global smart pointer for the factory and a HMODULE variable for the D2D1.DLL handle and then I inserted the following code into my DllMain in the DLL_PROCESS_ATTACH branch. It was a bit tricky to find the correct function prototype for D2D1CreateFactory because the docs state four different variants and the sample code uses another one.

if (NULL != (hD2D1Lib = AfxLoadLibrary("D2D1.DLL")))
  {
  typedef HRESULT(STDAPICALLTYPE* FPCF)(D2D1_FACTORY_TYPE, REFIID, CONST D2D1_FACTORY_OPTIONS *, void**);
   FPCF pfn = (FPCF) GetProcAddress(hD2D1Lib, "D2D1CreateFactory");
   if (pfn == NULL || FAILED(pfn(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), NULL, (void**)&g_spFactory)))
  {
      g_spFactory = NULL;
      AfxFreeLibrary(hD2D1Lib);
      hD2D1Lib = NULL;
      }
   }

What’s left is not to forget to call AfxFreeLibrary again in the DLL_PROCESS_DETACH branch.

In my implementation I can now simply test the g_spFactory pointer to decide if I can use Direct2D features.