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).
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.
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));
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
void CDirect2DGeometrySampleView::OnDraw(CDC* )
{
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).