José, Petr,
When you will have moved to Seven, here are a few Direct2D demos that i have compiled with Visual Studio 2010 - ENU, in Win32 mode.
The GPU is being used instead of CPU, with latest graphic card generation when they are D2D compatible.
To better see the hardware antialias, play the demos full screen.
Note: The demos are part of the latest Windows Seven SDK.
...
Thanks Patrice,
I will try it in school, we have terrible intel cards there, so at least I will see if it runs on it.
I will upgrade to Windows 7, but with new PC ... which doesn't seem to happen very soon sadly.
My first step with Direct2D and PB...
--José
Do you think that would be the correct way to create the initial D2D1CreateFactory with PB using the C Flat API syntax?
I am using $IID_ID2D1RenderTarget because what i want to create here is a render target that draws to a Windows Graphics Device Interface (GDI) device context.
I am using BYVAL %NULL, because i don't care of the "debug layer DLL".
GLOBAL gpD2F AS IUnknown
LOCAL hr AS LONG
' // Create a Direct2D factory.
hr = D2D1CreateFactory(%D2D1_FACTORY_TYPE_SINGLE_THREADED, $IID_ID2D1Factory, BYVAL %NULL, gpD2F)
gpD2F = NOTHING
Added:
There was a typo in $IID constant being used.
...
Quote
(the PB syntax to use is so different than C++ encapsulation)
Because, like they did with GDI+, they have written some wrapper classes and only have documented them. C++ uses 4 overloaded functions on top of the D2D1CreateFactory function, and documents them but not the API function, that is the one that really does the work.
They have also messed other things, like a couple of equates that have different values in different files, an interface with duplicated method names and some classes for DirectX 10 implemented as C++ classes instead of COM classes.
When i wrote my GDIPLUS helper, back in 2002, i had an MSDN CD-rom that gave me the right syntax to use with the Flat API, however since that time i never found it again on MSDN dvd, only wrapper classes.
I was also able to translate ALL the GDI+ examples to PB and the source code was provided with the old WinLIFT Pro version. This is how Rainer was able to write a nice charting tool DLL with PB ;)
The demo was named "picture.exe" and must be still hanging somewhere around.
May be i shall post it back, after full completion of the new WSA.
...
Quote
In d2d1Helper.inc, don't you think that dxgiFormat parameter of the PixelFormat method should be passed BYVAL instead of BYREF?
Yes, I have changed it.
Quote
Note: It is a real pain to translate the Direct2D VS C++ examples to PB, especially when they use default parameters like in the code below:
They have the advantage of default parameters and overloaded methods. I already had a hard time translating the GDI+ examples. Guess I will have to write a reference guide showing the PB syntax, because the MSDN documentation is for the C++ wrapper classes.
Using my upcoming updated headers, the translation of the CreateDeviceResources procedure will be:
I have translated GdiInteropSample.cpp to test if my translation of the Direct2D interfaces and the helper class works.
This is the original C++ code:
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "GdiInteropSample.h"
/******************************************************************
* *
* WinMain *
* *
* Application entrypoint *
* *
******************************************************************/
int WINAPI WinMain(
HINSTANCE /*hInstance*/,
HINSTANCE /*hPrevInstance*/,
LPSTR /*lpCmdLine*/,
int /*nCmdShow*/
)
{
// Ignore the return value because we want to continue running even in the
// unlikely event that HeapSetInformation fails.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (SUCCEEDED(CoInitialize(NULL)))
{
{
DemoApp app;
if (SUCCEEDED(app.Initialize()))
{
app.RunMessageLoop();
}
}
CoUninitialize();
}
return 0;
}
/******************************************************************
* *
* DemoApp::DemoApp constructor *
* *
* Initialize member data *
* *
******************************************************************/
DemoApp::DemoApp() :
m_hwnd(NULL),
m_pD2DFactory(NULL),
m_pDCRT(NULL),
m_pBlackBrush(NULL)
{
}
/******************************************************************
* *
* DemoApp::~DemoApp destructor *
* *
* Tear down resources *
* *
******************************************************************/
DemoApp::~DemoApp()
{
SafeRelease(&m_pD2DFactory);
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
/******************************************************************
* *
* DemoApp::Initialize *
* *
* Create the application window and device-independent resources *
* *
******************************************************************/
HRESULT DemoApp::Initialize()
{
HRESULT hr;
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&wcex);
// Create the application window.
//
// Because the CreateWindow function takes its size in pixels, we
// obtain the system DPI and use it to scale the window size.
FLOAT dpiX, dpiY;
m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
m_hwnd = CreateWindow(
L"D2DDemoApp",
L"Direct2D Demo App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
UpdateWindow(m_hwnd);
}
}
return hr;
}
/******************************************************************
* *
* DemoApp::CreateDeviceIndependentResources *
* *
* This method is used to create resources which are not bound *
* to any device. Their lifetime effectively extends for the *
* duration of the app. *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceIndependentResources()
{
// Create D2D factory
return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
}
/******************************************************************
* *
* DemoApp::CreateDeviceResources *
* *
* This method creates resources which are bound to a particular *
* D3D device. It's all centralized here, in case the resources *
* need to be recreated in case of D3D device loss (eg. display *
* change, remoting, removal of video card, etc). *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pDCRT)
{
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
if (SUCCEEDED(hr))
{
// Create a black brush.
hr = m_pDCRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black),
&m_pBlackBrush
);
}
}
return hr;
}
/******************************************************************
* *
* DemoApp::DiscardDeviceResources *
* *
* Discard device-specific resources which need to be recreated *
* when a D3D device is lost *
* *
******************************************************************/
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
/******************************************************************
* *
* DemoApp::RunMessageLoop *
* *
* Main window message loop *
* *
******************************************************************/
void DemoApp::RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/******************************************************************
* *
* DemoApp::OnRender *
* *
* This method draws Direct2D content to a GDI HDC. *
* *
* This method will automatically discard device-specific *
* resources if the D3D device disappears during function *
* invocation, and will recreate the resources the next time it's *
* invoked. *
* *
******************************************************************/
HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{
HRESULT hr;
RECT rc;
// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
//
// Draw the pie chart with Direct2D.
//
// Create the DC render target.
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);
m_pDCRT->BeginDraw();
m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pDCRT->DrawEllipse(
D2D1::Ellipse(
D2D1::Point2F(150.0f, 150.0f),
100.0f,
100.0f),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f + 100.0f * 0.15425f),
(150.0f - 100.0f * 0.988f)),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f + 100.0f * 0.525f),
(150.0f + 100.0f * 0.8509f)),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f - 100.0f * 0.988f),
(150.0f - 100.0f * 0.15425f)),
m_pBlackBrush,
3.0
);
hr = m_pDCRT->EndDraw();
if (SUCCEEDED(hr))
{
//
// Draw the pie chart with GDI.
//
// Save the original object.
HGDIOBJ original = NULL;
original = SelectObject(
ps.hdc,
GetStockObject(DC_PEN)
);
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(ps.hdc, blackPen);
Ellipse(ps.hdc, 300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
Polyline(ps.hdc, pntArray1, 2);
Polyline(ps.hdc, pntArray2, 2);
Polyline(ps.hdc, pntArray3, 2);
DeleteObject(blackPen);
// Restore the original object.
SelectObject(ps.hdc, original);
}
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
/******************************************************************
* *
* DemoApp::WndProc *
* *
* Window message handler *
* *
******************************************************************/
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(pDemoApp)
);
result = 1;
}
else
{
DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_PAINT:
case WM_DISPLAYCHANGE:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
pDemoApp->OnRender(ps);
EndPaint(hwnd, &ps);
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
And this is my translation to PB:
#COMPILE EXE
#DIM ALL
#INCLUDE "d2d1.inc"
#INCLUDE "d2d1Helper.inc"
GLOBAL g_pD2DFactory AS ID2D1Factory
GLOBAL g_pDCRT AS ID2D1DCRenderTarget
GLOBAL g_pBlackBrush AS ID2D1SolidColorBrush
GLOBAL g_pID2D1Helper AS ID2D1Helper
' ========================================================================================
'*
'* This function creates resources which are bound to a particular D3D device. It's all
'* centralized here, in case the resources need to be recreated in case of D3D device
'* loss (eg. display change, remoting, removal of video card, etc).
'*
' ========================================================================================
FUNCTION CreateDeviceResources () AS LONG
LOCAL hr AS LONG
IF ISNOTHING(g_pDCRT) THEN
' // Create a DC render target.
LOCAL props AS D2D1_RENDER_TARGET_PROPERTIES
props = g_pID2D1Helper.RenderTargetProperties(%D2D1_RENDER_TARGET_TYPE_DEFAULT, _
g_pID2D1Helper.PixelFormat(%DXGI_FORMAT_B8G8R8A8_UNORM, %D2D1_ALPHA_MODE_IGNORE), _
0, 0, %D2D1_RENDER_TARGET_USAGE_NONE, %D2D1_FEATURE_LEVEL_DEFAULT)
hr = g_pD2DFactory.CreateDCRenderTarget(props, g_pDCRT)
IF SUCCEEDED(hr) THEN
' // Create a black brush.
hr = g_pDCRT.CreateSolidColorBrush( _
g_pID2D1Helper.ColorF_3(%D2D1_Black, 1.0!), _
BYVAL %NULL, _
g_pBlackBrush)
END IF
END IF
FUNCTION = hr
END FUNCTION
' ========================================================================================
' ========================================================================================
'*
'* Discard device-specific resources which need to be recreated when a D3D device is lost
'*
' ========================================================================================
SUB DiscardDeviceResources
g_pDCRT = NOTHING
g_pBlackBrush = NOTHING
END SUB
' ========================================================================================
' ========================================================================================
'* This function draws Direct2D content to a GDI HDC.
'*
'* This function will automatically discard device-specific resources if the D3D device
'* disappears during function invocation, and will recreate the resources the next time
'* it's invoked.
'*
' ========================================================================================
FUNCTION OnRender (BYVAL hwnd AS DWORD, BYREF ps AS PAINTSTRUCT) AS LONG
LOCAL hr AS LONG
LOCAL rc AS RECT
' // Get the dimensions of the client drawing area.
GetClientRect(hwnd, rc)
' // Draw the pie chart with Direct2D.
' // Create the DC render target.
hr = CreateDeviceResources
IF SUCCEEDED(hr) THEN
' // Bind the DC to the DC render target.
hr = g_pDCRT.BindDC(ps.hdc, rc)
g_pDCRT.BeginDraw
g_pDCRT.SetTransform(g_pID2D1Helper.IdentityMatrix)
g_pDCRT.Clear(g_pID2D1Helper.ColorF_3(%D2D1_White, 1.0!))
g_pDCRT.DrawEllipse( _
g_pID2D1Helper.Ellipse( _
g_pID2D1Helper.Point2F(150.0!, 150.0!), _
100.0!, _
100.0!), _
g_pBlackBrush, 3.0!)
LOCAL point0, point1 AS D2D1_POINT_2F
point0 = g_pID2D1Helper.Point2F(150.0!, 150.0!)
point1 = g_pID2D1Helper.Point2F( _
150.0! + 100.0! * 0.15425!, _
150.0! - 100.0! * 0.988!)
g_pDCRT.DrawLine( _
point0, _
point1, _
g_pBlackBrush, 3.0!)
point0 = g_pID2D1Helper.Point2F(150.0!, 150.0!)
point1 = g_pID2D1Helper.Point2F( _
150.0! + 100.0! * 0.525!, _
150.0! + 100.0! * 0.8509!)
g_pDCRT.DrawLine( _
point0, _
point1, _
g_pBlackBrush, 3.0!)
point0 = g_pID2D1Helper.Point2F(150.0!, 150.0!)
point1 = g_pID2D1Helper.Point2F( _
150.0! - 100.0! * 0.988!, _
150.0! - 100.0! * 0.15425!)
g_pDCRT.DrawLine( _
point0, _
point1, _
g_pBlackBrush, 3.0!)
hr = g_pDCRT.EndDraw
IF SUCCEEDED(hr) THEN
' //
' // Draw the pie chart with GDI.
' //
' // Save the original object.
LOCAL original AS DWORD
original = SelectObject(ps.hdc, GetStockObject(%DC_PEN))
LOCAL blackPen AS WORD
blackPen = CreatePen(%PS_SOLID, 3, 0)
SelectObject(ps.hdc, blackPen)
Ellipse(ps.hdc, 300, 50, 500, 250)
DIM pntArray1(1) AS POINT
pntArray1(0).x = 400
pntArray1(0).y = 150
pntArray1(1).x = 400 + 100 * 0.15425
pntArray1(1).y = 150 - 100 * 0.9885
DIM pntArray2(2) AS POINT
pntArray2(0).x = 400
pntArray2(0).y = 150
pntArray2(1).x = 400 + 100 * 0.525
pntArray2(1).y = 150 + 100 * 0.8509
DIM pntArray3(2) AS POINT
pntArray3(0).x = 400
pntArray3(0).y = 150
pntArray3(1).x = 400 - 100 * 0.988
pntArray3(1).y = 150 - 100 * 0.15425
Polyline(ps.hdc, pntArray1(0), 2)
Polyline(ps.hdc, pntArray2(0), 2)
Polyline(ps.hdc, pntArray3(0), 2)
DeleteObject(blackPen)
' // Restore the original object.
SelectObject(ps.hdc, original)
END IF
END IF
IF hr = %D2DERR_RECREATE_TARGET THEN
hr = %S_OK
DiscardDeviceResources
END IF
FUNCTION = hr
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
LOCAL hr AS LONG
LOCAL hWndMain AS DWORD
LOCAL wcex AS WNDCLASSEX
LOCAL szClassName AS ASCIIZ * 80
LOCAL rc AS RECT
LOCAL szCaption AS ASCIIZ * 255
' // Create D2D factory
' hr = D2D1CreateFactory(%D2D1_FACTORY_TYPE_SINGLE_THREADED, $IID_ID2D1Factory, BYVAL %NULL, g_pD2DFactory)
hr = D2D1CreateFactory2(%D2D1_FACTORY_TYPE_SINGLE_THREADED, g_pD2DFactory)
IF ISNOTHING(g_pD2DFactory) THEN EXIT FUNCTION
' // Create an instance of the CD2D1Helper class
g_pID2D1Helper = CLASS "CD2D1Helper"
IF ISNOTHING(g_pID2D1Helper) THEN EXIT FUNCTION
' Register the window class
szClassName = "D2DDemoApp"
wcex.cbSize = SIZEOF(wcex)
wcex.style = %CS_HREDRAW OR %CS_VREDRAW
wcex.lpfnWndProc = CODEPTR(WndProc)
wcex.cbClsExtra = 0
wcex.cbWndExtra = 0
wcex.hInstance = hInstance
wcex.hCursor = LoadCursor (%NULL, BYVAL %IDC_ARROW)
wcex.hbrBackground = %COLOR_3DFACE + 1
wcex.lpszMenuName = %NULL
wcex.lpszClassName = VARPTR(szClassName)
wcex.hIcon = LoadIcon (%NULL, BYVAL %IDI_APPLICATION) ' Sample, if resource icon: LoadIcon(hInst, "APPICON")
wcex.hIconSm = LoadIcon (%NULL, BYVAL %IDI_APPLICATION) ' Remember to set small icon too..
RegisterClassEx wcex
' // Create the application window.
' //
' // Because the CreateWindow function takes its size in pixels, we
' // obtain the system DPI and use it to scale the window size.
LOCAL dpiX, dpiY AS SINGLE
g_pD2DFactory.GetDesktopDpi(dpiX, dpiY)
' Window caption
szCaption = "Direct2D Demo App"
' Create a window using the registered class
hWndMain = CreateWindowEx(%WS_EX_CONTROLPARENT, _ ' extended style
szClassName, _ ' window class name
szCaption, _ ' window caption
%WS_OVERLAPPEDWINDOW, _ ' window styles
%CW_USEDEFAULT, _ ' initial x position
%CW_USEDEFAULT, _ ' initial y position
CEIL(600 * dpiX / 96), _ ' initial x size
CEIL(400 * dpiY / 96), _ ' initial y size
%NULL, _ ' parent window handle
0, _ ' window menu handle
hInstance, _ ' program instance handle
BYVAL %NULL) ' creation parameters
' Show the window
ShowWindow hWndMain, nCmdShow
UpdateWindow hWndMain
' Message handler loop
LOCAL uMsg AS tagMsg
WHILE GetMessage(uMsg, %NULL, 0, 0)
IF ISFALSE IsDialogMessage(hWndMain, uMsg) THEN
TranslateMessage uMsg
DispatchMessage uMsg
END IF
WEND
FUNCTION = uMsg.wParam
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main Window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
LOCAL rc AS RECT
SELECT CASE wMsg
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDCANCEL
IF HI(WORD, wParam) = %BN_CLICKED THEN
SendMessage hWnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE %WM_SYSCOMMAND
' Capture this message and send a WM_CLOSE message
IF (wParam AND &HFFF0) = %SC_CLOSE THEN
SendMessage hWnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
CASE %WM_PAINT, %WM_DISPLAYCHANGE
LOCAL ps AS PAINTSTRUCT
BeginPaint(hwnd, ps)
OnRender(hwnd, ps)
EndPaint(hwnd, ps)
EXIT FUNCTION
CASE %WM_DESTROY
PostQuitMessage 0
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
Here is my PB's translation of the SimplePathAnimationSample.cpp
(It is based on Windows SDK v7.0)
I had a hard time to do it, because MFC is realy not my cup of tea.
There is a small difference with the original, for the computation of the animation speed at the start/end of the geometry path.
Perhaps a C++ expert could tell me what the difference is :)
'+--------------------------------------------------------------------------+
'| |
'| PathAnim |
'| |
'| SDK example showing how to use the Direct2D API. |
'| |
'+--------------------------------------------------------------------------+
'| |
'| Author Patrice TERRIER |
'| copyright(c) 2010 |
'| www.zapsolution.com |
'| pterrier@zapsolution.com |
'| |
'+--------------------------------------------------------------------------+
'| Project started on : 06-17-2009 (MM-DD-YYYY) |
'| Last revised : 06-19-2010 (MM-DD-YYYY) |
'+--------------------------------------------------------------------------+
#COMPILE EXE "PathAnim.exe"
#INCLUDE "D2D1.inc"
#INCLUDE "DWMAPI.inc"
#INCLUDE "D2D1Helper.inc"
TYPE Animation
rStart AS SINGLE
rEnd AS SINGLE
rDuration AS SINGLE
END TYPE
GLOBAL g_pD2DFactory AS ID2D1Factory
GLOBAL g_pPathGeometry AS ID2D1PathGeometry
GLOBAL g_pObjectGeometry AS ID2D1PathGeometry
GLOBAL g_pID2D1Helper AS ID2D1Helper
GLOBAL g_pRT AS ID2D1HwndRenderTarget
GLOBAL g_pRedBrush, g_pYellowBrush AS ID2D1SolidColorBrush
'GLOBAL g_pDCRT AS ID2D1DCRenderTarget
GLOBAL gAni AS Animation
GLOBAL gDwmTimingInfo AS DWM_TIMING_INFO
DECLARE FUNCTION zTrace LIB "zTrace.DLL" ALIAS "zTrace" (zMessage AS ASCIIZ) AS LONG
FUNCTION WinMain (BYVAL hInstance AS LONG, _
BYVAL hPrevInstance AS LONG, _
BYVAL lpCmdLine AS ASCIIZ PTR, _
BYVAL iCmdShow AS LONG) AS LONG
LOCAL rc AS RECT
LOCAL Msg AS tagMsg
LOCAL wc AS WndClassEx
LOCAL zClass AS ASCIIZ * 80
LOCAL hr, IsInitialized, x, y AS LONG, dwExStyle, dwStyle, hMain, hDC AS DWORD
LOCAL rLength AS SINGLE
'
g_pID2D1Helper = CLASS "CD2D1Helper"
IF ISNOTHING(g_pID2D1Helper) THEN
MsgBox "Unable to create the D2D1Helper class"
EXIT FUNCTION
END IF
hr = CreateDeviceIndependentResources()
IF hr = 0 THEN
zClass = "ZD2D1"
'
IsInitialized = GetClassInfoEx(hInstance, zClass, wc)
IF IsInitialized& = 0 THEN
wc.cbSize = SIZEOF(wc)
wc.style = %CS_HREDRAW OR %CS_VREDRAW
wc.lpfnWndProc = CODEPTR(WndProc)
wc.cbClsExtra = 0
wc.cbWndExtra = 0
wc.hInstance = hInstance
wc.hIcon = LoadIcon(wc.hInstance, "PROGRAM")
wc.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
wc.hbrBackground = %NULL ' GetStockObject(%BLACK_BRUSH)
wc.lpszMenuName = %NULL
wc.lpszClassName = VARPTR(zClass)
wc.hIconSm = wc.hIcon
IF RegisterClassEx(wc) THEN IsInitialized = %TRUE
END IF
'
IF IsInitialized THEN
'
' Window Extended Style
dwExStyle = 0'%WS_EX_APPWINDOW OR %WS_EX_WINDOWEDGE
' Windows Style
dwStyle = %WS_OVERLAPPEDWINDOW
'
CALL SetRect(rc, 0, 0, 512, 512)
CALL AdjustWindowRectEx(rc, dwStyle, %FALSE, dwExStyle) ' Adjust Window To True Requested Size
'
x = MAX&((GetSystemMetrics(%SM_CXSCREEN) - rc.nRight - rc.nLeft) \ 2, 0)
y = MAX&((GetSystemMetrics(%SM_CYSCREEN) - rc.nBottom - rc.nTop) \ 2, 0)
'
' Create The Window
MyTitle$ = "PathAnimation"
hMain = CreateWindowEx(dwExStyle, _ ' Extended Style For The Window
zClass, _ ' Class Name
(MyTitle$), _ ' Window Title
dwStyle OR _ ' Defined Window Style
%WS_CLIPSIBLINGS OR _ ' Required Window Style
%WS_CLIPCHILDREN, _ ' Required Window Style
x, y, _ ' Window Position
rc.nRight - rc.nLeft, _ ' Calculate Window Width
rc.nBottom - rc.nTop, _ ' Calculate Window Height
%NULL, _ ' No Parent Window
%NULL, _ ' No Menu
wc.hInstance, _ ' Instance
BYVAL %NULL) ' Dont Pass Anything To WM_CREATE
'
IF hMain THEN
hr = g_pPathGeometry.ComputeLength(BYVAL %NULL, D2D1_DEFAULT_FLATTENING_TOLERANCE, rLength)
IF hr = 0 THEN
gAni.rStart = 0 '//start at begining of path
gAni.rEnd = rLength '//length at end of path
gAni.rDuration = 5.0 '//seconds
gDwmTimingInfo.cbSize = SIZEOF(DWM_TIMING_INFO)
'// Get the composition refresh rate. If the DWM isn't running,
'// get the refresh rate from GDI -- probably going to be 60Hz
IF DwmGetCompositionTimingInfo(%NULL, gDwmTimingInfo) THEN
hDC = GetDC(hMain)
gDwmTimingInfo.rateCompose.uiDenominator = 1
gDwmTimingInfo.rateCompose.uiNumerator = GetDeviceCaps(hDC, %VREFRESH)
CALL ReleaseDC(hMain, hDC)
END IF
END IF
' Show the main window
CALL ShowWindow(hMain, iCmdShow)
CALL SetForegroundWindow(hMain) ' Slightly Higher Priority
WHILE GetMessage(Msg, %NULL, 0, 0)
IF IsDialogMessage(hMain, Msg) = %FALSE THEN
CALL TranslateMessage(msg) ' Translate The Message
CALL DispatchMessage(msg) ' Dispatch The Message
END IF
WEND
FUNCTION = msg.wParam
' // Cleanup
g_pD2DFactory = NOTHING
g_pPathGeometry = NOTHING
g_pObjectGeometry = NOTHING
g_pID2D1Helper = NOTHING
g_pRT = NOTHING
g_pRedBrush = NOTHING
g_pYellowBrush = NOTHING
END IF
END IF
END IF
END FUNCTION
FUNCTION WndProc(BYVAL hWnd AS LONG, BYVAL Msg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
LOCAL ps AS PAINTSTRUCT
SELECT CASE LONG Msg
CASE %WM_SIZE
OnResize(LO(INTEGER, lParam), HI(INTEGER, lParam))
CASE %WM_COMMAND
SELECT CASE LONG LOWRD(wParam)
END SELECT
CASE %WM_PAINT, %WM_DISPLAYCHANGE
CALL BeginPaint(hWnd, ps)
CALL OnRender(hWnd)
CALL EndPaint(hWnd, ps)
FUNCTION = 0: EXIT FUNCTION
CASE %WM_CLOSE
CASE %WM_DESTROY
CALL PostQuitMessage(0)
FUNCTION = 0: EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProc(hWnd, Msg, wParam, lParam)
END FUNCTION
FUNCTION CreateDeviceIndependentResources() AS LONG
LOCAL pSink AS ID2D1GeometrySink
LOCAL hr AS LONG
' // Create Direct2D factory.
hr = D2D1CreateFactory(%D2D1_FACTORY_TYPE_SINGLE_THREADED, $IID_ID2D1Factory, BYVAL %NULL, g_pD2DFactory)
IF hr = 0 THEN
' // Create path geometry.
hr = g_pD2DFactory.CreatePathGeometry(g_pPathGeometry)
END IF
IF hr = 0 THEN
' // Write to the path geometry using the geometry sink. We are going to create a
' // spiral
hr = g_pPathGeometry.Open(pSink)
END IF
IF hr = 0 THEN
LOCAL currentLocation, locSwap, locDelta AS D2D1_POINT_2F
LOCAL radius AS SINGLE, i AS LONG
LOCAL ArcSegment AS D2D1_ARC_SEGMENT
currentLocation.x = 0
currentLocation.y = 0
pSink.BeginFigure(currentLocation, %D2D1_FIGURE_BEGIN_FILLED)
locDelta.x = 2
locDelta.y = 2
radius = 3
FOR i = 0 TO 29
currentLocation.x += radius * locDelta.x
currentLocation.y += radius * locDelta.y
ArcSegment.point = currentLocation
ArcSegment.size.width = 2 * radius
ArcSegment.size.height = 2 * radius
ArcSegment.rotationAngle = 0.0
ArcSegment.sweepDirection = %D2D1_SWEEP_DIRECTION_CLOCKWISE
ArcSegment.arcSize = %D2D1_ARC_SIZE_SMALL
pSink.AddArc(ArcSegment)
'// You can either use the José's helper method below.
' pSink.AddArc(g_pID2D1Helper.ArcSegment(currentLocation, g_pID2D1Helper.SizeF(2 * radius, 2 * radius), 0.0, %D2D1_SWEEP_DIRECTION_CLOCKWISE, %D2D1_ARC_SIZE_SMALL))
locSwap.x = -locDelta.y
locSwap.y = locDelta.x
locDelta.x = locSwap.x
locDelta.y = locSwap.y
'// You can either use the José's helper method below.
' locDelta = g_pID2D1Helper.Point2F(-locDelta.y, locDelta.x)
radius += 3
NEXT
pSink.EndFigure(%D2D1_FIGURE_END_OPEN)
hr = pSink.Close()
END IF
IF hr = 0 THEN
' // Create the path geometry.
hr = g_pD2DFactory.CreatePathGeometry(g_pObjectGeometry)
END IF
IF hr = 0 THEN
' // Write to the object geometry using the geometry sink.
' // We are going to create a simple triangle
hr = g_pObjectGeometry.Open(pSink)
END IF
IF hr = 0 THEN
currentLocation.x = 0
currentLocation.y = 0
pSink.BeginFigure(currentLocation, %D2D1_FIGURE_BEGIN_FILLED)
DIM ptTriangle(0 TO 2) AS D2D1_POINT_2F
ptTriangle(0).x = -10.0: ptTriangle(0).y = -10.0
ptTriangle(1).x = -10.0: ptTriangle(1).y = 10.0
ptTriangle(2).x = 0.0: ptTriangle(2).y = 0.0
pSink.AddLines(ptTriangle(0), 3)
pSink.EndFigure(%D2D1_FIGURE_END_OPEN)
hr = pSink.Close()
END IF
pSink = NOTHING
FUNCTION = hr
END FUNCTION
'/******************************************************************
'* *
'* DemoApp::CreateDeviceResources *
'* *
'* This method creates resources which are bound to a particular *
'* D3D device. It's all centralized here, in case the resources *
'* need to be recreated in case of D3D device loss (eg. display *
'* change, remoting, removal of video card, etc). *
'* *
'******************************************************************/
FUNCTION CreateDeviceResources(BYVAL hWnd AS LONG) AS LONG
LOCAL hr AS LONG, uSize AS D2D1_SIZE_U
LOCAL nType AS DWORD, pixelFormat AS D2D1_PIXEL_FORMAT, dpiX, dpiY AS SINGLE, usage, minlevel AS DWORD
LOCAL dxgiFormat, alphaMode AS DWORD
LOCAL brushProperties AS D2D1_BRUSH_PROPERTIES
IF ISNOTHING(g_pRT) THEN
LOCAL rc AS RECT
CALL GetClientRect(hWnd, rc)
uSize = g_pID2D1Helper.SizeU(rc.nRight - rc.nLeft, rc.nBottom - rc.nTop)
'// Create a Direct2D render target
nType = %D2D1_RENDER_TARGET_TYPE_DEFAULT
dxgiFormat = %DXGI_FORMAT_UNKNOWN
alphaMode = %D2D1_ALPHA_MODE_UNKNOWN
pixelFormat = g_pID2D1Helper.PixelFormat(dxgiFormat, alphaMode)
dpiX = 0.0
dpiY = 0.0
usage = %D2D1_RENDER_TARGET_USAGE_NONE
minLevel = %D2D1_FEATURE_LEVEL_DEFAULT
hr = g_pD2DFactory.CreateHwndRenderTarget( _
g_pID2D1Helper.RenderTargetProperties(nType, pixelFormat, dpiX, dpiY, usage, minLevel), _
g_pID2D1Helper.HwndRenderTargetProperties(hWnd, uSize, %D2D1_PRESENT_OPTIONS_NONE), _
g_pRT)
IF hr = 0 THEN
'// Create a red brush.
brushProperties.opacity = 1.0
brushProperties.transform = g_pID2D1Helper.IdentityMatrix()
hr = g_pRT.CreateSolidColorBrush( _
g_pID2D1Helper.ColorF_2(%D2D1_Red, 1.0), _
brushProperties, _
g_pRedBrush)
END IF
IF hr = 0 THEN
'// Create a yellow brush.
hr = g_pRT.CreateSolidColorBrush( _
g_pID2D1Helper.ColorF_2(%D2D1_Yellow, 1.0), _
brushProperties, _
g_pYellowBrush)
END IF
END IF
FUNCTION = hr
END FUNCTION
'/******************************************************************
'* *
'* DemoApp::DiscardDeviceResources *
'* *
'* Discard device-specific resources which need to be recreated *
'* when a D3D device is lost *
'* *
'******************************************************************/
SUB DiscardDeviceResources()
g_pRT = NOTHING
g_pRedBrush = NOTHING
g_pYellowBrush = NOTHING
END SUB
'/******************************************************************
'* *
'* DemoApp::OnRender *
'* *
'* Called whenever the application needs to display the client *
'* window. This method draws a single frame of animated content *
'* *
'* Note that this function will not render anything if the window *
'* is occluded (e.g. when the screen is locked). *
'* Also, this function will automatically discard device-specific *
'* resources if the D3D device disappears during function *
'* invocation, and will recreate the resources the next time it's *
'* invoked. *
'* *
'******************************************************************/
FUNCTION OnRender(BYVAL hWnd AS LONG) AS LONG
LOCAL hr AS LONG
hr = CreateDeviceResources(hWnd)
IF hr = 0 THEN
IF (g_pRT.CheckWindowState() AND %D2D1_WINDOW_STATE_OCCLUDED) = 0 THEN
LOCAL dPoint, tangent, center AS D2D1_POINT_2F
LOCAL triangleMatrix AS D2D1_MATRIX_3X2_F
LOCAL rtSize AS D2D1_SIZE_F
LOCAL minWidthHeightScale, rlength AS SINGLE
STATIC float_time AS SINGLE
rtSize = g_pRT.GetSize()
minWidthHeightScale = MIN(rtSize.width, rtSize.height) / 512
LOCAL mScale AS D2D1_MATRIX_3X2_F
mScale = g_pID2D1Helper.MatrixScale( _
minWidthHeightScale, _
minWidthHeightScale, _
center)
LOCAL translation AS D2D1_MATRIX_3X2_F
translation = g_pID2D1Helper.MatrixTranslation( _
rtSize.width / 2, _
rtSize.height / 2)
'// Prepare to draw.
g_pRT.BeginDraw()
'// Reset to identity transform
g_pRT.SetTransform(g_pID2D1Helper.IdentityMatrix())
'//clear the render target contents
g_pRT.Clear(g_pID2D1Helper.ColorF_2(%D2D1_Black, 1.0))
'//center the path
LOCAL centerpath AS D2D1_MATRIX_3X2_F
centerpath = g_pID2D1Helper.MultiplyMatrices(mScale, translation)
g_pRT.SetTransform(centerpath)
'//draw the path in red
g_pRT.DrawGeometry(g_pPathGeometry, g_pRedBrush, 1.0)
'//rlength = m_Animation.GetValue(float_time)
rlength = MIN(MAX(float_time, 0), gAni.rDuration)
rlength = gAni.rStart + ((gAni.rEnd - gAni.rStart) * (rlength / gAni.rDuration))
'// Ask the geometry to give us the point that corresponds with the
'// length at the current time.
hr = g_pPathGeometry.ComputePointAtLength(rlength, BYVAL %NULL, 0.0, dPoint, tangent)
'//Assert(SUCCEEDED(hr))
IF hr THEN MsgBox "Assert failed"
'// Reorient the triangle so that it follows the
'// direction of the path.
triangleMatrix = g_pID2D1Helper.Matrix3x2F( _
tangent.x, tangent.y, _
-tangent.y, tangent.x, _
dPoint.x, dPoint.y)
LOCAL m AS D2D1_MATRIX_3X2_F
m = g_pID2D1Helper.MultiplyMatrices(triangleMatrix, centerpath)
g_pRT.SetTransform(m) '// triangleMatrix * mScale * translation)
'// Draw the yellow triangle.
g_pRT.FillGeometry(g_pObjectGeometry, g_pYellowBrush)
'// Commit the drawing operations.
hr = g_pRT.EndDraw()
IF hr = %D2DERR_RECREATE_TARGET THEN
hr = 0
DiscardDeviceResources()
END IF
'// When we reach the end of the animation, loop back to the beginning.
IF float_time >= gAni.rDuration THEN
float_time = 0.0
ELSE
float_time += gDwmTimingInfo.rateCompose.uiDenominator / gDwmTimingInfo.rateCompose.uiNumerator
END IF
END IF
END IF
CALL InvalidateRect(hWnd, BYVAL %NULL, %FALSE)
FUNCTION = hr
END FUNCTION
'/******************************************************************
'* *
'* DemoApp::OnResize *
'* *
'* If the application receives a WM_SIZE message, this method *
'* resize the render target appropriately. *
'* *
'******************************************************************/
SUB OnResize(BYVAL nWidth AS LONG, BYVAL nHeight AS LONG)
IF NOT ISNOTHING(g_pRT) THEN
LOCAL su AS D2D1_SIZE_U
su.width = nWidth
su.height = nHeight
'// Note: This method can fail, but it's okay to ignore the
'// error here -- it will be repeated on the next call to
'// EndDraw.
g_pRT.Resize(su)
END IF
END SUB
Note that the animation is not based on a timer, but on the DWM refresh rate (60 Hz)
Thus in this case 60 Hz = 60 FPS, because it is the maximum you can have in composited mode ;D
...
When translating D2D C++ examples to PB, be careful to the translation of matrix manipulation.
Here is an example
This easy to understand C++ syntax:
Quotem_pRT->SetTransform(triangleMatrix * scale * translation);
Must be translated to something like this in PB (using José's helper function):
QuoteLOCAL centerpath AS D2D1_MATRIX_3X2_F
centerpath = g_pID2D1Helper.MultiplyMatrices(mScale, translation)
g_pRT.SetTransform(centerpath)
LOCAL m AS D2D1_MATRIX_3X2_F
m = g_pID2D1Helper.MultiplyMatrices(triangleMatrix, centerpath)
g_pRT.SetTransform(m) '// triangleMatrix * mScale * translation)
While we can emulate many of the D2D features with OpenGL, there is no comparison as soon as we want to use or manipulate text font.
...
Attached to this post you will find the PB's translation of the
SimpleDirect2DApplication.cppNote: Until José post a new
D2D1Helper.inc, you must edit the "BezierSegment" method like this:
' =====================================================================================
' Creates a D2D1_BEZIER_SEGMENT structure.
' =====================================================================================
METHOD BezierSegment ( _
BYREF point1 AS D2D1_POINT_2F, _
BYREF point2 AS D2D1_POINT_2F, _
BYREF point3 AS D2D1_POINT_2F _
) AS D2D1_ARC_SEGMENT
LOCAL bzseg AS D2D1_BEZIER_SEGMENT
bzseg.point1 = point1
bzseg.point2 = point2
bzseg.point3 = point3
METHOD = bzseg
END METHOD
This demo shows you:
- How to render WICbitmap from resource and/or from file.
- How to render a text font at any handle.
- How to render a path geometry.
- How to fill a custom shape with gradient brush.
- How to fill the background with a custom bitmap brush.
As a matter of comparison, here is the size of resulting EXE.
- VS2010 C++ = 768 Kb
- PB9 = 807 Kb
Thanks for the examples,
the antialiased look of primitives is nice, is there any way to determine the algorithm used for antialiasing?
Petr
Quotethe antialiased look of primitives is nice, is there any way to determine the algorithm used for antialiasing?
' enum D2D1_ANTIALIAS_MODE
%D2D1_ANTIALIAS_MODE_PER_PRIMITIVE = 0???
%D2D1_ANTIALIAS_MODE_ALIASED = 1???
' enum D2D1_TEXT_ANTIALIAS_MODE
%D2D1_TEXT_ANTIALIAS_MODE_DEFAULT = 0???
%D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE = 1???
%D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE = 2???
%D2D1_TEXT_ANTIALIAS_MODE_ALIASED = 3???
' enum D2D1_BITMAP_INTERPOLATION_MODE
%D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR = 0???
%D2D1_BITMAP_INTERPOLATION_MODE_LINEAR = 1???
The resulting quality is the same than using GDI+ bicubic, but thanks to the GPU it is done almost instantaneously!
Applications that use Direct2D for graphics can deliver higher visual quality than what can be achieved using GDI. Direct2D uses per-primitive antialiasing to deliver smoother looking curves and lines in rendered content. There is also full support for transparency and alpha blending when rendering 2-D primitives.
Direct2D also makes it easy to use
DirectWrite, the new text API, and the advanced imaging features of the Microsoft
Windows Imaging Component (WIC).
Learn more about Direct2D
here (http://msdn.microsoft.com/en-us/library/dd317121(v=VS.85).aspx)
...
I have modified d2d1Helper.inc to change many parameters as optional to take advantage of default values.
For example, this C++ code:
// Create a Direct2D render target.
hr = m_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
That previously needed to be translated as:
LOCAL nType AS DWORD, pixelFormat AS D2D1_PIXEL_FORMAT, dpiX, dpiY AS SINGLE, usage, minlevel AS DWORD
nType = %D2D1_RENDER_TARGET_TYPE_DEFAULT
dxgiFormat = %DXGI_FORMAT_UNKNOWN
alphaMode = %D2D1_ALPHA_MODE_UNKNOWN
pixelFormat = g_pID2D1Helper.PixelFormat(dxgiFormat, alphaMode)
dpiX = 0.0
dpiY = 0.0
usage = %D2D1_RENDER_TARGET_USAGE_NONE
minLevel = %D2D1_FEATURE_LEVEL_DEFAULT
hr = g_pD2DFactory.CreateHwndRenderTarget( _
g_pID2D1Helper.RenderTargetProperties(nType, pixelFormat, dpiX, dpiY, usage, minLevel), _
g_pID2D1Helper.HwndRenderTargetProperties(hWnd, uSize, %D2D1_PRESENT_OPTIONS_NONE), _
g_pRenderTarget)
Now becomes:
hr = g_pD2DFactory.CreateHwndRenderTarget( _
g_pID2D1Helper.RenderTargetProperties(), _
g_pID2D1Helper.HwndRenderTargetProperties(hWnd, uSize), _
g_pRenderTarget)
I also have added a new file, called d2dUtils.inc, that incorporates the functions D2D_LoadBitmapFromFile and D2D_LoadBitmapFromResource.
Attached to this post you will find the PB's translation of the
TextAnimationSample.cppYou can change the animations using these keys:
- "R" to perform rotation.
- "S" to perform scaling.
- "T" to perform translation.
Added:FPS counter to see the impact of changing the rendering method.
You can change the rendering method using these keys:
- "1" use the Default method.
- "2" use the Outline method.
- "3" use the UseA8Target method.
...
The examples have been edited, to let them work only with PB10+ and the latest José Roca's include files.
...