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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值