Qt集成Direct2D绘制,实现离屏渲染
没搜到关于Qt中使用Direct2D的方式,想了个办法,在此做个记录。
需要引入这两个库:
代码:
#pragma once
#include <QWidget>
#include <QImage>
#include <QPainter>
#include <QMouseEvent>
#include "d2d1.h"
#include "wincodec.h"
#include "dwrite.h"
#define SAFE_RELEASE(P) if(P){P->Release(); P = nullptr;}
#define START_RECORD_TIME(name) auto s_##name = std::chrono::high_resolution_clock::now();
#define END_RECORD_TIME_MS(name) auto e_##name = std::chrono::high_resolution_clock::now(); \
auto du_##name = std::chrono::duration_cast<std::chrono::milliseconds>(e_##name - s_##name).count(); \
OutputDebugStringA(std::string(std::string(__FUNCTION__) + " " + std::to_string(__LINE__) + " : " + #name + " " + std::to_string(du_##name) + " ms\n").c_str()); \
#define END_RECORD_TIME_US(name) auto e_##name = std::chrono::high_resolution_clock::now(); \
auto du_##name = std::chrono::duration_cast<std::chrono::microseconds>(e_##name - s_##name).count(); \
OutputDebugStringA(std::string(std::string(__FUNCTION__) + " " + std::to_string(__LINE__) + " : " + #name + " " + std::to_string(du_##name) + " us\n").c_str()); \
class CanvasWidget :public QWidget
{
public:
CanvasWidget(QWidget* parent = nullptr);
~CanvasWidget();
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void CreateD2DInit();
void Draw();
void Cleanup();
void TransImage();
HWND hWnd;
ID2D1Factory* pD2DFactory = nullptr;
ID2D1RenderTarget* pRenderTarget = nullptr;
ID2D1SolidColorBrush* pBrush = nullptr;
IDWriteFactory* pDWFactory = nullptr;
IDWriteTextFormat* pTextFormat = nullptr;
IWICImagingFactory* pWICFactory = nullptr;
IWICBitmap* pWICBitmap = nullptr;
QImage* pBufferImage = nullptr;
std::vector<std::vector<int>> rects;
std::vector<D2D1::ColorF> colors;
int count = 100;
int wh = 8;
int drawStartX = 0;
int drawStartY = 0;
QPoint pressPoint;
bool pressFlag = false;
};
#include "CanvasWidget.h"
#include <chrono>
#include <random>
CanvasWidget::CanvasWidget(QWidget* parent)
{
hWnd = (HWND)this->winId();
std::random_device rd;
std::uniform_int_distribution<int> dist(0, 255);
for (int i = 0; i < count; i++)
{
for (int j = 0; j < count; j++)
{
std::vector<int> rect;
rect.push_back(j * wh);
rect.push_back(i * wh);
rects.push_back(rect);
colors.push_back(D2D1::ColorF(dist(rd) / 255.0, dist(rd) / 255.0, dist(rd) / 255.0));
}
}
Draw();
}
CanvasWidget::~CanvasWidget()
{
Cleanup();
}
void CanvasWidget::mousePressEvent(QMouseEvent* event)
{
pressFlag = true;
pressPoint = event->pos();
QWidget::mousePressEvent(event);
}
void CanvasWidget::mouseReleaseEvent(QMouseEvent* event)
{
pressFlag = false;
QWidget::mouseReleaseEvent(event);
}
void CanvasWidget::mouseMoveEvent(QMouseEvent* event)
{
if (pressFlag)
{
QPoint delta = event->pos() - pressPoint;
drawStartX += delta.x();
drawStartY += delta.y();
pressPoint = event->pos();
this->update();
}
QWidget::mouseMoveEvent(event);
}
void CanvasWidget::paintEvent(QPaintEvent* event)
{
if (pBufferImage != nullptr)
{
START_RECORD_TIME(drawImage)
QPainter painter(this);
painter.drawImage(drawStartX, drawStartY, *pBufferImage);
END_RECORD_TIME_MS(drawImage)
}
QWidget::paintEvent(event);
}
void CanvasWidget::CreateD2DInit()
{
RECT rc;
GetClientRect(hWnd, &rc);
HRESULT hr;
// 创建WIC工厂
hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pWICFactory));
if (SUCCEEDED(hr))
{
// 创建WIC Bitmap
hr = pWICFactory->CreateBitmap(rc.right - rc.left, rc.bottom - rc.top, GUID_WICPixelFormat32bppPRGBA, WICBitmapCacheOnLoad, &pWICBitmap);
}
// 创建Direct2D工厂
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
if (FAILED(hr))
{
MessageBox(hWnd, L"Create D2D factory failed!", L"Error", 0);
return;
}
// 创建Render Target
hr = pD2DFactory->CreateWicBitmapRenderTarget(pWICBitmap, D2D1::RenderTargetProperties(), &pRenderTarget);
if (FAILED(hr))
{
MessageBox(hWnd, L"Create render target failed!", L"Error", 0);
return;
}
// 创建一个画刷
hr = pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &pBrush);
if (FAILED(hr))
{
MessageBox(hWnd, L"Create brush failed!", L"Error", 0);
return;
}
// 创建DirectWrite工厂
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWFactory));
if (FAILED(hr))
{
MessageBox(hWnd, L"Create DirectWrite factory failed!", L"Error", 0);
return;
}
// 创建文本格式
hr = pDWFactory->CreateTextFormat(L"Gabriola", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 50.0f, L"en-us", &pTextFormat);
if (FAILED(hr))
{
MessageBox(hWnd, L"Create IDWriteTextFormat failed!", L"Error", 0);
return;
}
}
void CanvasWidget::Draw()
{
CreateD2DInit(); // 创建初始化一下
START_RECORD_TIME(all)
pRenderTarget->BeginDraw(); // 开始绘画 绘画一定要在 BeginDraw 和 EndDraw 之间
/*************************************************/
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::YellowGreen));
int i = 0;
for (auto& p : rects)
{
pBrush->SetColor(colors[i]);
pRenderTarget->FillRectangle(D2D1::RectF(p[0], p[1], p[0] + wh, p[1] + wh), pBrush);
pRenderTarget->DrawRectangle(D2D1::RectF(p[0], p[1], p[0] + wh, p[1] + wh), pBrush, 1.0f);
i++;
}
pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White));
pRenderTarget->DrawTextW(L"Hello World !", std::strlen("Hello World !"), pTextFormat, D2D1::RectF(0, 0, 640, 480), pBrush);
/*************************************************/
HRESULT hr = pRenderTarget->EndDraw(); // 结束绘画
if (FAILED(hr))
{
MessageBox(nullptr, L"Draw failed!", L"Error", 0);
return;
}
END_RECORD_TIME_MS(all)
TransImage();
}
void CanvasWidget::Cleanup()
{
SAFE_RELEASE(pRenderTarget);
SAFE_RELEASE(pBrush);
SAFE_RELEASE(pD2DFactory);
}
void CanvasWidget::TransImage()
{
UINT wicWidth, wicHeight;
pWICBitmap->GetSize(&wicWidth, &wicHeight);
WICPixelFormatGUID wicPixelFormat;
pWICBitmap->GetPixelFormat(&wicPixelFormat);
UINT32 pitch = wicWidth * sizeof(DWORD);
UINT32 bufferSize = pitch * wicHeight;
BYTE* buffer = new BYTE[bufferSize];
HRESULT hr;
WICRect rcLock = { 0,0,wicWidth,wicHeight };
START_RECORD_TIME(trans)
// 通过Lock获取远快于CopyPixels(30us && 1000us)
IWICBitmapLock* pLock = nullptr;
hr = pWICBitmap->Lock(&rcLock, WICBitmapLockWrite, &pLock);
UINT lockBufferSize, lockWidth, lockHeight;
BYTE* lockBuffer = nullptr;
pLock->GetSize(&lockWidth, &lockHeight);
pLock->GetDataPointer(&lockBufferSize, &lockBuffer);
pLock->Release();
pBufferImage = new QImage(lockBuffer, lockWidth, lockHeight, QImage::Format_RGBA8888);
/*hr = pWICBitmap->CopyPixels(&rcLock, pitch, bufferSize, buffer);
pBufferImage = new QImage(buffer, wicWidth, wicHeight, QImage::Format_RGBA8888);*/
END_RECORD_TIME_US(trans)
this->update();
}
运行效果:
性能:目前绘制 100*100 个填充矩形 + "Hello World !"文本
CPU:i5-1235U 1帧需要100ms