【Fr2D开发笔记】四、图形、位图和文本渲染

初始化了Direct2D之后,就可以开始绘制了。

一、几何图元

如上篇所示,绘制几何图形的方法都在RenderTarget对象中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
virtual void DrawLine(
D2D1_POINT_2F point0,
D2D1_POINT_2F point1,
[in] ID2D1Brush *brush,
FLOAT strokeWidth = 1.0f,
[in, optional] ID2D1StrokeStyle *strokeStyle = NULL
) = 0;

void DrawRectangle(
[ref] const D2D1_RECT_F &rect,
[in] ID2D1Brush *brush,
FLOAT strokeWidth =1.0f,
[in, optional] ID2D1StrokeStyle *strokeStyle = NULL
);

void DrawEllipse(
[ref] const D2D1_ELLIPSE &ellipse,
[in] ID2D1Brush *brush,
FLOAT strokeWidth =1.0f,
[in, optional] ID2D1StrokeStyle *strokeStyle = NULL
);

等。

其中,D2D提供结构体表示对应的图元,可以使用D2D1中的对应函数构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct D2D_RECT_F {
FLOAT left;
FLOAT top;
FLOAT right;
FLOAT bottom;
};

struct D2D1_ELLIPSE {
D2D1_POINT_2F point;
FLOAT radiusX;
FLOAT radiusY;
};

D2D1_RECT_F RectF(FLOAT left = 0.f, FLOAT top = 0.f, FLOAT right = 0.f, FLOAT bottom = 0.f);
D2D1_ELLIPSE Ellipse(const D2D1_POINT_2F &center, FLOAT radiusX, FLOAT radiusY);

如此,可将一些图形的绘制函数封装在Fr2D类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Fr2D {	//as a d2d render target
public:
// 略
void DrawLine(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width);

void DrawRectangle(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width);
void FillRectangle(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom);

void DrawEllipse(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width);
void FillEllipse(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom);

void DrawTriangle(Fr2DBrush &fr2dbrush, float x1, float y1, float x2, float y2, float x3, float y3, float width);
private:
HWND * hwndptr;
ID2D1Factory * d2dFactory;
ID2D1HwndRenderTarget* hdl;
};

void Fr2D::DrawLine(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width) {
hdl->DrawLine(D2D1::Point2F(left, top), D2D1::Point2F(right, bottom), fr2dbrush.GetBrush(), width);
}

void Fr2D::DrawRectangle(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width) {
hdl->DrawRectangle(
D2D1::RectF(left, top, right, bottom),
fr2dbrush.GetBrush(),
width
);
}

void Fr2D::FillRectangle(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom) {
hdl->FillRectangle(
D2D1::RectF(left, top, right, bottom),
fr2dbrush.GetBrush()
);
}

void Fr2D::DrawEllipse(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom, float width) {
hdl->DrawEllipse(
D2D1::Ellipse({ (left + right) / 2,(top + bottom) / 2 }, (right - left) / 2, (bottom - top) / 2),
fr2dbrush.GetBrush(),
width
);
}

void Fr2D::FillEllipse(Fr2DBrush &fr2dbrush, float left, float top, float right, float bottom) {
hdl->FillEllipse(
D2D1::Ellipse({ (left + right) / 2,(top + bottom) / 2 }, (right - left) / 2, (bottom - top) / 2),
fr2dbrush.GetBrush()
);
}

void Fr2D::DrawTriangle(Fr2DBrush &fr2dbrush, float x1, float y1, float x2, float y2, float x3, float y3, float width) {
hdl->DrawLine(D2D1::Point2F(x1, y1), D2D1::Point2F(x2, y2), fr2dbrush.GetBrush(), width);
hdl->DrawLine(D2D1::Point2F(x2, y2), D2D1::Point2F(x3, y3), fr2dbrush.GetBrush(), width);
hdl->DrawLine(D2D1::Point2F(x3, y3), D2D1::Point2F(x1, y1), fr2dbrush.GetBrush(), width);
}

即可使用Fr2D对象和Fr2DBrush对象绘制对应图形。

二、位图

Direct2D的位图是基于WIC(Windows Image Component)来完成的,是基于COM组件的技术。WIC组件可以支持大部分图像格式的解码。

创建WIC位图

将WIC位图的创建封装至FrBitmap类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
bool FrBitmap::Create() {
ID2D1Bitmap * pBitmap;
IWICImagingFactory *pIWICFactory;
IWICBitmapDecoder *pDecoder;
IWICBitmapFrameDecode *pSource;
IWICStream *pStream;
IWICFormatConverter *pConverter;
IWICBitmapScaler *pScaler;

HRESULT hr = S_OK;
pIWICFactory = NULL;
pDecoder = NULL;
pSource = NULL;
pStream = NULL;
pConverter = NULL;
pScaler = NULL;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&pIWICFactory
);

hr = pIWICFactory->CreateDecoderFromFilename(
picname.c_str(),
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);

if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
if (FAILED(hr)) {
MessageBox(NULL, _T("Draw 1failed!"), _T("Error"), 0);
return false;
}
}

if (SUCCEEDED(hr))
{
hr = pIWICFactory->CreateFormatConverter(&pConverter);
if (FAILED(hr)) {
MessageBox(NULL, _T("Draw 2failed!"), _T("Error"), 0);
return false;
}
}

hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);

if (FAILED(hr)) {
MessageBox(NULL, _T("Draw 3failed!"), _T("Error"), 0);
return false;
}
return true;
}

此处注意对多线程程序,创建时须使用参数CoInitializeEx(NULL, COINIT_MULTITHREADED);

Direct2D位图

TODO:此处的接口设计不科学,后应改成友元类实现。

创建WIC位图后就可以创建Direct2D位图了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool Fr2D::CreateBitmap(FrBitmap &bmp) {
HRESULT hr;
hr = hdl->CreateBitmapFromWicBitmap(
bmp.GetConverter(),
NULL,
bmp.GetBitmapPtr()
);

if (FAILED(hr)) {
MessageBox(NULL, _T("Draw 4failed!"), _T("Error"), 0);
return false;
}
bmp.Release();
return true;
}

其中的Release函数是释放使用的COM组件:

1
2
3
4
5
6
7
8
#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}
void FrBitmap::Release() {
SAFE_RELEASE(pDecoder);
SAFE_RELEASE(pSource);
SAFE_RELEASE(pStream);
SAFE_RELEASE(pConverter);
SAFE_RELEASE(pScaler);
}

绘制位图

全部创建完毕,就可以绘图了,绘制方法在RenderTarget中:

1
2
3
4
5
6
void Fr2D::DrawBitmap(FrBitmap &bmp, float left, float top, float right, float bottom) {
hdl->DrawBitmap(
bmp.GetBitmap(),
D2D1::RectF(left, top, right, bottom)
);
}

三、文本

在Direct2D中,文本的绘制是通过DirectWrite来实现的,需要头文件Dwrite.h和静态库dwrite.lib。

初始化Dwrite

Dwrite这块初始化是与Direct2D的Factory类无关的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class FrText {
public:
bool Create(float left = 0.f, float top = 0.f, float right = 200.f, float bottom = 200.f);
IDWriteTextFormat* GetFormat();
void SetRect(float left, float top, float right, float bottom);

D2D1_RECT_F layoutRect;

private:
IDWriteFactory * pDWriteFactory;
IDWriteTextFormat* pTextFormat;
};

bool FrText::Create(float left, float top, float right, float bottom) {
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pDWriteFactory));

HRESULT hr = pDWriteFactory->CreateTextFormat(
L"Arial",
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
20.0f * 96.0f / 72.0f,
L"en-US",
&pTextFormat
);

layoutRect = D2D1::RectF(left, top, right, bottom);
return true;
}

IDWriteTextFormat* FrText::GetFormat() {
return pTextFormat;
}

void FrText::SetRect(float left, float top, float right, float bottom) {
layoutRect.left = left;
layoutRect.top = top;
layoutRect.right = right;
layoutRect.bottom = bottom;
}

绘制文本

1
2
3
4
5
6
7
8
9
void Fr2D::Write(FrText &frText, Fr2DBrush &fr2dBrush, std::string s) {
hdl->DrawText(
stringToLPCWSTR(s),
s.length(),
frText.GetFormat(),
frText.layoutRect,
fr2dBrush.GetBrush()
);
}