Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Programming Direct2D – Part 2 – Basic Geometry

0.00/5 (No votes)
24 Sep 2010 1  
In this installment, let’s cover basic primitives. The basic primitives are the mostly used shapes like lines, rectangle, rounded rectangle, circle etc.

Few weeks ago, we covered the basics of Direct2D API and usage. In this instalment, let’s cover basic primitives. The basic primitives are the mostly used shapes like lines, rectangle, rounded rectangle, circle, etc. Direct2D can support more complex primitives (like path geometries) but let’s get introduced to basic primitives.

ID2D1Geometry class act as the base (interface) class for all geometry classes in Direct2D. The base class provides a minimum set interfaces to handle the data management and drawing operations. The derived classes like ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry and ID2D1EllipseGeometry, etc. provide the concrete implementation for each primitive. This is an absolutely object oriented concept which helps us to manage the code flexible and easy. The usage of interface classes is easy. To create the absolute parameters, several typesafe helper functions are available for creating rectangle, point values, etc. (same as RECT and POINT structure in Windows).

image

Here I demonstrate a sample program which can draw Rectangle, Rounded Rectangle and Ellipse n number of times in a multi document application.
The basic architecture remains the same.

  • The view class uses the Direct2DHandler class for Drawing using Direct2D.
  • The user interaction is handled in the view class and corresponding interfaces are called on user action or on window operations (like paint resize, etc.).
  • Since this is an MDI application, each view/tab contains the object of Direct2DHandler object.

image

Direct2DHandler Class

To support any number of primitives, the class contains a vector of IDirect2DGeometry pointers. Since this is an abstract class pointer, it can hold any primitive classes derived from it. Upon calling the draw function, the scene is cleared and each primitive will draw again in a loop.

UI Operation

This is a very basic application which allows us to draw something in a conventional way. The user can mark rectangle area using mouse and this will be passed to the interface for creating each primitive. The interfaces provided in the Direct2DHandler class will calculate the parameters from the given rectangle and create the geometry. The geometry will be inserted in the vector given as a member class. This application doesn’t support to delete the drawn primitives.

Creating Rectangle Geometry

void Direct2DHandler::CreateRectangle( LPCRECT pRect, bool bFill )
{
    D2D1_RECT_F rectangle = D2D1::Rect
	( pRect->left, pRect->top, pRect->right, pRect->bottom );

    ID2D1RectangleGeometry* pRectangle;
    m_pDirect2dFactory->CreateRectangleGeometry( rectangle, &pRectangle );
    m_Geometries.push_back( pRectangle );
}

Creating Ellipse Geometry

void Direct2DHandler::CreateEllipse( LPCRECT pRectBoundingBox )
{
    int halfX = ( pRectBoundingBox->right - pRectBoundingBox->left ) /2;
    int halfY = ( pRectBoundingBox->bottom - pRectBoundingBox->top ) /2;
    D2D1_ELLIPSE ellipse = D2D1::Ellipse( D2D1::Point2
      ( pRectBoundingBox->left + halfX, pRectBoundingBox->top + halfY), halfX, halfY );

    ID2D1EllipseGeometry* pEllipse;
    m_pDirect2dFactory->CreateEllipseGeometry( ellipse, &pEllipse );
    m_Geometries.push_back( pEllipse );
}

Create Rounded Rectangle Geometry

void Direct2DHandler::CreateRoundedRectangle
	(LPCRECT pRect, int radiusx, int radiusY, bool bFill )
{
    D2D1_ROUNDED_RECT rectangle = D2D1::RoundedRect( D2D1::Rect
	(pRect->left, pRect->top, pRect->right, pRect->bottom), radiusx, radiusY);
    ID2D1RoundedRectangleGeometry* pRRectangle;
    m_pDirect2dFactory->CreateRoundedRectangleGeometry( rectangle, &pRRectangle );
    m_Geometries.push_back( pRRectangle );
}

Primitives Container

Primitive container is declared as member of class:

std::vector<ID2D1Geometry*> m_Geometries;

Drawing

HRESULT Direct2DHandler::OnRender()
{
    HRESULT hr = S_OK;

    hr = CreateDeviceResources();

    if (SUCCEEDED(hr))
    {
        m_pRenderTarget->BeginDraw();
        m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
        m_pRenderTarget->Clear( D2D1::ColorF(D2D1::ColorF::Black, 1.0f));
        // Iterate and draw all primitives
        for( std::vector<ID2D1Geometry*>::iterator it = m_Geometries.begin();
            it != m_Geometries.end(); ++it )
        {
            m_pRenderTarget->DrawGeometry( *it, m_pLightSlateGrayBrush);
        }

        hr = m_pRenderTarget->EndDraw();

    }
    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    return hr;
}

Cleanup

Release all the allocated primitives during exit (destructor).

Direct2DHandler::~Direct2DHandler(void)
{
    for( std::vector<ID2D1Geometry*>::iterator it = m_Geometries.begin();
            it != m_Geometries.end(); ++it )
    {
            (*it)->Release();

    }

    m_Geometries.clear();

    SafeRelease(&m_pDirect2dFactory);
    SafeRelease(&m_pRenderTarget);
    SafeRelease(&m_pLightSlateGrayBrush);
    CoUninitialize();
}

View Code

Initialize Direct2DHandler Object

int CDirect2DGeometrySampleView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	m_pRender = new Direct2DHandler( m_hWnd );
	m_pRender->Initialize();

	return 0;
}

Delegate the Painting Operation to Direct2DHandler Class

// CDirect2DGeometrySampleView drawing
void CDirect2DGeometrySampleView::OnDraw(CDC* /*pDC*/)
{
    if( m_pRender )
        m_pRender->OnRender();
}

Call Resize the Render Scene on Resizing the Window

void CDirect2DGeometrySampleView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);

    if( m_pRender && cx && cy )
        m_pRender->OnResize( cx, cy );
}

Handle the menu commands to select the shape. The enumerations are locally defined.

void CDirect2DGeometrySampleView::OnShapeRrect()
{
	m_eShape = SHAPE_RRECT;
}

void CDirect2DGeometrySampleView::OnShapeRectangle()
{
	m_eShape = SHAPE_RECT;
}

void CDirect2DGeometrySampleView::OnShapeEllipse()
{
	m_eShape = SHAPE_ELLIPSE;
}

Call the Handler Interface to Create Geometry on Mouse Operation

void CDirect2DGeometrySampleView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_ptEnd = point;

	if( SHAPE_RECT == m_eShape )
		m_pRender->CreateRectangle( CRect( m_ptBeg, point ), true );
	else if( SHAPE_RRECT == m_eShape )
		m_pRender->CreateRoundedRectangle
			( CRect( m_ptBeg, point),10,10, true );
	else if( SHAPE_ELLIPSE == m_eShape )
		m_pRender->CreateEllipse( CRect( m_ptBeg, point ));
	else
		return;

	RedrawWindow();
}

The code is self explaining to understand the operations. However, please find the full project source code here (GitHub).

License

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