2016年第一天,新年快乐!!!
由于一直跟视频这块打交道,对于图像编解码和显示等方面都有所研究。最近遇到一个性能要求比较高的应用,原本的GDI绘图导致到线程负荷比较重,造成整个系统不稳定,因而生了要用Direct2D来取代的念头。经过一番研究后发现,Direct2D原来是这么的简单方便,而效率也比GDI有了显著的提升。废话不多说,下面还是直接上代码吧。
本人一开始是在网上搜的一份代码,后来改了很多才将代码调好,并修复了一些内存泄露的bug,所以如有雷同绝非偶然,但不雷同之处便是其中要紧之处。
主要的代码都封装成了BaseFactory类,其代码如下:
BaseFactory.h
#pragma once
// 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
//#include
/******************************************************************
* *
* Macros *
* *
******************************************************************/
template
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;
private:
float scale;//缩放比例
RECT rc;
HWND m_hwnd;
ID2D1Factory* m_pDirect2dFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1Bitmap* m_pBitmap;
};
#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);
scale = (GetDeviceCaps(GetDC(m_hwnd), LOGPIXELSX))/(float)100/0.96;//获得缩放比例
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;
m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);//设置图像为抗锯齿模式
m_pRenderTarget->CreateBitmap(imgsize, nullptr, pitch, &prop, &m_pBitmap);
imgrect.left = 0;
imgrect.right = imgwidth;
imgrect.top = 0;
imgrect.bottom = imgheight;
}
return hr;
}
void BaseFactory::DiscardDeviceResources()
{
SafeRelease(&m_pRenderTarget);
m_pDirect2dFactory->Release();
m_pBitmap->Release();
}
void BaseFactory::OnRender(char *data)//绘制图形到指定控件中
{
m_pRenderTarget->BeginDraw();//跟显示刷新频率有关系
m_pBitmap->CopyFromMemory(&imgrect, data, imgwidth * 4);
float fWidth = rc.right - rc.left;
float fHeight = rc.bottom - rc.top;
m_pRenderTarget->DrawBitmap(m_pBitmap, D2D1::RectF(0, 0, fWidth/ scale, fHeight/ scale));//该矩形大小会受到"更改文本、应用和其他项目的大小:xxx%"的影响
m_pRenderTarget->EndDraw();
}
完成了上面的工作,在需要用到的地方加上下面代码即可,代码中调用了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;
另外,可以通过如下的方式对视频数据进行截取:
D2D1_POINT_2U destPoint1 = D2D1::Point2U(0, 0);
D2D1_POINT_2U destPoint2 = D2D1::Point2U(imgwidth / 2, 0);
D2D1_RECT_U sreRect1 = D2D1::RectU(imgwidth / 2, 0, imgwidth-1, imgheight);
D2D1_RECT_U sreRect2 = D2D1::RectU(0, 0, imgwidth/2, imgheight);
m_pCopyBitmap->CopyFromBitmap(&destPoint1, m_pBitmap, &sreRect1);
m_pCopyBitmap->CopyFromBitmap(&destPoint2, m_pBitmap, &sreRect2);
可以通过如下代码添加和使用纯色背景画刷:
//对象纯背景色画刷
ID2D1SolidColorBrush *pGridBrush = NULL;
//创建纯色画刷
hr=m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(RGB(70, 70, 70)),&pGridBrush);
//绘制纯色背景
m_pRenderTarget->FillRectangle(D2D1::RectF(rc.left,rc.top,rc.right,rc.bottom), pGridBrush);
//释放纯色画刷对象
pGridBrush->Release();
pGridBrush=NULL;
可以通过如下代码绘制文字到界面上,当然在这之前需要添加如下的三个链接库dxgi.lib、dxguid.lib、dwrite.lib。
ID2D1SolidColorBrush *pWhiteBrush = NULL;//纯色画刷
IDWriteFactory *pDWriteFactory;//文字工厂对象
IDWriteTextFormat *pTextFormat;//文字模板对象
//创建文字工厂
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(pDWriteFactory), reinterpret_cast(&pDWriteFactory));
//创建文字模板
hr = pDWriteFactory->CreateTextFormat(L"微软雅黑", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,24.0f*96.0f / 72.0f, L"en-US", &pTextFormat);
//创建纯色画刷
hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(RGB(255, 255, 255)), &pWhiteBrush);
//绘制文字
m_pRenderTarget->DrawTextW(showresolution, wcslen(showresolution), pTextFormat, D2D1::RectF(rc.right - rc.left - 360, 0, rc.right - rc.left, 40), pWhiteBrush);//绘制文字信息,showresolution为文字内容,pWhiteBrush为前画刷,也就是文字的颜色
可以设置文字的水平和垂直居中
//水平居中
pPointTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
//垂直居中
pPointTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
如果只想显示图像中的一个部分,可以做如下操作:
m_pRenderTarget->BeginDraw();//跟显示刷新频率有关系
m_pBitmap1->CopyFromMemory(&imgrect, data1, imgwidth * 4);
float fWidth = rc.right - rc.left;
float fHeight = rc.bottom - rc.top;
const D2D1_RECT_F destinationrect1 = D2D1::RectF(0, 80, fWidth / scale, fHeight / scale / 2-10);
const D2D1_RECT_F destinationrect2 = D2D1::RectF(0, 10+fHeight / scale / 2, fWidth / scale, fHeight / scale-80);
const D2D1_RECT_F srcrect1 = D2D1::RectF(0, 0, 50, 100);//这里似乎是从0~100取百分比换算过来的,而不是去实际图像的大小
const D2D1_RECT_F srcrect2 = D2D1::RectF(50, 0, 100, 100);
//绘制图像
m_pRenderTarget->DrawBitmap(m_pBitmap1,&destinationrect1,1, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,&srcrect1);
m_pRenderTarget->DrawBitmap(m_pBitmap1, &destinationrect2, 1, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, &srcrect2);
m_pRenderTarget->EndDraw();