Click here to Skip to main content
Click here to Skip to main content

Programming Direct2D – Part 2 – Basic Geometry

, 24 Sep 2010
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sarath C
Technical Lead
India India
Software Developer
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberThornik1-May-13 13:23 
GeneralMy vote of 2 PinmemberS.H.Bouwhuis30-Sep-10 0:58 
GeneralRe: My vote of 2 PinmemberSarath.9-Oct-10 16:15 
GeneralRe: My vote of 4 PinmemberS.H.Bouwhuis10-Oct-10 20:53 
GeneralRe: My vote of 4 PinmemberSarath.11-Oct-10 20:05 
GeneralRe: My vote of 4 PinmemberS.H.Bouwhuis11-Oct-10 20:58 
GeneralRe: My vote of 4 PinmemberSarath.11-Oct-10 21:11 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 25 Sep 2010
Article Copyright 2010 by Sarath C
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid