参考:https://docs.microsoft.com/zh-cn/windows/win32/direct2d/direct2d-quickstart
0、Direct2D目前只支持C++,对于C++来说,不建议把程序都写在main/WinMain函数或全局函数中,定义一个App类更符合C++的编程习惯。
1、在包含任何系统头文件之前,先将_WIN32_WINNT用#define设为0x0601(Win7)或以下版本,不然wincodec.h等的Win7兼容性有可能出问题。
2、在main/WinMain函数中
- 调用CoInitialize(wincodec.h需要用到这个东西)
- 调用app.Initialize()创建窗口
- 调用app.RunMessageLoop()进入消息循环
- 调用CoUninitialize
3、在Initialize的函数中
- 调用this->CreateDeviceIndependentResources(),这个函数创建ID2D1Factory(以及按需创建IDWriteFactory、IWICBitmapFactory等)
- 调用ID2D1Factory的GetDesktopDpi获取系统DPI
- RegisterClassEx注册窗口类(注意hbrBackground应该设置为NULL,以防止DefWindowProc使用GDI清空窗口内容导致窗口闪烁)
- CreateWindow创建窗口(使用CreateWindow最后一个参数传递this指针)
- ShowWindow显示窗口、UpdateWindow更新窗口
#pragma warning(push)
#pragma warning(disable:4996)
m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY);
#pragma warning(pop)
3、WndProc中对于this指针的处理
- 在WM_CREATE中,传入的this指针可以从((LPCREATESTRUCT)lParam)->lpCreateParams获取
- 使用窗口的GWLP_USERDATA字段(使用SetWindowLongPtr和GetWindowLongPtr访问)保持this指针
4、要注意尽量避免程序崩溃,适当处理HRESULT错误码
- 要注意对象指针当前是否可能为NULL,当对象指针可能为NULL时,就不要使用该对象指针。一般如果创建对象的方法返回值hr表示操作成功SUCCEEDED(hr),指针一般认为是可用的,如果返回值hr表示操作失败FAILED(hr),指针一般认为有可能是NULL。
- HRESULT错误码不一定要全部处理。可以忽略某些无关紧要的失败;重要的但可恢复的失败需要进行处理;如果是导致程序无法继续的失败,使用exit(hr);处理即可;如果是逻辑上应该根本不可能出现的失败,使用Assert(SUCCEEDED(hr));处理。
5、在WM_PAINT中
- 调用pThis->OnRender();
- 调用ValidateRect标记窗口客户区为有效,不再触发WM_PAINT(BeginPaint加上EndPaint也有同样的效果,但这里我们不使用GDI,因此使用ValidateRect看起来更干净),一般用于静态绘制;或者调用InvalidateRect,再次触发WM_PAINT,一般用于动画绘制。
6、在OnRender()中,进行如下操作
- 调用this->CreateDeviceResources(),这个函数创建ID2D1HwndRenderTarget和相关的对象(如果已创建则不再创建)
- 调用BeginDraw
- SetTransform设置变换,通常设置为Matrix::Identity()
- Clear清空
- GetSize获取绘图区大小(相当于DPI无关的GetClientSize)
- 绘图
- 调用EndDraw
- 当EndDraw返回D2DERR_RECREATE_TARGET时,调用this->DiscardDeviceResources(),这个函数使用SafeRelease释放所有与RenderTarget相关的对象,以便下次绘图时重新创建,忽略EndDraw除这个错误码以外的错误码
7、当WM_SIZE时,调用pThis->OnResize(),这个函数调用ID2D1HwndRenderTarget的Resize调整绘图区域大小,一般忽略错误码,因为这个这个错误码会在下次EndDraw时返回
8、当WM_DISPLAYCHANGE(分辨率改变)时,调用InvalidateRect触发WM_PAINT
9、common.h或stdafx.h公共头文件
#pragma once
// 必须设为0x0601(Windows 7)或以下,否则wincodec.h对Windows 7会有兼容性问题
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#define WIN32_LEAN_AND_MEAN 1
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files:
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <wchar.h>
#include <math.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
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