Most of the native Windows Programmers are familiar with GDI,
the graphics API provided under Windows. GDI is widely used
in windows applications. Later, Microsoft had introduced
class-based, better services through GDI+ under Windows.
Both Native programmers and Managed developers are making
use of GDI/GDI+ classes and APIs.
But GDI was too old and was introduced with the initial
versions of the Windows and over the time the capabilities
of graphics hardware has grown and so does the demand for
creating Visually Rich UI. With Windows 7, Microsoft has
introduced a totally revamped 2D
graphics subsystem called Direct2D based
on their prominent Direct3D platform to create high quality
2D rendering. This is
supposed to replace the GDI APIs in the coming years.
Direct2D is not only
available under Windows 7 but Microsoft has made it
available in Window Vista with the latest service pack
upgrade. If you know or not, using
DirectX platform, Windows
Aero system is already taking advantage of graphics
hardware since Windows Vista.
The major advantages of the
Direct2D APIs are it’s hardware accelerated and
provides high quality 2D rendering.
It enables the user to create visually rich applications by
paying less effort, when comparing to conventional APIs .
GDI uses Pixel graphics but
Direct2D can supports vector graphics as well, in
which mathematical formulas are used to draw the lines and
curves. Vector graphics
provides high quality rendering independent of resolution of
the device, while the pixelated graphics has dependency with
resolution which may results in choppy graphics.
Most of the GDI APIs are not using anti-aliasing and
transparency. Of crouse there are functions to do so
but always there’s programming cost for taking advantage of
these features. Also if we apply transparency and
anti-aliasing, the computations are done using CPU.
Direct2D can take
advantage of graphics hardware and delegate the
computationally intensive tucancode.nets to GPU.
Direct2D built on top of
Direct3D components. The layered architecture is described
below
You can see the underlying layers are Direct3D which make
use of DXGI(DirectX Graphics Infrastructure), which manages
the low level graphics
related tucancode.nets that are independent of DirectX graphics
runtime. DXGI provides common framework for graphics
components. Simultaneously a high performance software
rasterizer is available when the hardware acceleration is
not possible. Another advantage of
Direct2D API is, it’s using lightweight COM. It’s
almost like simple C++ class. No BSTRS, COM Variants,
Interfaces, Apartments etc.
Let’s start analyzing a simple
Direct2D Program
There are 3 main components inevitable for enabling D2D
rendering your
application.
The below example provides steps for enabling
Direct2D
Rendering in your MFC
Application. It’s hard to describe the parameters of all
APIs. Please refer them in MSDN.
Create your Window
Here I’m taking a dialog based application for rendering.
The functions and messages may change if you’re using any
other type of applications like SDI or MDI. No need to
reinvent the wheel, allow the wizard to create your main
dialog.
Prepare your Rendering
Engine
Let’s delegate all the rendering tucancode.nets to the other window
instead of managing everything in your dialog class. I call
this class, Direct2DHandler.
The Direct2DHandler declaration goes like below and it
exposes the following functions.
Collapse | Copy
Code
#pragma once
#include <d2d1.h>
#include <d2d1helper.h>
class Direct2DHandler
{
public:
Direct2DHandler( HWND hWnd ); ~Direct2DHandler(void); HRESULT Initialize(); HRESULT OnRender(); void OnResize(UINT width, UINT height);
private:
HRESULT CreateDeviceResources(); void DiscardDeviceResources();
private:
HWND m_hWnd;
ID2D1Factory* m_pDirect2dFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pLightSlateGrayBrush;
ID2D1LinearGradientBrush* m_pLinearGradientBrush;
};
The constructor
does nothing other than calling CoInitialize() function and
initialize the member variables( mostly to NULL).
The Initialize function
creates the Direct2D
factor object, which similar the HDC (context variable) in
GDI.
Collapse | Copy
Code
HRESULT Direct2DHandler::Initialize()
{
HRESULT hr = S_OK;
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
return hr;
}
OnResize
function handles the rendering area for the render target.
We will be using whole window area in this example.
Collapse | Copy
Code
void Direct2DHandler::OnResize(UINT width, UINT height)
{
if (m_pRenderTarget)
{
m_pRenderTarget->Resize(D2D1::SizeU(width, height));
}
}
OnRender
function does the following
-
Create the device resource for drawing (if not created)
-
Render the scene to the Window
-
Release the resources if error occurred
If you’re experienced 3D programming probably you might be
knowing about the role of matrix and how the affects the
rendering object. The matrix can be used for representing
the objects coordinates system. We can apply rotation,
translation (movement) etc (collectively called
transformation). on a matrix and this can be used for
specifying how object must be rendered. Here we’re using
D2D1::Matrix3x2F::Identity() helper function which creates
an identity matrix(object will be rendered at origin without
any transformation)
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
We can clear the rendering area with a default color (as the
dialog background filled with some default color according
to Window’s theme. Here I selected white color
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
The important thing is, whatever you draw for the scene must
be done between BeginDraw() and EndDraw() APIs. The other
part of this code is simply straight forward and you can
easily grasp it. See the source below.
Collapse | Copy
Code
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::White));
D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
int width = static_cast<int>(rtSize.width);
int height = static_cast<int>(rtSize.height);
D2D1_RECT_F rectangle1 = D2D1::RectF(
rtSize.width/2 - 50.0f,
rtSize.height/2 - 50.0f,
rtSize.width/2 + 50.0f,
rtSize.height/2 + 50.0f
);
D2D1_RECT_F rectangle2 = D2D1::RectF(
rtSize.width/2 - 100.0f,
rtSize.height/2 - 100.0f,
rtSize.width/2 + 100.0f,
rtSize.height/2 + 100.0f
);
m_pRenderTarget->FillRectangle(&rectangle2, m_pLinearGradientBrush);
m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
hr = m_pRenderTarget->EndDraw();
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
CreateDeviceResources
is slightly a lengthy function to initialize the required
resources for rendering, like we create pens, brushes,
bitmaps etc. Here mainly I’m creating a light colored solid
brush and linear gradient brush to render as depicted in
above picture. The comments are inlined and please read it
along with the code. It will be interesting to learn about
gradient brushes. If you’ve created a gradient brush in
photoshop things will be far more easy. We can put stop
spots to controls the end of gradients also line can be used
for representing flow and angle for the gradient. Simple
isn’t it? Like we create a normal gradient. See the code
below.
Collapse | Copy
Code
HRESULT Direct2DHandler::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pRenderTarget)
{
RECT rc;
GetClientRect(m_hWnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hWnd, size),
&m_pRenderTarget
);
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::LightSlateGray),
&m_pLightSlateGrayBrush
);
}
ID2D1GradientStopCollection *pGradientStops = NULL;
D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Maroon, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::Red, 1);
gradientStops[1].position = 1.0f;
hr = m_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
);
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateLinearGradientBrush(
D2D1::LinearGradientBrushProperties(
D2D1::Point2F(0, 0),
D2D1::Point2F(300, 300)),
pGradientStops,
&m_pLinearGradientBrush
);
}
}
return hr;
}
DiscardDeviceResources
Just releases the allocated resouces. It simply uses helper
macro SafeRelease described below the code.
Collapse | Copy
Code
void Direct2DHandler::DiscardDeviceResources()
{
SafeRelease(&m_pRenderTarget);
SafeRelease(&m_pLightSlateGrayBrush);
SafeRelease(&m_pLinearGradientBrush);
}
template<class Interface>
inline void SafeRelease(
Interface **ppInterfaceToRelease
)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
Initializing Rendering in Dialog class
In the
OnInitDialog function,
allocate memory for the Direct2DHandler class and Initialize
it. You can call this from OnCreate function as well but
ensure that there’s a valid Window handle is there.
Collapse | Copy
Code
m_pRender = new Direct2DHandler( m_hWnd );
m_pRender->Initialize();
Disable Default Background Drawing
Disable the default OnErasBkgrnd function because it may
cause flickering while drawing. Also we’re occupying full
client area in the dialog and the background is clearing is
OnRender function itself.
Collapse | Copy
Code
BOOL CDirect2DDemoDlg::OnEraseBkgnd(CDC* pDC)
{
return FALSE;}
OnSize (WM_SIZE)
function handles the Window size events. It will resize
render target according to the Window Size.
Collapse | Copy
Code
void CDirect2DDemoDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
if( m_pRender )
m_pRender->OnResize( cx, cy );
}
OnPaint function
calls OnRender function of Direct2DHandler to draw the
scene.
Collapse | Copy
Code
void CDirect2DDemoDlg::OnPaint()
{
if (IsIconic())
{
}
else
{
if( m_pRender )
m_pRender->OnRender();
}
}
Linking
The Application must link to d2d1.lib through project
settings or using #pragma comment( lib, “d2d1.lib” ) in the
source file
d2d1helper
d2d1helper.h contains some useful functions to help the
drawing. See the header for more details.
OK that’s the basics of creating
Direct2D rendering
for your Windows Application. Go ahead with the attached
source code.
The project file is in Visual Studio 2010 format, but
basically you only need to have Direct2DHandler class. You
can also use other prior version of Visual Studio with
Windows 7/Vista SDK.
News:
1 UCanCode Advance E-XD++
CAD Drawing and Printing Solution
Source Code Solution for C/C++, .NET V2023 is released!
2
UCanCode Advance E-XD++
HMI & SCADA Source Code Solution for C/C++, .NET V2023 is
released!
3
UCanCode Advance E-XD++ GIS SVG Drawing and Printing Solution
Source Code Solution for C/C++, .NET V2023 is released!
Contact UCanCode Software
To buy the source code or learn more about with:
|