- 前言
- D2DEngine库
- D2D_PICTURE.H
- D2D_PICTURE.CPP
- D2D_DRAW.H
- D2D_DRAW.CPP
- 后记
参考了以下大神的链接:
D3D12部分教程:GameBabyRockSun_QQ
D2D入门教程:MoonlightDesigner
此库开始于2022.7.20,一开始我发现网上很多D2D教程很繁琐,原因有像ID2D1Bitmap* Bitmap这样直接调用指针的,用完还要记得释放赋值NULL,也有在WinMain函数和回调函数调用各种函数的,导致写的代码很混乱,创建设备和渲染分不清楚。
我从去年开始想做一个封装D2D的库,先是照着主流教程做了一遍,发现各种内存错误,网上也找不到解决方法。于是项目停了一会。之后稍微学了一下D3D的部分教程,发现管理各种D2D & WIC 设备可以借助微软的COM接口省一半力。
下面的库就是将有关D2D和WIC的设备集成到一个叫 D2DEngine 的接口上,单独分出一个储存图像信息的 Picture 的接口,这样就能非常方便使用Direct2D绘制图像了。
代码可以全部复制使用,放到一个项目上,推荐在Visual Studio系列食用,效果更佳,如图:
D2D_PICTURE.H另外,第一次编译出现错误:“非常量引用的初始值必须为左值” 的解决方法
可以参考我的D2DEngine第一篇教程:https://blog.csdn.net/DGAF2198588973/article/details/125911630
#pragma once #ifndef D2D_PICTURE_H_ #define D2D_PICTURE_H_ #includeD2D_PICTURE.CPPusing namespace Microsoft; using namespace Microsoft::WRL; #include typedef enum Picture_Type { SINGLE_FRAME, //单帧图片 MULTI_FRAME //多帧图片,如GIF } Picture_Type; //图片类,必须提供图片信息 //可以使用vector类存储一组图片 class Picture : public ComPtr { public: Picture(WCHAR* FileName) : PictureFileName(FileName) {}; Picture() { PictureFileName = nullptr; } public: //获取Picture类内部存储的图片路径 inline WCHAR* GetPictureFileName() { return PictureFileName; } //改变Picture类存储的图片文件路径 bool ChangeFileName(WCHAR* Filename); //检测图片路径是否存在 bool CheckFileIfExist(WCHAR* Filename); private: //Picture类单独存储的图片路径 WCHAR* PictureFileName; }; #endif
#include "D2D_PICTURE.h" #includeD2D_DRAW.Hbool Picture::ChangeFileName(WCHAR* Filename) { if (!(PictureFileName = Filename)) { return false; } return true; } bool Picture::CheckFileIfExist(WCHAR* Filename) { std::fstream f; f.open(Filename,std::ios::in); if (f.good()) { f.close(); return true; } else { f.close(); return false; } }
#pragma once #ifndef D2D_DRAW_H_ #define D2D_DRAW_H_ #includeD2D_DRAW.CPPusing namespace Microsoft; using namespace Microsoft::WRL; #include using namespace D2D1; #include #include using namespace std; #include "D2D_PICTURE.h" #pragma comment(lib,"d2d1.lib") //用于检测COM,WIC组件的错误 class D2DException { public: D2DException(HRESULT hr) : m_hrError(hr) { } HRESULT Error() const { return m_hrError; } private: const HRESULT m_hrError; }; //D2D图形引擎接口 class D2DEngine { private: ComPtr D2DFactory; ComPtr D2DRenderTarget; ComPtr D2DBrush; private: ComPtr WICFactory; ComPtr WICDecoder; ComPtr WICFrame; ComPtr WICFormatConverter; public: //构造函数 D2DEngine(); void BeginDraw(); void EndDraw(); //初始化函数,D2DEngine的任何操作都要先经这一步 //一参句柄必填,二参表示画刷,默认为白色 bool Initialize(const HWND hwnd, const ColorF::Enum BrushColor = ColorF::White); //清屏并填充背景色,默认白色 void Clear(const ColorF::Enum ClearColor = ColorF::White); //绘制单帧图片 (XY坐标,宽,高) void DrawPicture(Picture Bitmap, double x, double y, double width, double height); public: //以下函数使用了WIC库 //对图片格式进行转换,图片只有被转换成D2D位图才能加载操作 //当转换动图(如GIF)时,需要额外提供一个 vector //当转换不支持的图片时,会返回false bool TransformPictureIntoBitmap( Picture& Bitmap, Picture_Type type, vector & BitmapGroup = vector ()); }; #endif
#include "D2D_DRAW.h"
D2DEngine::D2DEngine()
{
if (FAILED(::CoInitialize(nullptr)))
{
::PostQuitMessage(0);
::exit(0);
}
}
bool D2DEngine::Initialize(
const HWND hwnd,
const ColorF::Enum BrushColor
)
{
try
{
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&D2DFactory));
RECT rc = {};
GetClientRect(hwnd, &rc);
D2DFactory->CreateHwndRenderTarget(
RenderTargetProperties(),
HwndRenderTargetProperties(hwnd, SizeU(rc.right - rc.left, rc.bottom - rc.top)),
&D2DRenderTarget);
D2DRenderTarget->CreateSolidColorBrush(ColorF(BrushColor), &D2DBrush);
}
catch (D2DException& Error)
{
return false;
}
return true;
}
void D2DEngine::BeginDraw()
{
D2DRenderTarget->BeginDraw();
}
void D2DEngine::EndDraw()
{
D2DRenderTarget->EndDraw();
}
void D2DEngine::Clear(const ColorF::Enum ClearColor)
{
D2DRenderTarget->Clear(ColorF(ClearColor));
}
bool D2DEngine::TransformPictureIntoBitmap(
Picture& Bitmap,Picture_Type type,std::vector& BitmapGroup)
{
try
{
WICFactory.Reset();
WICDecoder.Reset();
WICFrame.Reset();
WICFormatConverter.Reset();
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&WICFactory));
//通过Picture内部储存的路径将图片读入内存
WICFactory->CreateDecoderFromFilename(
Bitmap.GetPictureFileName(),
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnDemand,
&WICDecoder
);
if (type == SINGLE_FRAME)
{
WICDecoder->GetFrame(0, &WICFrame);
WICFactory->CreateFormatConverter(&WICFormatConverter);
//对图片进行格式转换
WICFormatConverter->Initialize(
WICFrame.Get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom
);
D2DRenderTarget->CreateBitmapFromWicBitmap(WICFormatConverter.Get(), NULL, Bitmap.GetAddressOf());
}
else if (type == MULTI_FRAME)
{
UINT _framecount;
WICDecoder->GetFrameCount(&_framecount);
Picture _bitphoto;
for (UINT i = 0; i < _framecount; i++)
{
WICDecoder->GetFrame(i, &WICFrame);
WICFactory->CreateFormatConverter(&WICFormatConverter);
//对图片进行格式转换
WICFormatConverter->Initialize(
WICFrame.Get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom
);
D2DRenderTarget->CreateBitmapFromWicBitmap(WICFormatConverter.Get(), NULL, _bitphoto.GetAddressOf());
BitmapGroup.push_back(_bitphoto);
}
}
}
catch (D2DException& Error)
{
return false;
}
return true;
}
void D2DEngine::DrawPicture(Picture Bitmap, double x, double y, double width, double height)
{
D2D1_RECT_F imgr = { x,y,width + x,height + y };
D2DRenderTarget->DrawBitmap(Bitmap.Get(), imgr);
}
后记
本人写D2DEngine库时,正在过暑假,高一升高二。暑期做完作业没什么事,非常喜欢做图像一类的程序,有点不喜欢竞赛。鄙人见识浅陋,肯定有不足之处,大家有意见和问题的在评论区提出就行了
如果大家喜欢,支持我的项目,点个赞就行了,如果有和我一样喜欢或从事图形学的那更好了。
最后,D2DEngine库会在暑期持续更新,教程我会在系列后面提供。



