2016年第一天,新年快乐!!!
由于一直跟视频这块打交道,对于图像编解码和显示等方面都有所研究。最近遇到一个性能要求比较高的应用,原本的GDI绘图导致到线程负荷比较重,造成整个系统不稳定,因而生了要用Direct2D来取代的念头。经过一番研究后发现,Direct2D原来是这么的简单方便,而效率也比GDI有了显著的提升。废话不多说,下面还是直接上代码吧。
本人一开始是在网上搜的一份代码,后来改了很多才将代码调好,并修复了一些内存泄露的bug,所以如有雷同绝非偶然,但不雷同之处便是其中要紧之处。
主要的代码都封装成了BaseFactory类,其代码如下:
BaseFactory.h
// 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 // #pragma once // Modify the following defines if you have to target a platform prior to the ones specified below. // Refer to MSDN for the latest info on corresponding values for different platforms. #ifndef WINVER // Allow use of features specific to Windows 7 or later. #define WINVER 0x0700 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINNT // Allow use of features specific to Windows 7 or later. #define _WIN32_WINNT 0x0700 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef UNICODE #define UNICODE #endif // Exclude rarely-used items from Windows headers. #include <d2d1.h> //#include <d2d1helper.h> /****************************************************************** * * * Macros * * * ******************************************************************/ template<class Interface> inline void SafeRelease( Interface **ppInterfaceToRelease ) { if (*ppInterfaceToRelease != NULL) { (*ppInterfaceToRelease)->Release(); (*ppInterfaceToRelease) = NULL; } } #ifndef Assert #if defined( DEBUG ) || defined( _DEBUG ) #define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0) #else #define Assert(b) #endif //DEBUG || _DEBUG #endif #ifndef HINST_THISCOMPONENT EXTERN_C IMAGE_DOS_HEADER __ImageBase; #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) #endif class BaseFactory { public: BaseFactory(void); ~BaseFactory(void); bool Initialize(HWND hwnd,int width,int height); private: // Initialize device-dependent resources. HRESULT CreateDeviceResources(); // Release device-dependent resource. public: //绘图 void OnRender(char *data); void DiscardDeviceResources(); //图片信息 int imgwidth; int imgheight; D2D1_RECT_U imgrect; char *imgdata; private: RECT rc; HWND m_hwnd; ID2D1Factory* m_pDirect2dFactory; ID2D1HwndRenderTarget* m_pRenderTarget; ID2D1Bitmap* m_pBitmap; };BaseFactory.cpp
#include "stdafx.h" #include "BaseFactory.h" BaseFactory::BaseFactory(void) { } bool BaseFactory::Initialize(HWND hwnd, int width, int height) { imgheight = height; imgwidth = width; m_hwnd = hwnd; m_pRenderTarget = nullptr; m_pBitmap = nullptr; CreateDeviceResources(); return true; } BaseFactory::~BaseFactory(void) { } HRESULT BaseFactory::CreateDeviceResources() { D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory); HRESULT hr = S_OK; if (!m_pRenderTarget) { GetClientRect(m_hwnd, &rc); D2D1_SIZE_U size = D2D1::SizeU ( rc.right-rc.left, rc.bottom-rc.top ); // Create a Direct2D render target. hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),//第三个参数设置不等待垂直同步,默认垂直同步时最高刷新频率为显卡刷新频率,一般60FPS &m_pRenderTarget ); //创建位图 D2D1_SIZE_U imgsize= D2D1::SizeU(imgwidth, imgheight); D2D1_PIXEL_FORMAT pixelFormat = //位图像素格式描述 { DXGI_FORMAT_B8G8R8A8_UNORM, //该参数设置图像数据区的像素格式,现为RGBA,可根据需要改为别的格式,只是后面的数据拷贝要做相应的调整 D2D1_ALPHA_MODE_IGNORE }; D2D1_BITMAP_PROPERTIES prop = //位图具体信息描述 { pixelFormat, imgsize.width, imgsize.height }; long pitch = imgsize.width*4; imgdata = new char[imgsize.width * imgsize.height * 4]; memset(imgdata, 0, imgsize.width * imgsize.height * 4); m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);//设置图像为抗锯齿模式 m_pRenderTarget->CreateBitmap(imgsize, imgdata, pitch, &prop, &m_pBitmap); imgrect.left = 0; imgrect.right = imgwidth; imgrect.top = 0; imgrect.bottom = imgheight; } return hr; } void BaseFactory::DiscardDeviceResources() { delete[] imgdata; SafeRelease(&m_pRenderTarget); m_pDirect2dFactory->Release(); m_pBitmap->Release(); } void BaseFactory::OnRender(char *data)//绘制图形到指定控件中 { m_pRenderTarget->BeginDraw();//跟显示刷新频率有关系 m_pBitmap->CopyFromMemory(&imgrect, data, imgwidth * 4); m_pRenderTarget->DrawBitmap(m_pBitmap, D2D1::RectF(0, 0, rc.right - rc.left, rc.bottom - rc.top));//该矩形大小会受到"更改文本、应用和其他项目的大小:xxx%"的影响 m_pRenderTarget->EndDraw(); }将两个文件添加到工程,在需要的时候将 BaseFactory.h 包含进去即可。对了,由于用到了Diretc2D,所以需要将d2d1.lib给链接上去。
完成了上面的工作,在需要用到的地方加上下面代码即可,代码中调用了OpenCV库来读取并解码图片,所以给Direct2D的只是指向图像RGBA数据区的指针而已,假设不想如此,也可自作修改。
char *filename = "1.jpg";//图像路径 IplImage* img = cvLoadImage(filename); BaseFactory* m_BaseFactory = new BaseFactory();//创建Direct2D工厂对象 HWND m_hWnd = GetDlgItem(IDC_STATIC_SHOW)->GetSafeHwnd();//IDC_STATIC_SHOW为Picture Control的ID m_BaseFactory->Initialize(m_hWnd, img->width, img->height);//初始化 //将rgb转为rgba char *data = new char[img->width*img->height * 4]; for (size_t row = 0; row < img->height; row++) for (size_t col = 0; col < img->width; col++) { int point = col * 3 + row*img->width * 3; int pointrbga = col * 4 + row*img->width * 4; data[pointrbga + 0] = img->imageData[point + 0]; data[pointrbga + 1] = img->imageData[point + 1]; data[pointrbga + 2] = img->imageData[point + 2]; } int i = 480; long time = clock(); while (i--) { //将图像绘制到控件中 m_BaseFactory->OnRender(data); } printf("D2D花费时间%dms\n", clock() - time); printf("刷新频率%lf张/s\n", 480 / (double(clock() - time) / 1000)); //释放对象,回收内存 if (m_BaseFactory != NULL) { m_BaseFactory->DiscardDeviceResources(); delete m_BaseFactory; m_BaseFactory = NULL; } cvReleaseImage(&img); delete[]data;