首页 > 代码库 > D2D引擎与GDI\GDI+绘制效果对比

D2D引擎与GDI\GDI+绘制效果对比

本例主要是对比D2D和GDI在绘制文字、线条的区别,以及D2D与GDI+在绘制图片时的区别。

D2D是基于COM组件开发的,使用前的CoInitialize(NULL)是必须的;另外,GDI+的初始化GdiplusStartup()也别忘了。

废话少说,完整代码如下:

// D2DDemo.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "D2DDemo.h"
#include <D2D1.h>
#include <DWrite.h>
#pragma comment(lib, "D2D1")
#pragma comment(lib, "DWrite")
#include <atlbase.h>
#include <atlcom.h>
#include <wincodec.h>
#pragma comment(lib, "windowscodecs")
#include <GdiPlus.h>
#pragma comment(lib, "GdiPlus")
using namespace Gdiplus;


#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst;								// 当前实例
TCHAR szTitle[MAX_LOADSTRING];					// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];			// 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
BOOL				InitD2DResource();
BOOL				InitDeviceResource(HWND hWnd);
void				D2DDraw();
BOOL				OnCreate(HWND hWnd);
HRESULT				LoadBitmapFromFile(
						   ID2D1RenderTarget *pRenderTarget,
						   IWICImagingFactory *pIWICFactory,
						   PCWSTR uri,
						   UINT destinationWidth,
						   UINT destinationHeight,
						   ID2D1Bitmap **ppBitmap
						   );
//这里来定义全局变量
CComPtr<ID2D1Factory>			g_pD2d1Factory;
CComPtr<IDWriteFactory>			g_pDWriteFactory;
CComPtr<IDWriteTextFormat>		g_pDWriteFormat;
CComPtr<ID2D1HwndRenderTarget>	g_pD2D1HwndRender;
CComPtr<ID2D1GdiInteropRenderTarget>	g_pD2DGdiRender;
CComPtr<IWICImagingFactory>		g_pWicImageFactory;
CComPtr<ID2D1Bitmap>			g_pBkImage;
LOGFONT g_logFont;
HFONT	g_hTextFont	= NULL;
#define FREE_COM_PTR(x)	{ if (x) { x->Release(); x = NULL; } }

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	HRESULT hr = CoInitialize(NULL);
	ULONG_PTR ptr;
	GdiplusStartupInput	input;
	GdiplusStartup(&ptr, &input, NULL);
 	// TODO: 在此放置代码。
	MSG msg;
	HACCEL hAccelTable;
	memset(&g_logFont, 0, sizeof(LOGFONT));
	g_logFont.lfHeight	= 40;
	g_logFont.lfWidth	= 0;
	g_logFont.lfWeight	= FW_NORMAL;
	g_logFont.lfCharSet	= DEFAULT_CHARSET;
	g_logFont.lfOutPrecision	= OUT_DEFAULT_PRECIS;
	g_logFont.lfClipPrecision	= CLIP_DEFAULT_PRECIS;
	g_logFont.lfQuality			= DEFAULT_QUALITY;
	g_logFont.lfPitchAndFamily	= DEFAULT_PITCH|FF_SWISS;
	wcscpy(g_logFont.lfFaceName, L"微软雅黑");
	g_hTextFont = CreateFontIndirect(&g_logFont);
	// 初始化全局字符串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_D2DDEMO, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// 执行应用程序初始化:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D2DDEMO));

	// 主消息循环:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	if ( g_hTextFont )
	{
		DeleteObject(g_hTextFont);
		g_hTextFont = NULL;
	}
	GdiplusShutdown(ptr);
	CoUninitialize();
	return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
//  注释:
//
//    仅当希望
//    此代码与添加到 Windows 95 中的“RegisterClassEx”
//    函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
//    这样应用程序就可以获得关联的
//    “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	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, MAKEINTRESOURCE(IDI_D2DDEMO));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_D2DDEMO);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND	- 处理应用程序菜单
//  WM_PAINT	- 绘制主窗口
//  WM_DESTROY	- 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// 分析菜单选择:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_CREATE:
		OnCreate(hWnd);
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: 在此添加任意绘图代码...
		D2DDraw();
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_SIZE:
		{
			if ( g_pD2D1HwndRender )
			{
				int nWidth = LOWORD(lParam);
				int nHeight= HIWORD(lParam);
				D2D1_SIZE_U sz = D2D1::SizeU(nWidth, nHeight);
				g_pD2D1HwndRender->Resize(sz);
			}
		}
	case WM_ERASEBKGND:
		return TRUE;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

