Draw a circular progress bar using Direct2D - visual-c++
I'm creating a circular progress bar control and using Direct2D to draw it.
I want to get something like this:
I am having a hard time trying to figure out how to draw this kind of gradient. I have already achieved to draw the progress with a solid color brush or using diferent gradients (linear, radial) but I can not get to draw a gradient following the bar like in the image.
This is what I have right now:
Can anyone give me a hint of what kind of brush I have to use to get what I want???
Thanks in advance!
I want an arc gradient...something like this:
I just work out a initial version of this, not sure if this is what you want, just for your reference.
The final effect
The programming steps:
Create the outline of the circle(this can be done by combine two ellipse to build up a geometry group).
Create a radius gradient brush, use this brush to fill the circle.
Rotate the gradient brush in each frame to make the rotation effect of the progress bar.
Full code:
Note, you need to link d2d1.lib and winmm.lib to make it work.
Since you are familiar with Direct2D, so I am not going to go through the details of the code, please leave a comment if you need any further discuss. thanks. you can also download the full project on github here.
#include <D2D1.h>
#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}
const int GEOMETRY_COUNT = 2;
ID2D1Factory* g_pD2DFactory = NULL; // Direct2D factory
ID2D1HwndRenderTarget* g_pRenderTarget = NULL; // Render target
ID2D1SolidColorBrush* g_pBlackBrush = NULL; // Outline brush
ID2D1RadialGradientBrush* g_pRadialGradientBrush = NULL ; // Radial gradient brush
// 2 circle to build up a geometry group. this is the outline of the progress bar
D2D1_ELLIPSE g_Ellipse0 = D2D1::Ellipse(D2D1::Point2F(300, 300), 150, 150);
D2D1_ELLIPSE g_Ellipse1 = D2D1::Ellipse(D2D1::Point2F(300, 300), 200, 200);
D2D1_ELLIPSE g_Ellipse[GEOMETRY_COUNT] =
{
g_Ellipse0,
g_Ellipse1,
};
ID2D1EllipseGeometry* g_pEllipseArray[GEOMETRY_COUNT] = { NULL };
ID2D1GeometryGroup* g_pGeometryGroup = NULL;
VOID CreateD2DResource(HWND hWnd)
{
if (!g_pRenderTarget)
{
HRESULT hr ;
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory) ;
if (FAILED(hr))
{
MessageBox(hWnd, "Create D2D factory failed!", "Error", 0) ;
return ;
}
// Obtain the size of the drawing area
RECT rc ;
GetClientRect(hWnd, &rc) ;
// Create a Direct2D render target
hr = g_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(
hWnd,
D2D1::SizeU(rc.right - rc.left,rc.bottom - rc.top)
),
&g_pRenderTarget
) ;
if (FAILED(hr))
{
MessageBox(hWnd, "Create render target failed!", "Error", 0) ;
return ;
}
// Create the outline brush(black)
hr = g_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black),
&g_pBlackBrush
) ;
if (FAILED(hr))
{
MessageBox(hWnd, "Create outline brush(black) failed!", "Error", 0) ;
return ;
}
// Define gradient stops
D2D1_GRADIENT_STOP gradientStops[2] ;
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Blue) ;
gradientStops[0].position = 0.f ;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::White) ;
gradientStops[1].position = 1.f ;
// Create gradient stops collection
ID2D1GradientStopCollection* pGradientStops = NULL ;
hr = g_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
) ;
if (FAILED(hr))
{
MessageBox(NULL, "Create gradient stops collection failed!", "Error", 0);
}
// Create a linear gradient brush to fill in the ellipse
hr = g_pRenderTarget->CreateRadialGradientBrush(
D2D1::RadialGradientBrushProperties(
D2D1::Point2F(170, 170),
D2D1::Point2F(0, 0),
150,
150),
pGradientStops,
&g_pRadialGradientBrush
) ;
if (FAILED(hr))
{
MessageBox(hWnd, "Create linear gradient brush failed!", "Error", 0) ;
return ;
}
// Create the 2 ellipse.
for (int i = 0; i < GEOMETRY_COUNT; ++i)
{
hr = g_pD2DFactory->CreateEllipseGeometry(g_Ellipse[i], &g_pEllipseArray[i]);
if (FAILED(hr))
{
MessageBox(hWnd, "Create Ellipse Geometry failed!", "Error", 0);
return;
}
}
// Create the geometry group, the 2 circles make up a group.
hr = g_pD2DFactory->CreateGeometryGroup(
D2D1_FILL_MODE_ALTERNATE,
(ID2D1Geometry**)&g_pEllipseArray,
ARRAYSIZE(g_pEllipseArray),
&g_pGeometryGroup
);
}
}
VOID Render(HWND hwnd)
{
// total angle to rotate
static float totalAngle = 0.0f;
// Get last time
static DWORD lastTime = timeGetTime();
// Get current time
DWORD currentTime = timeGetTime();
// Calculate time elapsed in current frame.
float timeDelta = (float)(currentTime - lastTime) * 0.1;
// Increase the totalAngle by the time elapsed in current frame.
totalAngle += timeDelta;
CreateD2DResource(hwnd) ;
g_pRenderTarget->BeginDraw() ;
// Clear background color to White
g_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
// Draw geometry group
g_pRenderTarget->DrawGeometry(g_pGeometryGroup, g_pBlackBrush);
// Roatate the gradient brush based on the total elapsed time
D2D1_MATRIX_3X2_F rotMatrix = D2D1::Matrix3x2F::Rotation(totalAngle, D2D1::Point2F(300, 300));
g_pRadialGradientBrush->SetTransform(rotMatrix);
// Fill geometry group with the transformed brush
g_pRenderTarget->FillGeometry(g_pGeometryGroup, g_pRadialGradientBrush);
HRESULT hr = g_pRenderTarget->EndDraw() ;
if (FAILED(hr))
{
MessageBox(NULL, "Draw failed!", "Error", 0) ;
return ;
}
// Update last time to current time for next loop
lastTime = currentTime;
}
VOID Cleanup()
{
SAFE_RELEASE(g_pRenderTarget) ;
SAFE_RELEASE(g_pBlackBrush) ;
SAFE_RELEASE(g_pGeometryGroup);
SAFE_RELEASE(g_pRadialGradientBrush);
for (int i = 0; i < GEOMETRY_COUNT; ++i)
{
SAFE_RELEASE(g_pEllipseArray[i]);
g_pEllipseArray[i] = NULL;
}
SAFE_RELEASE(g_pD2DFactory) ;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
Render(hwnd) ;
return 0 ;
case WM_KEYDOWN:
{
switch( wParam )
{
case VK_ESCAPE:
SendMessage( hwnd, WM_CLOSE, 0, 0 );
break ;
default:
break ;
}
}
break ;
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
WNDCLASSEX winClass ;
winClass.lpszClassName = "Direct2D";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WndProc;
winClass.hInstance = hInstance;
winClass.hIcon = NULL ;
winClass.hIconSm = NULL ;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = NULL ;
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
if (!RegisterClassEx (&winClass))
{
MessageBox ( NULL, TEXT( "This program requires Windows NT!" ), "error", MB_ICONERROR) ;
return 0 ;
}
HWND hwnd = CreateWindowEx(NULL,
"Direct2D", // window class name
"Circular Progressbar", // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
600, // initial x size
600, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
MSG msg ;
ZeroMemory(&msg, sizeof(msg)) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
Related
How to capture a certain region of screen
Ihave to record a certain area of a screen and save the frames (20-30fps) of the video being played. Currently I'm able to capture the screen at the required frame rate using the following code, However I'm not really sure about how I can capture a certain region. HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, BYTE, *pixels, LPWSTR filePath, const GUID &format) { if (!filePath || !pixels) return E_INVALIDARG; HRESULT hr = S_OK; IWICImagingFactory *factory = nullptr; IWICBitmapEncoder *encoder = nullptr; IWICBitmapFrameEncode *frame = nullptr; IWICStream *stream = nullptr; GUID pf = GUID_WICPixelFormat32bppPBGRA; BOOL coInit = CoInitialize(nullptr); CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)); factory->CreateStream(&stream); stream->InitializeFromFilename(filePath, GENERIC_WRITE); factory->CreateEncoder(format, nullptr, &encoder); encoder->Initialize(stream, WICBitmapEncoderNoCache); encoder->CreateNewFrame(&frame, nullptr); // we don't use options here frame->Initialize(nullptr); // we dont' use any options here frame->SetSize(width, height); frame->SetPixelFormat(&pf); frame->WritePixels(height, stride, stride * height, pixels); frame->Commit(); encoder->Commit(); RELEASE(stream); RELEASE(frame); RELEASE(encoder); RELEASE(factory); if (coInit) CoUninitialize(); return hr; } HRESULT Direct3D9TakeScreenshots(UINT adapter, int count) { HRESULT hr = S_OK; IDirect3D9 *d3d = nullptr; IDirect3DDevice9 *device = nullptr; IDirect3DSurface9 *surface = nullptr; D3DPRESENT_PARAMETERS parameters = { 0 }; D3DDISPLAYMODE mode; D3DLOCKED_RECT rc; UINT pitch; SYSTEMTIME st; BYTE *shot = nullptr; // init D3D and get screen size d3d = Direct3DCreate9(D3D_SDK_VERSION); d3d->GetAdapterDisplayMode(adapter, &mode); parameters.Windowed = TRUE; parameters.BackBufferCount = 1; parameters.BackBufferHeight = mode.Height; parameters.BackBufferWidth = mode.Width; parameters.SwapEffect = D3DSWAPEFFECT_DISCARD; parameters.hDeviceWindow = NULL; // create device & capture surface d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device); device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr); // compute the required buffer size surface->LockRect(&rc, NULL, 0); pitch = rc.Pitch; surface->UnlockRect(); // allocate screenshots buffers shot = new BYTE[pitch * mode.Height]; // get the data device->GetFrontBufferData(0, surface); // copy it into our buffers surface->LockRect(&rc, NULL, 0); CopyMemory(shot, rc.pBits, rc.Pitch * mode.Height); surface->UnlockRect(); WCHAR file[100]; wsprintf(file, L"C:/Frames/cap%i.png", count); SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shot, file, GUID_ContainerFormatPng); if (shot != nullptr) { delete shot; } RELEASE(surface); RELEASE(device); RELEASE(d3d); return hr; } I know I can open the saved images and crop them and then again save them, but that will take up extra time. Is there a way to select a desired region
How to Render TriangleList and LineList in same frame in dx11
How to Render TriangleList and LineList in same frame in dx11. I tried to bind two sets of buffers to GPU. Each buffer set contains one index_buffer and one vertices buffer. And use ID3D11Device::DrawIndexed Function to Render the vertices. I thought that to decide what polygon to render just simply call the ID3D11DeviceContext::IASetPrimitiveTopology function real-timely. But it seems won't work. There would be only one single line rendered. How can I render both the triangle and the line in same frame ? My code is based on dxsdk tutorials03 in" dx11 area".The code is as follows.I simply add a ray render class to render a line . #pragma once //RayRenderClass.h #include<D3D11.h> #include<D3DX11.h> #include<D3DX10math.h> struct PointClass { float _XYZ[3]; }; struct VertexClass { PointClass pts; float nor[3]; float tex[3]; }; class RayRenderClass { public: RayRenderClass(); ~RayRenderClass(); HRESULT Initialize(ID3D11Device* device, ID3D11DeviceContext* context); HRESULT Shutdown(); HRESULT Render(ID3D11Device* device, ID3D11DeviceContext* context); HRESULT UpdateStartPosition(D3DXVECTOR3 st); HRESULT UpdateEndPosition(D3DXVECTOR3 ed); D3DXVECTOR3 GetStartPosition(); D3DXVECTOR3 GetEndPosition(); private: ULONG indices[2]; VertexClass pts[2]; ID3D11Buffer* m_indexBuffer; ID3D11Buffer* m_vertices; }; #include "RayRenderClass.h" //RayRenderClass.cpp #define SAFE_RELEASE(x) if((x)){(x)->Release(); x= nullptr;} RayRenderClass::RayRenderClass() { ZeroMemory(pts, sizeof(pts)); //start point pts[0].pts._XYZ[0] = 0.0f; pts[0].pts._XYZ[1] = 0.0f; pts[0].pts._XYZ[2] = 0.0f; //end point pts[1].pts._XYZ[0] = 1.0f; pts[1].pts._XYZ[1] = 0.0f; pts[1].pts._XYZ[2] = 0.0f; } RayRenderClass::~RayRenderClass() { } HRESULT RayRenderClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* context) { HRESULT hr = E_FAIL; D3D11_BUFFER_DESC verticesDesc; D3D11_BUFFER_DESC indexDesc; D3D11_SUBRESOURCE_DATA verticesData; D3D11_SUBRESOURCE_DATA indexData; ZeroMemory(&verticesDesc, sizeof(verticesDesc)); verticesDesc.Usage = D3D11_USAGE_DYNAMIC; verticesDesc.ByteWidth = sizeof(VertexClass)* 2; verticesDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; verticesDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; ZeroMemory(&verticesData, sizeof(verticesData)); verticesData.pSysMem = &pts[0].pts._XYZ[0]; hr = device->CreateBuffer(&verticesDesc, &verticesData, &m_vertices); if (FAILED(hr)) return hr; ZeroMemory(&indexDesc, sizeof(indexDesc)); indexDesc.Usage = D3D11_USAGE_DEFAULT; indexDesc.ByteWidth = sizeof(ULONG)* 2; indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; ZeroMemory(&indexData, sizeof(indexData)); indexData.pSysMem = indices; indexData.SysMemPitch = 0; indexData.SysMemSlicePitch = 0; hr = device->CreateBuffer(&indexDesc, &indexData, &m_indexBuffer); if (FAILED(hr)) { return hr; } return S_OK; } HRESULT RayRenderClass::Shutdown() { SAFE_RELEASE(m_vertices); SAFE_RELEASE(m_indexBuffer); return S_OK; } HRESULT RayRenderClass::UpdateStartPosition(D3DXVECTOR3 st) { HRESULT hr = E_FAIL; pts[0].pts._XYZ[0] = st.x; pts[0].pts._XYZ[1] = st.y; pts[0].pts._XYZ[2] = st.z; return hr; } HRESULT RayRenderClass::UpdateEndPosition(D3DXVECTOR3 ed) { HRESULT hr = E_FAIL; pts[1].pts._XYZ[0] = ed.x; pts[1].pts._XYZ[1] = ed.y; pts[1].pts._XYZ[2] = ed.z; return hr; } D3DXVECTOR3 RayRenderClass::GetStartPosition() { D3DXVECTOR3 ret = { pts[0].pts._XYZ }; return ret; } D3DXVECTOR3 RayRenderClass::GetEndPosition() { D3DXVECTOR3 ret = { pts[1].pts._XYZ }; return ret; } HRESULT RayRenderClass::Render(ID3D11Device* device, ID3D11DeviceContext* context) { HRESULT hr = E_FAIL; D3D11_MAPPED_SUBRESOURCE mappedResource; VertexClass* verticesPtr; UINT stride = sizeof(VertexClass); UINT offset = 0; hr = context->Map(m_vertices, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if (FAILED(hr)) return hr; verticesPtr = (VertexClass*)mappedResource.pData; memcpy(verticesPtr, (void*)pts, (sizeof(VertexClass)* 2)); context->Unmap(m_vertices, 0); context->IASetVertexBuffers(0, 1, &m_vertices, &stride, &offset); context->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0); //D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); context->DrawIndexed(2, 0, 0); return hr; } //-------------------------------------------------------------------------------------- // File: Tutorial03.cpp // // This application displays a triangle using Direct3D 11 // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dcompiler.h> #include <xnamath.h> #include "resource.h" #include "RayRenderClass.h" //-------------------------------------------------------------------------------------- // Structures //-------------------------------------------------------------------------------------- struct SimpleVertex { XMFLOAT3 Pos; }; //-------------------------------------------------------------------------------------- // Global Variables //-------------------------------------------------------------------------------------- HINSTANCE g_hInst = NULL; HWND g_hWnd = NULL; D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; ID3D11Device* g_pd3dDevice = NULL; ID3D11DeviceContext* g_pImmediateContext = NULL; IDXGISwapChain* g_pSwapChain = NULL; ID3D11RenderTargetView* g_pRenderTargetView = NULL; ID3D11VertexShader* g_pVertexShader = NULL; ID3D11PixelShader* g_pPixelShader = NULL; ID3D11InputLayout* g_pVertexLayout = NULL; ID3D11Buffer* g_pVertexBuffer = NULL; ID3D11Buffer* g_index = NULL; RayRenderClass m_ray; //-------------------------------------------------------------------------------------- // Forward declarations //-------------------------------------------------------------------------------------- HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); HRESULT InitDevice(); void CleanupDevice(); LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); void Render(); //-------------------------------------------------------------------------------------- // Entry point to the program. Initializes everything and goes into a message processing // loop. Idle time is used to render the scene. //-------------------------------------------------------------------------------------- int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { UNREFERENCED_PARAMETER( hPrevInstance ); UNREFERENCED_PARAMETER( lpCmdLine ); if( FAILED( InitWindow( hInstance, nCmdShow ) ) ) return 0; if( FAILED( InitDevice() ) ) { CleanupDevice(); return 0; } // Main message loop MSG msg = {0}; while( WM_QUIT != msg.message ) { if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } else { Render(); } } CleanupDevice(); return ( int )msg.wParam; } //-------------------------------------------------------------------------------------- // Register class and create window //-------------------------------------------------------------------------------------- HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ) { // Register class WNDCLASSEX wcex; wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 ); wcex.lpszMenuName = NULL; wcex.lpszClassName = L"TutorialWindowClass"; wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 ); if( !RegisterClassEx( &wcex ) ) return E_FAIL; // Create window g_hInst = hInstance; RECT rc = { 0, 0, 640, 480 }; AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); g_hWnd = CreateWindow( L"TutorialWindowClass", L"Direct3D 11 Tutorial 3: Shaders", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL ); if( !g_hWnd ) return E_FAIL; ShowWindow( g_hWnd, nCmdShow ); return S_OK; } //-------------------------------------------------------------------------------------- // Helper for compiling shaders with D3DX11 //-------------------------------------------------------------------------------------- HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut ) { HRESULT hr = S_OK; DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; #if defined( DEBUG ) || defined( _DEBUG ) // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders. // Setting this flag improves the shader debugging experience, but still allows // the shaders to be optimized and to run exactly the way they will run in // the release configuration of this program. dwShaderFlags |= D3DCOMPILE_DEBUG; #endif ID3DBlob* pErrorBlob; hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL ); if( FAILED(hr) ) { if( pErrorBlob != NULL ) OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() ); if( pErrorBlob ) pErrorBlob->Release(); return hr; } if( pErrorBlob ) pErrorBlob->Release(); return S_OK; } //-------------------------------------------------------------------------------------- // Create Direct3D device and swap chain //-------------------------------------------------------------------------------------- HRESULT InitDevice() { HRESULT hr = S_OK; RECT rc; GetClientRect( g_hWnd, &rc ); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; UINT createDeviceFlags = 0; #ifdef _DEBUG createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_DRIVER_TYPE driverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = ARRAYSIZE( driverTypes ); D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, }; UINT numFeatureLevels = ARRAYSIZE( featureLevels ); DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof( sd ) ); sd.BufferCount = 1; sd.BufferDesc.Width = width; sd.BufferDesc.Height = height; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.OutputWindow = g_hWnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = TRUE; for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ ) { g_driverType = driverTypes[driverTypeIndex]; hr = D3D11CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext ); if( SUCCEEDED( hr ) ) break; } if( FAILED( hr ) ) return hr; // Create a render target view ID3D11Texture2D* pBackBuffer = NULL; hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer ); if( FAILED( hr ) ) return hr; hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView ); pBackBuffer->Release(); if( FAILED( hr ) ) return hr; g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL ); // Setup the viewport D3D11_VIEWPORT vp; vp.Width = (FLOAT)width; vp.Height = (FLOAT)height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; g_pImmediateContext->RSSetViewports( 1, &vp ); // Compile the vertex shader ID3DBlob* pVSBlob = NULL; hr = CompileShaderFromFile( L"Tutorial03.fx", "VS", "vs_4_0", &pVSBlob ); if( FAILED( hr ) ) { MessageBox( NULL, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK ); return hr; } // Create the vertex shader hr = g_pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader ); if( FAILED( hr ) ) { pVSBlob->Release(); return hr; } // Define the input layout D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE( layout ); // Create the input layout hr = g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout ); pVSBlob->Release(); if( FAILED( hr ) ) return hr; // Set the input layout g_pImmediateContext->IASetInputLayout( g_pVertexLayout ); // Compile the pixel shader ID3DBlob* pPSBlob = NULL; hr = CompileShaderFromFile( L"Tutorial03.fx", "PS", "ps_4_0", &pPSBlob ); if( FAILED( hr ) ) { MessageBox( NULL, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK ); return hr; } // Create the pixel shader hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader ); pPSBlob->Release(); if( FAILED( hr ) ) return hr; // Create vertex buffer SimpleVertex vertices[] = { XMFLOAT3( 0.0f, 0.5f, 0.5f ), XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT3( -0.5f, -0.5f, 0.5f ), }; D3D11_BUFFER_DESC bd; ZeroMemory( &bd, sizeof(bd) ); bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof( SimpleVertex ) * 3; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; D3D11_SUBRESOURCE_DATA InitData; ZeroMemory( &InitData, sizeof(InitData) ); InitData.pSysMem = vertices; hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ); if( FAILED( hr ) ) return hr; // Set vertex buffer UINT stride = sizeof( SimpleVertex ); UINT offset = 0; D3D11_BUFFER_DESC indexDesc; D3D11_SUBRESOURCE_DATA indexData; g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset ); // Set primitive topology g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); UINT indices[] = {0,1,2}; ZeroMemory(&indexDesc, sizeof(indexDesc)); indexDesc.Usage = D3D11_USAGE_DEFAULT; indexDesc.ByteWidth = sizeof(ULONG)* 3; indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; ZeroMemory(&indexData, sizeof(indexData)); indexData.pSysMem = indices; indexData.SysMemPitch = 0; indexData.SysMemSlicePitch = 0; hr = g_pd3dDevice->CreateBuffer(&indexDesc, &indexData, &g_index); if (FAILED(hr)) { return hr; } m_ray.Initialize(g_pd3dDevice, g_pImmediateContext); return S_OK; } //-------------------------------------------------------------------------------------- // Clean up the objects we've created //-------------------------------------------------------------------------------------- void CleanupDevice() { if( g_pImmediateContext ) g_pImmediateContext->ClearState(); if( g_pVertexBuffer ) g_pVertexBuffer->Release(); if( g_pVertexLayout ) g_pVertexLayout->Release(); if( g_pVertexShader ) g_pVertexShader->Release(); if( g_pPixelShader ) g_pPixelShader->Release(); if( g_pRenderTargetView ) g_pRenderTargetView->Release(); if( g_pSwapChain ) g_pSwapChain->Release(); if( g_pImmediateContext ) g_pImmediateContext->Release(); if( g_pd3dDevice ) g_pd3dDevice->Release(); } //-------------------------------------------------------------------------------------- // Called every time the application receives a message //-------------------------------------------------------------------------------------- LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; HDC hdc; switch( message ) { case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); EndPaint( hWnd, &ps ); break; case WM_DESTROY: PostQuitMessage( 0 ); break; default: return DefWindowProc( hWnd, message, wParam, lParam ); } return 0; } //-------------------------------------------------------------------------------------- // Render a frame //-------------------------------------------------------------------------------------- void Render() { // Clear the back buffer float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; // red,green,blue,alpha g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor ); g_pImmediateContext->IASetIndexBuffer(g_index, DXGI_FORMAT_R32_UINT, 0); g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // Render a triangle g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 ); g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 ); g_pImmediateContext->DrawIndexed( 3, 0 ,0); // Present the information rendered to the back buffer to the front buffer (the screen) m_ray.Render(g_pd3dDevice, g_pImmediateContext); g_pSwapChain->Present(0, 0); }
In your render method, you don't set the vertex buffer for your triangle (only on initialization), so by calling m_ray.Render(g_pd3dDevice, g_pImmediateContext); You permanently set the pipeline to use your line buffer instead. Make sure that the following line is called within your render function, to properly assign your triangle before the draw: g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset ); For you ray, you should not even need an index buffer, you can get rid of it and use: g_pImmediateContext->Draw(2,0); For the same effect
Visual c++ DirectX compilation error
Can any one help me with this error ? And I have Linked to d3d10.lib and d3dx10.lib. I am new to directX stuff and I was following wendy jones DirectX 10 toturial 1>t1.obj : error LNK2019: unresolved external symbol _D3D10CreateDeviceAndSwapChain#32 referenced in function "bool cdecl InitDirect3D(struct HWND *,int,int)" (?InitDirect3D##YA_NPAUHWND__##HH#Z) 1>C:\Users\Ehsan\Documents\Visual Studio 2010\Projects\DirectX\t1\Debug\t1.exe : fatal error LNK1120: 1 unresolved externals source code : // t1.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "windows.h" #include "tchar.h" #include <d3d10.h> #include <d3dx10.h> // Global Variables: HINSTANCE hInst; // global handle to hold the application instance HWND wndHandle; // global variable to hold the window handle int width = 640; int height = 480; // Direct3D global vars ID3D10Device * pD3DDevice = NULL; IDXGISwapChain * pSwapChain = NULL; ID3D10RenderTargetView * pRenderTargetView = NULL; // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); bool InitWindow( HINSTANCE hInstance, int width, int height ); void Render(); void ShutDownDirect3D(); bool InitDirect3D(HWND hWnd, int width, int height); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg = {0}; // Perform application initialization: if ( !InitWindow( hInstance, width, height ) ) { return FALSE; } // called after creating the window if(!InitDirect3D(wndHandle, width, height)) { return FALSE; } //hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_T1)); // Main message loop: while (WM_QUIT != msg.message) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } // Call the render function Render(); } ShutDownDirect3D(); return (int) msg.wParam; } bool InitWindow(HINSTANCE hInstance, int width, int height) { WNDCLASSEX wcex; // Fill in the WNDCLASSEX structure. This describes how the window // will look to the system wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback wcex.cbClsExtra = 0; // extra bytes to allocate for this class wcex.cbWndExtra = 0; // extra bytes to allocate for this instance wcex.hInstance = hInstance; // handle to the application instance wcex.hIcon = 0; // icon to associate with the application wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor to use wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // the background color wcex.lpszMenuName = NULL; // the resource name for the menu wcex.lpszClassName = TEXT("DirectXExample"); // the class name being created wcex.hIconSm = 0; // the handle to the small icon RegisterClassEx(&wcex); // Resize the window RECT rect = { 0, 0, width, height }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); // create the window from the class above wndHandle = CreateWindow(TEXT("DirectXExample"), TEXT("DirectXExample"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL); if (!wndHandle) { return false; } // Display the window on the screen ShowWindow(wndHandle, SW_SHOW); UpdateWindow(wndHandle); return true; } // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // Check any available messages from the queue switch (message) { // Allow the user to press the Escape key to end the application case WM_KEYDOWN: switch(wParam) { // Check if the user hit the Escape key case VK_ESCAPE: PostQuitMessage(0); break; } break; // The user hit the close button, close the application case WM_DESTROY: PostQuitMessage(0); break; } // Always return the message to the default window procedure for furtherprocessing return DefWindowProc(hWnd, message, wParam, lParam); } /******************************************************************* * InitDirect3D * Initializes Direct3D * Inputs - Parent window handle - HWND, Window width - int Window height - int Updating the Code 31 * Outputs - true if successful, false if failed - bool *******************************************************************/ bool InitDirect3D(HWND hWnd, int width, int height) { // Create the clear the DXGI_SWAP_CHAIN_DESC structure DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); // Fill in the needed values swapChainDesc.BufferCount = 1; swapChainDesc.BufferDesc.Width = width; swapChainDesc.BufferDesc.Height = height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hWnd; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = TRUE; // Create the D3D device and the swap chain HRESULT hr = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_REFERENCE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &pSwapChain, &pD3DDevice); // Error checking. Make sure the device was created if (hr != S_OK) { return false; } // Get the back buffer from the swapchain ID3D10Texture2D * pBackBuffer; hr = pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer); if (hr != S_OK) { return false; } // create the render target view hr = pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView); // release the back buffer pBackBuffer->Release(); // Make sure the render target view was created successfully if (hr != S_OK) { return false; } // set the render target pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL); // create and set the viewport D3D10_VIEWPORT viewport; viewport.Width = width; viewport.Height = height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; viewport.TopLeftX = 0; viewport.TopLeftY = 0; pD3DDevice->RSSetViewports(1, &viewport); return true; } /******************************************************************* * ShutdownDirect3D * Closes down and releases the resources for Direct3D 34 Chapter 2 n Your First DirectX Program * Inputs - void * Outputs - void *******************************************************************/ void ShutDownDirect3D() { // release the rendertarget if(pRenderTargetView) { pRenderTargetView->Release(); } // release the swapchain if(pSwapChain) { pSwapChain->Release(); } // release the D3D Device if(pD3DDevice) { pD3DDevice->Release(); } } /******************************************************************* * Render * All drawing happens in the Render function * Inputs - void * Outputs - void *******************************************************************/ void Render() { if (pD3DDevice != NULL) { // clear the target buffer pD3DDevice->ClearRenderTargetView(pRenderTargetView, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f)); // All drawing will go here. // display the next item in the swap chain pSwapChain->Present(0, 0); } }
It's a problem of linkage, did you add the right library in your visual studio ?
Add D3D10.lib to the list of linker dependencies.
What about your include folder in your hard drive? You need to go to that through your includes and also added in lib/86 to library dependances. Follow the steps in this link: http://www.rastertek.com/dx10tut01.html This will take you through the process of setting up DirectX. Worst happens and you do it and you still get the same problem, you know its not a linker issue. Which 4 people have now told you it is. Also this site is great for taking you through DirectX as well. I highly advise.
Looking for code samples for Direct3D tessellation
I am trying to learn how to use the Direct3D function D3DXTessellateRectPatch: msdn.microsoft.com/en-us/library/bb205471(v=VS.85).aspx I have found the MSDN documentation quite useful and have been able to implement tessellation of a single rectangle patch. I am now trying to tesselate a mesh that consists of thirty two bicubic Bezier 3D patches (the Utah teapot). I have tried a simple approach - tesselate each Bezier curve individually, then join the vertices and indices appropriately, taking into account vertex offsets, to create a tessellated merged mesh. However, this does not quite seem to have the desired result. If anyone has hints on this problem or, even better, code samples, much appreciated. Specifically, I have checked: Www.directxtutorial.com http://www.amazon.com/Introduction-Game-Programming-Direct-9-0c/dp/1598220160/ And another Direct3D reference, as well as Google. Thank you and look forward to your advice/pointers. Yours Misha
Tim C Schroeder has been a huge help and suggested I use ID3DXPatchMesh. Here is some sample code that generates a tessellated teapot (place in file tester.cpp): // Main D3DX framework from www.directxtutorial.com (free section) #include <assert.h> #include <stdio.h> // include the basic windows header files and the Direct3D header file #include <windows.h> #include <windowsx.h> #include <d3d9.h> #include <d3dx9.h> // define the screen resolution #define SCREEN_WIDTH 800 #define SCREEN_HEIGHT 600 // include the Direct3D Library files #pragma comment (lib, "d3d9.lib") #pragma comment (lib, "d3dx9.lib") // global declarations LPDIRECT3D9 d3d; LPDIRECT3DDEVICE9 d3ddev; LPD3DXMESH mesh = NULL; // define the mesh pointer // function prototypes void initD3D(HWND hWnd); void render_frame(void); void cleanD3D(void); void init_graphics(void); struct vertex_data { D3DXVECTOR3 position; DWORD color; }; #define FVF_VERTEX_DATA (D3DFVF_XYZ | D3DFVF_DIFFUSE) // the WindowProc function prototype LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // the entry point for any Windows program int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX)); wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = "WindowClass"; RegisterClassEx(&wc); hWnd = CreateWindowEx(NULL, "WindowClass", "Our Direct3D Program", WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); initD3D(hWnd); MSG msg; while(TRUE) { while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } if(msg.message == WM_QUIT) break; render_frame(); } cleanD3D(); return msg.wParam; } // this is the main message handler for the program LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc (hWnd, message, wParam, lParam); } // this function initializes and prepares Direct3D for use void initD3D(HWND hWnd) { d3d = Direct3DCreate9(D3D_SDK_VERSION); D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hWnd; d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; d3dpp.BackBufferWidth = SCREEN_WIDTH; d3dpp.BackBufferHeight = SCREEN_HEIGHT; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev); init_graphics(); d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE); // turn off the 3D lighting d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // turn off culling d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE); // turn on the z-buffer } // this is the function used to render a single frame void render_frame(void) { d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); d3ddev->BeginScene(); d3ddev->SetFVF(FVF_VERTEX_DATA); // set the view transform D3DXMATRIX matView; // the view transform matrix D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3 (0.0f, 8.0f, 25.0f), // the camera position &D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at position &D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction d3ddev->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView // set the projection transform D3DXMATRIX matProjection; // the projection transform matrix D3DXMatrixPerspectiveFovLH(&matProjection, D3DXToRadian(45), // the horizontal field of view (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio 1.0f, // the near view-plane 100.0f); // the far view-plane d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection // set the world transform static float index = 0.0f; index+=0.03f; // an ever-increasing float value D3DXMATRIX matRotateY; // a matrix to store the rotation for each triangle D3DXMatrixRotationY(&matRotateY, index); // the rotation matrix d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY)); // set the world transform if (mesh) mesh->DrawSubset(0); d3ddev->EndScene(); d3ddev->Present(NULL, NULL, NULL, NULL); } // this is the function that cleans up Direct3D and COM void cleanD3D(void) { if (mesh) mesh->Release(); d3ddev->Release(); d3d->Release(); } #define MAX_PATCHES 1000 #define POINTS_PER_PATCH 16 int num_patches = -1; int patches[MAX_PATCHES][POINTS_PER_PATCH]; void B_patch(int ii, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) { assert(ii < MAX_PATCHES); patches[ii][0] = a-1; patches[ii][1] = b-1; patches[ii][2] = c-1; patches[ii][3] = d-1; patches[ii][4] = e-1; patches[ii][5] = f-1; patches[ii][6] = g-1; patches[ii][7] = h-1; patches[ii][8] = i-1; patches[ii][9] = j-1; patches[ii][10] = k-1; patches[ii][11] = l-1; patches[ii][12] = m-1; patches[ii][13] = n-1; patches[ii][14] = o-1; patches[ii][15] = p-1; assert(POINTS_PER_PATCH==16); } #define MAX_POINTS 1000 int num_points = -1; D3DXVECTOR3 points[MAX_POINTS]; void B_point(int ii, double x, double y, double z) { ii--; assert(ii < MAX_POINTS); points[ii].x = x; /*** Y AND Z FLIPPED ***/ points[ii].y = z; points[ii].z = y; } // BEGIN http://www.sjbaker.org/teapot/teaset.tgz /* * The file input.c -- Juhana Kouhia, jk87377#cs.tut.fi, Oct. 25, 1991 * * Load_patch(filename, patches, vertices); * char *filename; int *patches, *vertices; * A sample program to read Bezier patches in. * Returns count of patches and vertices. * User defined subroutines: * B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p); * int ii, a, b, ..., p; * Defines one Bezier patch with index number ii, * indexes to points are in a, b, c, ..., p. * B_point(ii, x, y, z); * int ii; double x, y, z; * Defines one point with index number ii. */ #include <stdio.h> // Modified to work with g++ void Load_patch(char *filename, int *patches, int *vertices) { int ii; float x,y,z; int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p; FILE *fp; if (!(fp = fopen(filename,"r"))) { fprintf(stderr,"Load_patch: Can't open %s\n",filename); exit(1); } (void)fscanf(fp,"%i\n",patches); for (ii = 0; ii < *patches; ii++) { (void)fscanf(fp,"%i, %i, %i, %i,",&a,&b,&c,&d); (void)fscanf(fp,"%i, %i, %i, %i,",&e,&f,&g,&h); (void)fscanf(fp,"%i, %i, %i, %i,",&i,&j,&k,&l); (void)fscanf(fp,"%i, %i, %i, %i\n",&m,&n,&o,&p); B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p); } (void)fscanf(fp,"%i\n",vertices); for (ii = 1; ii <= *vertices; ii++) { (void)fscanf(fp,"%f, %f, %f\n",&x,&y,&z); B_point(ii, (double)x,(double)y,(double)z); } } // END http://www.sjbaker.org/teapot/teaset.tgz // this is the function that puts the 3D models into video RAM void init_graphics(void) { // load patch char filename[255]; sprintf(filename,"teapot"); Load_patch(filename,&num_patches,&num_points); printf("Loaded patch %s with %d patches and %d vertices.\n", filename,num_patches,num_points); // create declarator from FVF D3DVERTEXELEMENT9 inDecl[MAX_FVF_DECL_SIZE]; if (!SUCCEEDED(D3DXDeclaratorFromFVF(FVF_VERTEX_DATA,inDecl))) assert(FALSE); // create patch mesh LPD3DXPATCHMESH p_mesh; D3DXPATCHINFO info; info.PatchType = D3DXPATCHMESH_RECT; info.Degree = D3DDEGREE_CUBIC; info.Basis = D3DBASIS_BEZIER; if (!SUCCEEDED(D3DXCreatePatchMesh(&info,num_patches,num_points,0,inDecl,d3ddev,&p_mesh))) assert(FALSE); assert(p_mesh->GetControlVerticesPerPatch()==POINTS_PER_PATCH); // copy vertices LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL; if (!SUCCEEDED(p_mesh->GetVertexBuffer(&v_buffer))) assert(FALSE); struct vertex_data* vertex_data = NULL; DWORD number_of_vertices=p_mesh->GetNumVertices(); assert(number_of_vertices==num_points); if (!SUCCEEDED(v_buffer->Lock(0,number_of_vertices*sizeof(struct vertex_data),(void **)&vertex_data,D3DLOCK_DISCARD))) assert(FALSE); for (int i=0; i<num_points; i++) { vertex_data[i].position.x = points[i].x; vertex_data[i].position.y = points[i].y; vertex_data[i].position.z = points[i].z; vertex_data[i].color = D3DCOLOR_XRGB(255,0,0); } v_buffer->Unlock(); v_buffer->Release(); // copy indices LPDIRECT3DINDEXBUFFER9 i_buffer = NULL; if (!SUCCEEDED(p_mesh->GetIndexBuffer(&i_buffer))) assert(FALSE); D3DINDEXBUFFER_DESC i_buffer_desc; if (!SUCCEEDED(i_buffer->GetDesc(&i_buffer_desc))) assert(FALSE); assert(i_buffer_desc.Size==num_patches*POINTS_PER_PATCH*sizeof(WORD)); WORD* index_data = NULL; if (!SUCCEEDED(i_buffer->Lock(0,0,(void **)&index_data,D3DLOCK_DISCARD))) assert(FALSE); int idx=0; for (int i=0; i<num_patches; i++) { for (int j=0; j<POINTS_PER_PATCH; j++) { index_data[idx] = patches[i][j]; idx++; } } i_buffer->Unlock(); i_buffer->Release(); // create mesh for tesselation FLOAT fTessLevel=1.0f; DWORD Adaptive=FALSE; DWORD NumTriangles,NumVertices; if (!SUCCEEDED(p_mesh->GetTessSize(fTessLevel,Adaptive,&NumTriangles,&NumVertices))) assert(FALSE); if (!SUCCEEDED(D3DXCreateMeshFVF(NumTriangles,NumVertices,D3DXMESH_MANAGED,FVF_VERTEX_DATA,d3ddev,&mesh))) assert(FALSE); // tesselate assert(Adaptive==FALSE); if (!SUCCEEDED(p_mesh->Tessellate(fTessLevel,mesh))) assert(FALSE); printf("Generated tesselated mesh with %d triangles, %d vertices\n",NumTriangles,NumVertices); p_mesh->Release(); } The teapot data (place in file teapot) is (from http://www.sjbaker.org/teapot/teaset.tgz): 32 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28 19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40 31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13 13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60 16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69 28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78 40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57 57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96 60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105 69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114 78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93 121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136 124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133 133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155 136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177 165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174 174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197 177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194 204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215 204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222 204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229 204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212 212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245 215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254 222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263 229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242 270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274 270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285 270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294 270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271 306 1.4,0.0,2.4 1.4,-0.784,2.4 0.784,-1.4,2.4 0.0,-1.4,2.4 1.3375,0.0,2.53125 1.3375,-0.749,2.53125 0.749,-1.3375,2.53125 0.0,-1.3375,2.53125 1.4375,0.0,2.53125 1.4375,-0.805,2.53125 0.805,-1.4375,2.53125 0.0,-1.4375,2.53125 1.5,0.0,2.4 1.5,-0.84,2.4 0.84,-1.5,2.4 0.0,-1.5,2.4 -0.784,-1.4,2.4 -1.4,-0.784,2.4 -1.4,0.0,2.4 -0.749,-1.3375,2.53125 -1.3375,-0.749,2.53125 -1.3375,0.0,2.53125 -0.805,-1.4375,2.53125 -1.4375,-0.805,2.53125 -1.4375,0.0,2.53125 -0.84,-1.5,2.4 -1.5,-0.84,2.4 -1.5,0.0,2.4 -1.4,0.784,2.4 -0.784,1.4,2.4 0.0,1.4,2.4 -1.3375,0.749,2.53125 -0.749,1.3375,2.53125 0.0,1.3375,2.53125 -1.4375,0.805,2.53125 -0.805,1.4375,2.53125 0.0,1.4375,2.53125 -1.5,0.84,2.4 -0.84,1.5,2.4 0.0,1.5,2.4 0.784,1.4,2.4 1.4,0.784,2.4 0.749,1.3375,2.53125 1.3375,0.749,2.53125 0.805,1.4375,2.53125 1.4375,0.805,2.53125 0.84,1.5,2.4 1.5,0.84,2.4 1.75,0.0,1.875 1.75,-0.98,1.875 0.98,-1.75,1.875 0.0,-1.75,1.875 2.0,0.0,1.35 2.0,-1.12,1.35 1.12,-2.0,1.35 0.0,-2.0,1.35 2.0,0.0,0.9 2.0,-1.12,0.9 1.12,-2.0,0.9 0.0,-2.0,0.9 -0.98,-1.75,1.875 -1.75,-0.98,1.875 -1.75,0.0,1.875 -1.12,-2.0,1.35 -2.0,-1.12,1.35 -2.0,0.0,1.35 -1.12,-2.0,0.9 -2.0,-1.12,0.9 -2.0,0.0,0.9 -1.75,0.98,1.875 -0.98,1.75,1.875 0.0,1.75,1.875 -2.0,1.12,1.35 -1.12,2.0,1.35 0.0,2.0,1.35 -2.0,1.12,0.9 -1.12,2.0,0.9 0.0,2.0,0.9 0.98,1.75,1.875 1.75,0.98,1.875 1.12,2.0,1.35 2.0,1.12,1.35 1.12,2.0,0.9 2.0,1.12,0.9 2.0,0.0,0.45 2.0,-1.12,0.45 1.12,-2.0,0.45 0.0,-2.0,0.45 1.5,0.0,0.225 1.5,-0.84,0.225 0.84,-1.5,0.225 0.0,-1.5,0.225 1.5,0.0,0.15 1.5,-0.84,0.15 0.84,-1.5,0.15 0.0,-1.5,0.15 -1.12,-2.0,0.45 -2.0,-1.12,0.45 -2.0,0.0,0.45 -0.84,-1.5,0.225 -1.5,-0.84,0.225 -1.5,0.0,0.225 -0.84,-1.5,0.15 -1.5,-0.84,0.15 -1.5,0.0,0.15 -2.0,1.12,0.45 -1.12,2.0,0.45 0.0,2.0,0.45 -1.5,0.84,0.225 -0.84,1.5,0.225 0.0,1.5,0.225 -1.5,0.84,0.15 -0.84,1.5,0.15 0.0,1.5,0.15 1.12,2.0,0.45 2.0,1.12,0.45 0.84,1.5,0.225 1.5,0.84,0.225 0.84,1.5,0.15 1.5,0.84,0.15 -1.6,0.0,2.025 -1.6,-0.3,2.025 -1.5,-0.3,2.25 -1.5,0.0,2.25 -2.3,0.0,2.025 -2.3,-0.3,2.025 -2.5,-0.3,2.25 -2.5,0.0,2.25 -2.7,0.0,2.025 -2.7,-0.3,2.025 -3.0,-0.3,2.25 -3.0,0.0,2.25 -2.7,0.0,1.8 -2.7,-0.3,1.8 -3.0,-0.3,1.8 -3.0,0.0,1.8 -1.5,0.3,2.25 -1.6,0.3,2.025 -2.5,0.3,2.25 -2.3,0.3,2.025 -3.0,0.3,2.25 -2.7,0.3,2.025 -3.0,0.3,1.8 -2.7,0.3,1.8 -2.7,0.0,1.575 -2.7,-0.3,1.575 -3.0,-0.3,1.35 -3.0,0.0,1.35 -2.5,0.0,1.125 -2.5,-0.3,1.125 -2.65,-0.3,0.9375 -2.65,0.0,0.9375 -2.0,-0.3,0.9 -1.9,-0.3,0.6 -1.9,0.0,0.6 -3.0,0.3,1.35 -2.7,0.3,1.575 -2.65,0.3,0.9375 -2.5,0.3,1.125 -1.9,0.3,0.6 -2.0,0.3,0.9 1.7,0.0,1.425 1.7,-0.66,1.425 1.7,-0.66,0.6 1.7,0.0,0.6 2.6,0.0,1.425 2.6,-0.66,1.425 3.1,-0.66,0.825 3.1,0.0,0.825 2.3,0.0,2.1 2.3,-0.25,2.1 2.4,-0.25,2.025 2.4,0.0,2.025 2.7,0.0,2.4 2.7,-0.25,2.4 3.3,-0.25,2.4 3.3,0.0,2.4 1.7,0.66,0.6 1.7,0.66,1.425 3.1,0.66,0.825 2.6,0.66,1.425 2.4,0.25,2.025 2.3,0.25,2.1 3.3,0.25,2.4 2.7,0.25,2.4 2.8,0.0,2.475 2.8,-0.25,2.475 3.525,-0.25,2.49375 3.525,0.0,2.49375 2.9,0.0,2.475 2.9,-0.15,2.475 3.45,-0.15,2.5125 3.45,0.0,2.5125 2.8,0.0,2.4 2.8,-0.15,2.4 3.2,-0.15,2.4 3.2,0.0,2.4 3.525,0.25,2.49375 2.8,0.25,2.475 3.45,0.15,2.5125 2.9,0.15,2.475 3.2,0.15,2.4 2.8,0.15,2.4 0.0,0.0,3.15 0.0,-0.002,3.15 0.002,0.0,3.15 0.8,0.0,3.15 0.8,-0.45,3.15 0.45,-0.8,3.15 0.0,-0.8,3.15 0.0,0.0,2.85 0.2,0.0,2.7 0.2,-0.112,2.7 0.112,-0.2,2.7 0.0,-0.2,2.7 -0.002,0.0,3.15 -0.45,-0.8,3.15 -0.8,-0.45,3.15 -0.8,0.0,3.15 -0.112,-0.2,2.7 -0.2,-0.112,2.7 -0.2,0.0,2.7 0.0,0.002,3.15 -0.8,0.45,3.15 -0.45,0.8,3.15 0.0,0.8,3.15 -0.2,0.112,2.7 -0.112,0.2,2.7 0.0,0.2,2.7 0.45,0.8,3.15 0.8,0.45,3.15 0.112,0.2,2.7 0.2,0.112,2.7 0.4,0.0,2.55 0.4,-0.224,2.55 0.224,-0.4,2.55 0.0,-0.4,2.55 1.3,0.0,2.55 1.3,-0.728,2.55 0.728,-1.3,2.55 0.0,-1.3,2.55 1.3,0.0,2.4 1.3,-0.728,2.4 0.728,-1.3,2.4 0.0,-1.3,2.4 -0.224,-0.4,2.55 -0.4,-0.224,2.55 -0.4,0.0,2.55 -0.728,-1.3,2.55 -1.3,-0.728,2.55 -1.3,0.0,2.55 -0.728,-1.3,2.4 -1.3,-0.728,2.4 -1.3,0.0,2.4 -0.4,0.224,2.55 -0.224,0.4,2.55 0.0,0.4,2.55 -1.3,0.728,2.55 -0.728,1.3,2.55 0.0,1.3,2.55 -1.3,0.728,2.4 -0.728,1.3,2.4 0.0,1.3,2.4 0.224,0.4,2.55 0.4,0.224,2.55 0.728,1.3,2.55 1.3,0.728,2.55 0.728,1.3,2.4 1.3,0.728,2.4 0.0,0.0,0.0 1.5,0.0,0.15 1.5,0.84,0.15 0.84,1.5,0.15 0.0,1.5,0.15 1.5,0.0,0.075 1.5,0.84,0.075 0.84,1.5,0.075 0.0,1.5,0.075 1.425,0.0,0.0 1.425,0.798,0.0 0.798,1.425,0.0 0.0,1.425,0.0 -0.84,1.5,0.15 -1.5,0.84,0.15 -1.5,0.0,0.15 -0.84,1.5,0.075 -1.5,0.84,0.075 -1.5,0.0,0.075 -0.798,1.425,0.0 -1.425,0.798,0.0 -1.425,0.0,0.0 -1.5,-0.84,0.15 -0.84,-1.5,0.15 0.0,-1.5,0.15 -1.5,-0.84,0.075 -0.84,-1.5,0.075 0.0,-1.5,0.075 -1.425,-0.798,0.0 -0.798,-1.425,0.0 0.0,-1.425,0.0 0.84,-1.5,0.15 1.5,-0.84,0.15 0.84,-1.5,0.075 1.5,-0.84,0.075 0.798,-1.425,0.0 1.425,-0.798,0.0 Finally, to compile using mingw on Ubuntu 10.04 amd64 with proper software installed: #!/bin/bash rm tester.exe > /dev/null 2>&1 i586-mingw32msvc-g++ tester.cpp -o tester.exe -fcheck-new -Idxsdk/DXSDK/Include -ld3d9 dxsdk/DXSDK/Lib/x86/d3dx9.lib
Performance difference between DrawLine and DrawLines?
Im using GDI+ in C++ to draw a chart control. I want to know if there is any performance difference between the above 2 functions. I am not lazy to write code for DrawLines() but it is that doing so makes my code complex. So im weighin the chances of whether to make code execution faster at the expense of reducing readability and potentially increasing errors and bugs. Any help wud be appreciated. Eraj.
There shouldn't be a significant difference between the two for most drawing activities, but to be sure, I wrote up a test project to compare the difference between them (well, actually 3 of them). For a very large number of lines (x25000) on my machine, DrawLines() (640ms) was about 50% faster over DrawLine() (420ms). To be honest here, I also misread the question the first time around and wrote my initial test in C#. Performance was about the same between the two, which is to be expected as .NET Graphics are based upon GDI+. Just out of curiosity, I tried regular GDI, which I expect would be faster. Using the win32 PolyLine() (530ms) function was about 20% faster, with 45000 lines. This is 116% faster than using GDI+ DrawLines(). Even more stunning, perhaps, is that using win32 LineTo() instead of GDI+ DrawLine() results in times under 125ms. With an assumed time of 125ms and 45000 lines, this method is at least 800% faster. (Timer resolution and thread timing make it difficult to measure performance in this threshold without resorting to QueryPerformanceCounter and other timing methods of higher frequency.) However, I should caution against making the assumption that this is a significant bottleneck in drawing code. Many of the performance improvements that can be made will have nothing to do with what objects have to be drawn. I would guess that your requirements will probably dictate that a few hundred items may need to be drawn in normal operation for your control. In that case, I would recommend you write your drawing code to be as straightforward and bug-free as you can, as debugging drawing issues can be an expensive use of time and potentially less beneficial as improving the rest of your control or your application. Also, if you need to actively update thousands of items, you will see much higher performance gains by moving to a back-buffered solution. This should also make it easier to develop code to draw your control, aside from managing the off-screen buffer. Here are my source code examples. Each of them handles mouse clicks to alternate between using bulk drawing versus itemized drawing. GDI+, hosted in a barebones MFC SDI App This assumes that someone has already declared GDI+ headers and written code to initialize/teardown GDI+. In ChildView.h // Attributes public: bool m_bCompositeMode; // Operations public: void RedrawScene(Graphics &g, int lineCount, int width, int height); PointF *CreatePoints(int lineCount, int width, int height); void ReportTime(Graphics &g, int lineCount, DWORD tickSpan); public: afx_msg void OnLButtonUp(UINT nFlags, CPoint point); In ChildView.cpp, added to PreCreateWindow() m_bCompositeMode = false; Remainder of ChildView.cpp, including OnPaint() and Message Map changes. BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_LBUTTONUP() END_MESSAGE_MAP() void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting RECT rcClient; ::GetClientRect(this->GetSafeHwnd(), &rcClient); Graphics g(dc.GetSafeHdc()); g.Clear(Color(0, 0, 0)); RedrawScene(g, 25000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); } void CChildView::RedrawScene(Graphics &g, int lineCount, int width, int height) { DWORD tickStart = 0; DWORD tickEnd = 0; Pen p(Color(0, 0, 0x7F)); PointF *pts = CreatePoints(lineCount, width, height); tickStart = GetTickCount(); if (m_bCompositeMode) { g.DrawLines(&p, pts, lineCount); } else { int i = 0; int imax = lineCount - 1; for (i = 0; i < imax; i++) { g.DrawLine(&p, pts[i], pts[i + 1]); } } tickEnd = GetTickCount(); delete[] pts; ReportTime(g, lineCount, tickEnd - tickStart); } void CChildView::ReportTime(Graphics &g, int lineCount, DWORD tickSpan) { CString strDisp; if(m_bCompositeMode) { strDisp.Format(_T("Graphics::DrawLines(Pen *, PointF *, INT) x%d took %dms"), lineCount, tickSpan); } else { strDisp.Format(_T("Graphics::DrawLine(Pen *, PointF, PointF) x%d took %dms"), lineCount, tickSpan); } // Note: sloppy, but simple. Font font(L"Arial", 14.0f); PointF ptOrigin(0.0f, 0.0f); SolidBrush br(Color(255, 255, 255)); Status s = g.DrawString(strDisp, -1, &font, ptOrigin, &br); } PointF* CChildView::CreatePoints(int lineCount, int width, int height) { if(lineCount <= 0) { PointF *ptEmpty = new PointF[2]; ptEmpty[0].X = 0; ptEmpty[0].Y = 0; ptEmpty[1].X = 0; ptEmpty[1].Y = 0; return ptEmpty; } PointF *pts = new PointF[lineCount + 1]; int i = 1; while(i < lineCount) { pts[i].X = (float)(rand() % width); pts[i].Y = (float)(rand() % height); i++; } return pts; } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { m_bCompositeMode = !m_bCompositeMode; this->Invalidate(); CWnd::OnLButtonUp(nFlags, point); } C#.NET, hosted in a basebones WinForms App, with default class Form1 Set a default size for the form, equal to the size of the MFC version if you are comparing the two. A size-change handler could be added as well. public Form1() { InitializeComponent(); bCompositeMode = false; } bool bCompositeMode; private void Form1_Paint(object sender, PaintEventArgs e) { e.Graphics.Clear(Color.Black); RedrawScene(e.Graphics, 25000, this.ClientRectangle.Width, this.ClientRectangle.Height); } private void RedrawScene(Graphics g, int lineCount, int width, int height) { DateTime dtStart = DateTime.MinValue; DateTime dtEnd = DateTime.MinValue; using (Pen p = new Pen(Color.Navy)) { Point[] pts = CreatePoints(lineCount, width, height); dtStart = DateTime.Now; if (bCompositeMode) { g.DrawLines(p, pts); } else { int i = 0; int imax = pts.Length - 1; for (i = 0; i < imax; i++) { g.DrawLine(p, pts[i], pts[i + 1]); } } dtEnd = DateTime.Now; } ReportTime(g, lineCount, dtEnd - dtStart); } private void ReportTime(Graphics g, int lineCount, TimeSpan ts) { string strDisp = null; if (bCompositeMode) { strDisp = string.Format("DrawLines(Pen, Point[]) x{0} took {1}ms", lineCount, ts.Milliseconds); } else { strDisp = string.Format("DrawLine(Pen, Point, Point) x{0} took {1}ms", lineCount, ts.Milliseconds); } // Note: sloppy, but simple. using (Font font = new Font(FontFamily.GenericSansSerif, 14.0f, FontStyle.Regular)) { g.DrawString(strDisp, font, Brushes.White, 0.0f, 0.0f); } } private Point[] CreatePoints(int count, int width, int height) { Random rnd = new Random(); if (count <= 0) { return new Point[] { new Point(0,0), new Point(0,0)}; } Point[] pts = new Point[count + 1]; pts[0] = new Point(0, 0); int i = 1; while (i <= count) { pts[i] = new Point(rnd.Next(width), rnd.Next(height)); i++; } return pts; } private void Form1_Click(object sender, EventArgs e) { bCompositeMode = !bCompositeMode; Invalidate(); } Regular GDI, hosted in a barebones MFC SDI App In ChildView.h // Attributes public: bool m_bCompositeMode; // Operations public: void RedrawScene(HDC hdc, int lineCount, int width, int height); POINT *CreatePoints(int lineCount, int width, int height); void ReportTime(HDC hdc, int lineCount, DWORD tickSpan); public: afx_msg void OnLButtonUp(UINT nFlags, CPoint point); In ChildView.cpp Update PreCreateWindow() just as in the GDI+ sample. BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_LBUTTONUP() END_MESSAGE_MAP() void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting HDC hdc = dc.GetSafeHdc(); HBRUSH brClear = (HBRUSH)::GetStockObject(BLACK_BRUSH); RECT rcClient; ::GetClientRect(this->m_hWnd, &rcClient); ::FillRect(hdc, &rcClient, brClear); ::DeleteObject(brClear); RedrawScene(hdc, 45000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); } void CChildView::RedrawScene(HDC hdc, int lineCount, int width, int height) { DWORD tickStart = 0; DWORD tickEnd = 0; HPEN p = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0x7F)); POINT *pts = CreatePoints(lineCount, width, height); HGDIOBJ prevPen = SelectObject(hdc, p); tickStart = GetTickCount(); if(m_bCompositeMode) { ::Polyline(hdc, pts, lineCount); } else { ::MoveToEx(hdc, pts[0].x, pts[0].y, &(pts[0])); int i = 0; int imax = lineCount; for(i = 1; i < imax; i++) { ::LineTo(hdc, pts[i].x, pts[i].y); } } tickEnd = GetTickCount(); ::SelectObject(hdc, prevPen); delete pts; ::DeleteObject(p); ReportTime(hdc, lineCount, tickEnd - tickStart); } POINT *CChildView::CreatePoints(int lineCount, int width, int height) { if(lineCount <= 0) { POINT *ptEmpty = new POINT[2]; memset(&ptEmpty, 0, sizeof(POINT) * 2); return ptEmpty; } POINT *pts = new POINT[lineCount + 1]; int i = 1; while(i < lineCount) { pts[i].x = rand() % width; pts[i].y = rand() % height; i++; } return pts; } void CChildView::ReportTime(HDC hdc, int lineCount, DWORD tickSpan) { CString strDisp; if(m_bCompositeMode) { strDisp.Format(_T("PolyLine(HDC, POINT *, int) x%d took %dms"), lineCount, tickSpan); } else { strDisp.Format(_T("LineTo(HDC, HPEN, int, int) x%d took %dms"), lineCount, tickSpan); } HFONT font = (HFONT)::GetStockObject(SYSTEM_FONT); HFONT fontPrev = (HFONT)::SelectObject(hdc, font); RECT rcClient; ::GetClientRect(this->m_hWnd, &rcClient); ::ExtTextOut(hdc, 0, 0, ETO_CLIPPED, &rcClient, strDisp.GetString(), strDisp.GetLength(), NULL); ::SelectObject(hdc, fontPrev); ::DeleteObject(font); } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { m_bCompositeMode = !m_bCompositeMode; this->Invalidate(); CWnd::OnLButtonUp(nFlags, point); }