BOOL InitD2DResource()
{
	BOOL bRet = FALSE;
	ID2D1Factory*			pD2dFactory			= NULL;
	IDWriteFactory*			pDwriteFactory		= NULL;
	IDWriteTextFormat*		pDwriteTextFormat	= NULL;
	IWICImagingFactory*		pWicImgFactory		= NULL;
	try
	{
		HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2dFactory);
		if ( FAILED(hr) )
			throw L"D2D1CreateFactory error!";
		hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
			IID_IWICImagingFactory, (LPVOID*)&pWicImgFactory );
		if ( FAILED(hr) )
			throw L"CoCreateInstance error!";
		hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), 			reinterpret_cast<IUnknown**>(&pDwriteFactory));
		if ( FAILED(hr) )
			throw L"DWriteCreateFactory error!";
		hr = pDwriteFactory->CreateTextFormat(L"微软雅黑", NULL, DWRITE_FONT_WEIGHT_REGULAR, 			DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 30.0f, L"chs", &pDwriteTextFormat);
		if ( FAILED(hr) )
			throw L"CreateTextFormat error!";
		//水平居中
		pDwriteTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
		//垂直居中
		pDwriteTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
		bRet = TRUE;
	}
	catch(WCHAR* pMsg)
	{
		MessageBox(NULL, pMsg, L"InitD2DResource出错:", IDOK);
	}
	catch(...)
	{

	}
	if ( bRet )
	{
		g_pD2d1Factory		= pD2dFactory;
		g_pDWriteFactory	= pDwriteFactory;
		g_pDWriteFormat		= pDwriteTextFormat;
		g_pWicImageFactory	= pWicImgFactory;
	}
	else
	{
		FREE_COM_PTR(pD2dFactory);
		FREE_COM_PTR(pDwriteFactory);
		FREE_COM_PTR(pDwriteTextFormat);
		FREE_COM_PTR(pWicImgFactory);
	}
	return bRet;
}


BOOL InitDeviceResource(HWND hWnd)
{
	if ( g_pD2d1Factory == NULL )
		return FALSE;
	RECT rc;
	GetClientRect(hWnd, &rc);
	D2D1_SIZE_U sz = D2D1::SizeU(rc.right-rc.left, rc.bottom-rc.top);
	D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
	rtProps.usage =  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
	HRESULT hr = g_pD2d1Factory->CreateHwndRenderTarget( rtProps, 		D2D1::HwndRenderTargetProperties(hWnd, sz), &g_pD2D1HwndRender);
	if ( FAILED(hr) )
		return FALSE;
	hr = g_pD2D1HwndRender->QueryInterface(__uuidof(ID2D1GdiInteropRenderTarget), (void**)&g_pD2DGdiRender);
	return SUCCEEDED(hr);
}

void D2DDraw()
{//D2D GDI混合绘图
	if ( g_pD2D1HwndRender == NULL )
		return ;
	g_pD2D1HwndRender->BeginDraw();
	//D2D绘制
	g_pD2D1HwndRender->Clear(D2D1::ColorF(RGB(255,255,255), 1.0));
	//绘制背景位图
	if ( g_pBkImage )
	{
		D2D1_RECT_F desRc = http://www.mamicode.com/D2D1::RectF(420.0f, 0.0f, 720.0f, 200.0f);>基于COM的对象在使用完之后都应该调用Release(),当引用计数为0时,将删除这个对象。这里为了使用方便,用到了COM里的智能指针CComPtr,析构时会自动调用对象的Release方法,可以查看ATL源码。

运行图如下:

技术分享

对比效果可以明显发现,D2D绘制文字、线条(实际上调用的是DirectWrite)效果好于GDI,GDI的锯齿太明显了。在绘图方面,感觉D2D和GDI+的绘制效果差不多。但是,GDI+的性能大家都知道那叫一个渣,而且不支持硬件加速。

D2D引擎与GDI\GDI+绘制效果对比