字符输出,对于AGG来说,这个功能可以处于显示流程的 不同位置。比如字体引擎可直接处于“Scanline Rasterizer”层向渲染器提供已处理完毕的扫描线,也可以处于“Vertex Source顶点源”层提供字体的顶点数据。
下面,我们开始学习AGG不同的字符输出方式。如没有特殊说明,所以示例代码都基于此处代码
gsv_text属于顶点源层的对象,它的用法也很简单,直接看下例:
引用头文件:#include <agg_gsv_text.h>
在on_draw()方法的最后加入下面的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// gsv_text类
agg::gsv_text txt;
agg::conv_stroke<agg::gsv_text> cstxt(txt);
// 设置大小及是否反转
txt.flip(
true
);
txt.size(18);
// 设置位置和文字
txt.start_point(20,100);
txt.text(
"cpp"
);
// 以红色输出上面的文字
ras.add_path(cstxt);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(1,0,0));
// 设置新的位置和文字
txt.start_point(20+txt.text_width(),100);
txt.text(
"prog.com"
);
// 以蓝色输出上面的文字
ras.add_path(cstxt);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(0,0,1));
|
显示效果
注:gsv_text的flip ()方法指出是否上下反转输出,这里设置了flip是因为在Windows下agg::platform_support的rbuf_window()其 实是一个DIB缓存,它的方向是从下到上的。
gsv_text必须用conv_stroke转 换才能正确输出文字,否则会被当作多边形处理。为了使用方便,AGG提供了gsv_text_outline包 装,它实现了conv_stroke和坐标转换,代码很短:
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
|
template
<
class
Transformer = trans_affine>
class
gsv_text_outline
{
public
:
gsv_text_outline(gsv_text& text,
const
Transformer& trans) :
m_polyline(text),
m_trans(m_polyline, trans){}
void
width(
double
w){
m_polyline.width(w);
}
void
transformer(
const
Transformer* trans){
m_trans->transformer(trans);
}
void
rewind
(unsigned path_id) {
m_trans.
rewind
(path_id);
m_polyline.line_join(round_join);
m_polyline.line_cap(round_cap);
}
unsigned vertex(
double
* x,
double
* y) {
return
m_trans.vertex(x, y);
}
private
:
conv_stroke<gsv_text> m_polyline;
conv_transform<conv_stroke<gsv_text>, Transformer> m_trans;
};
|
我们使用gsv_text_outline重写上面的代码:
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
|
#include <agg_gsv_text.h>
#include <agg_trans_single_path.h>
...
/////////////////////////////////////////////////
// gsv_text及gsv_text_outline
agg::gsv_text txt;
agg::trans_single_path tran_path;
//使用trans_single_path作为变换器
agg::gsv_text_outline<agg::trans_single_path> txtol(txt,tran_path);
// 设置变换器
tran_path.add_path(ell);
/////////////////////////////////////////////////
// 设置大小及是否反转
txt.flip(
true
);
txt.size(24);
// 设置位置和文字
txt.start_point(0,0);
txt.text(
"cpp"
);
// 以红色输出上面的文字
ras.add_path(txtol);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(1,0,0));
// 设置新的位置和文字
txt.start_point(0+txt.text_width(),0);
txt.text(
"prog.com"
);
// 以蓝色输出上面的文字
ras.add_path(txtol);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(0,0,1));
|
显示效果
gsv_text的使用很简单,不过要了解的一点是:它只能输出ASCII可显示字符,对于汉字是无能为力的(你可以试试输出"C++编程")。如果要输出汉字,我 们得继续寻找其它字符输出方式。
AGG的字体引擎利用WinAPI:GetGlyphOutline或FreeType库得到字体数据(字模),它可以处于 “Scanline Rasterizer”层或“顶点源”层。要使用字体引擎,要把相应的字体引擎源码(agg_font_win32_tt.cpp或 agg_font_freetype.cpp)加入项目一起编译。
#include <agg_font_win32_tt.h>
#include <agg_font_freetype.h>
注意,它们都有自己的文件夹,不是在agg的include文件夹里。
agg::font_engine_win32_tt_int16 agg::font_engine_win32_tt_int32 agg::font_engine_freetype_int16 agg::font_engine_freetype_int32
显然,前两个利用WinAPI实现,后两个利用FreeType库实现。类型后面的_int16或_int32后缀用于指定坐标单位, 一般int16已经可以满足要求。
typedef path_adaptor_type | 把字体数据包装成顶点源的类 |
typedef gray8_adaptor_type | 把字体数据包装成Scanline Rasterizer的类 |
typedef mono_adaptor_type | 把字体数据包装成Scanline Rasterizer的类,但无AA效果 |
double: height | 字体高度,单位为Point(和Word里的单位一样) |
double: width | 字体宽度,单位为Point*2.4。0表示规则大小(height/2.4) |
bool: italic | 斜体 |
bool: flip_y | 上下翻转 |
bool: hinting | 字体修正 |
unsigned: resolution | 字体解析度,单位为dpi |
void transform(const trans_affine& affine); | 按矩阵变换 |
bool create_font(const char* typeface_, glyph_rendering ren_type); | font_engine_win32_tt_*专有方法 |
bool load_font(const char* font_name, |
font_engine_freetype_*专有方法 |
bool prepare_glyph(unsigned glyph_code) |
得到字体数据(字模)所需方法 |
字体引擎的create_font()方法和load_font()方法需要一个glyph_rendering类型的ren_type参数,它决 定了字 体数据的形式。三个成员类型定义:path_adaptor_type、gray8_adaptor_type和mono_adaptor_type所包 装的字体数据是 不一样的,只有与ren_type参数对应才能生成正确的AGG显示节点。
glyph_rendering是一个枚举类型,定义是:
1
2
3
4
5
6
7
|
enum
agg::glyph_rendering{
glyph_ren_native_mono,
//对应mono_adaptor_type
glyph_ren_native_gray8,
//对应gray8_adaptor_type
glyph_ren_outline,
//对应path_adaptor_type
glyph_ren_agg_mono,
//对应mono_adaptor_type
glyph_ren_agg_gray8
//对应gray8_adaptor_type
};
|
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
|
typedef
agg::font_engine_win32_tt_int16 fe_type;
typedef
fe_type::path_adaptor_type vs_type;
// 字体引擎
fe_type fe( ::GetDC(::GetActiveWindow()) );
//注意,实际应用时要释放HDC
fe.height(36.0);
fe.flip_y(
true
);
fe.hinting(
true
);
// 注意后面的glyph_rendering ren_type参数
fe.create_font(
"黑体"
,agg::glyph_ren_outline);
// 字体串
wchar_t
*s = L
"C++编程"
;
// 存放字体数据
std::vector<agg::int8u> data;
// 顶点源
vs_type vs;
// 注意这里,使用conv_curve转换
agg::conv_curve<vs_type> ccvs(vs);
// 字符输出的位置
int
x=20,y=100;
for
(;*s;s++)
{
// 让字体引擎准备好字体数据
if
(!fe.prepare_glyph(*s))
continue
;
// 把字体数据放到容器里
data.resize( fe.data_size() );
fe.write_glyph_to( &data[0] );
// 从字体数据中得到顶点源
vs.init(&data[0], data.size(), x, y);
// 移动输出位置
x += fe.advance_x();
y += fe.advance_y();
// 输出
ras.add_path(ccvs);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(0,0,1));
}
|
由于字体顶点源可能会包含带Curve命令的顶点,所以要用conv_curve来 转换。你可以试试去掉这层转换,字符'C' 就不会那么平滑了。
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
|
// 字体引擎类型定义
typedef
agg::font_engine_win32_tt_int16 fe_type;
typedef
fe_type::gray8_adaptor_type ras_type;
typedef
ras_type::embedded_scanline sl_type;
// 字体引擎
fe_type fe( ::GetDC(::GetActiveWindow()) );
//注意,实际应用时要释放HDC
fe.height(36.0);
fe.flip_y(
true
);
fe.hinting(
true
);
// 注意后面的glyph_rendering ren_type参数
fe.create_font(
"黑体"
,agg::glyph_ren_agg_gray8);
// 字体串
wchar_t
*s = L
"C++编程"
;
// 存放字体数据
std::vector<agg::int8u> data;
// Rasterizer和Scanline
ras_type ras_font;
sl_type sl_font;
// 字符输出的位置
int
x=20,y=100;
for
(;*s;s++)
{
// 让字体引擎准备好字体数据
if
(!fe.prepare_glyph(*s))
continue
;
// 把字体数据放到容器里
data.resize( fe.data_size() );
fe.write_glyph_to( &data[0] );
// 从字体数据中得到Rasterizer
ras_font.init(&data[0], data.size(), x, y);
// 移动输出位置
x += fe.advance_x();
y += fe.advance_y();
// 输出
agg::render_scanlines_aa_solid(ras_font,sl_font,renb,agg::rgba(0,0,1));
}
|
Linux、 FreeBSD等开源操作系统里一般使用FreeType来显示文字,Windows下的一些软件如Foxit也有FreeType的身影。
AGG的font_engine_freetype_int16字 体引擎就使用FreeType来取得字模,在Windows里,在使用font_engine_freetype_int16之前,我们得先编译好 FreeType:
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
|
typedef
agg::font_engine_freetype_int16 fe_type;
typedef
fe_type::path_adaptor_type vs_type;
fe_type fe;
if
(!fe.load_font(
"c:\\windows\\fonts\\simhei.ttf"
,0,agg::glyph_ren_outline))
return
;
fe.height(36.0);
fe.flip_y(
true
);
fe.hinting(
true
);
wchar_t
*s = L
"C++编程"
;
std::vector<agg::int8u> data;
vs_type vs;
agg::conv_curve<vs_type> ccvs(vs);
int
x=20,y=100;
for
(;*s;s++)
{
if
(!fe.prepare_glyph(*s))
continue
;
data.resize( fe.data_size() );
fe.write_glyph_to( &data[0] );
vs.init(&data[0], data.size(), x, y);
x += fe.advance_x();
y += fe.advance_y();
ras.add_path(ccvs);
agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(0,0,1));
}
|
每次都重新读字模是很费时的,比如前面的例子,"C++" 里的两个'+' 就读两次字模,效率可以想象。
一个好的办法是把已读出来的字模缓存起来,下次再遇到这个字时就不用从字体引擎里读取了,AGG提供的font_cache_manager类就是 负责这项工作的。
1
|
#include "agg_font_cache_manager.h"
|
1
|
template
<
class
FontEngine>
class
font_cache_manager;
|
模板参数FontEngine指定管理器所用的字体引擎。另外构造参数也是FontEngine。
|
获得字模并缓存,glyph_cache类的定义是:
|
||
|
字体引擎的path_adaptor_type实例 | ||
|
字体引擎的gray8_adaptor_type实例以及对应的Scanline | ||
|
字体引擎的mono_adaptor_type实例以及对应的Scanline | ||
|
初始化上面的adaptor成员实例(与字体引擎的ren_type设置相关) | ||
|
调整坐标 |
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
|
agg::font_engine_win32_tt_int16 font(dc);
agg::font_cache_manager<
agg::font_engine_win32_tt_int16
> font_manager(font);
font.height(72.0);
font.width(0);
font.italic(
true
);
font.flip_y(
true
);
font.hinting(
true
);
font.transform(agg::trans_affine_rotation(agg::deg2rad(4.0)));
font.create_font(
"宋体"
,agg::glyph_ren_agg_gray8);
double
x=10, y=72;
//起始位置
wchar_t
*text = L
"C++编程网"
;
// 画所有字符
for
(;*text;text++)
{
//取字模
const
agg::glyph_cache* glyph = font_manager.glyph(*text);
if
(glyph)
{
// 初始化gray8_adaptor实例
font_manager.init_embedded_adaptors(glyph, x, y);
agg::render_scanlines_aa_solid(font_manager.gray8_adaptor(),
font_manager.gray8_scanline(),
renb, agg::rgba8(0, 0, 0));
// 前进
x += glyph->advance_x;
y += glyph->advance_y;
}
}
|
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
|
typedef
agg::font_engine_win32_tt_int16 fe_type;
fe_type font(GetDC(0));
typedef
agg::font_cache_manager<fe_type> fcman_type;
fcman_type font_manager(font);
font.height(72.0);
font.width(0);
font.italic(
true
);
font.flip_y(
true
);
font.hinting(
true
);
font.transform(agg::trans_affine_rotation(agg::deg2rad(4.0)));
font.create_font(
"宋体"
,agg::glyph_ren_outline);
double
x=10, y=72;
//起始位置
wchar_t
*text = L
"C++编程网"
;
// 画所有字符
for
(;*text;text++)
{
const
agg::glyph_cache* glyph = font_manager.glyph(*text);
if
(glyph)
{
// 准备*_adaptor
font_manager.init_embedded_adaptors(glyph, x, y);
// 先用conv_curve
typedef
agg::conv_curve<
fcman_type::path_adaptor_type
> cc_pa_type;
cc_pa_type ccpath(font_manager.path_adaptor());
// 画轮廓
typedef
agg::conv_stroke<cc_pa_type> cs_cc_pa_type;
cs_cc_pa_type csccpath(ccpath);
agg::rasterizer_scanline_aa<> ras;
agg::scanline_u8 sl;
ras.add_path(csccpath);
agg::render_scanlines_aa_solid(ras, sl, renb, agg::rgba8(0, 0, 0));
// 前进
x += glyph->advance_x;
y += glyph->advance_y;
}
}
|
作为本文的结尾,这里放上一个用AGG生成不规则文字窗体的代码。它综合了我们之前学到的AGG字体引擎、坐标转换、颜色渐变等几大模 块。由于AGG的抗锯齿特性,使用生 成的窗体看上去边缘过渡非常自然,几乎看不到“毛边”。
先放上最终生成的窗体的效果:
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
|
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <agg_array.h>
#include <agg_pixfmt_rgba.h>
#include <agg_scanline_u.h>
#include <agg_renderer_scanline.h>
#include <../font_win32_tt/agg_font_win32_tt.h>
#include <agg_font_cache_manager.h>
#include <agg_span_solid.h>
#include <agg_span_interpolator_linear.h>
#include <agg_span_gradient.h>
#include <agg_span_allocator.h>
#include <agg_conv_transform.h>
#include <agg_ellipse.h>
#include <agg_trans_single_path.h>
typedef
agg::font_engine_win32_tt_int16 fe_type;
typedef
agg::font_cache_manager<fe_type> fcman_type;
typedef
agg::renderer_base<agg::pixfmt_bgra32> renb_type;
// 使用指定的顶点源和线段生成器输出文字
template
<
class
VS,
class
SpanGenerator>
void
AggDrawText(renb_type &renb,
fcman_type &font_manager,
VS &vs, SpanGenerator &span_gen,
const
wchar_t
*txt)
{
using
namespace
agg;
span_allocator<rgba8> span_alloc;
rasterizer_scanline_aa<> ras;
scanline_u8 sl;
double
x=0, y=0;
for
(
const
wchar_t
*p = txt; *p; p++)
{
const
glyph_cache* gc = font_manager.glyph(*p);
if
(gc)
{
font_manager.init_embedded_adaptors(gc, x, y);
ras.add_path(vs);
agg::render_scanlines_aa(ras, sl, renb, span_alloc, span_gen);
x += gc->advance_x;
y += gc->advance_y;
}
}
}
// 向renb的指定位置和半径输出http://www.cppprog.com ,有环绕效果
void
DrawUrl(
HDC
dc, renb_type &renb,
double
ox,
double
oy,
double
rx,
double
ry)
{
using
namespace
agg;
//字体引擎
fe_type font(dc);
fcman_type font_manager(font);
font.height(18.0);
font.flip_y(
true
);
font.hinting(
true
);
if
(!font.create_font(
"Comic Sans MS"
,agg::glyph_ren_outline))
return
;
//坐标转换管道
typedef
conv_curve<
fcman_type::path_adaptor_type
> cc_pa_type;
cc_pa_type ccpath(font_manager.path_adaptor());
typedef
conv_transform<cc_pa_type,
trans_single_path> ct_cc_pa_type;
trans_single_path trans_path;
ct_cc_pa_type ctpath(ccpath, trans_path);
ellipse ell(0,0,rx,ry);
trans_affine ellmtx;
conv_transform<ellipse> ctell(ell, ellmtx);
ellmtx.rotate(agg::pi);
ellmtx.translate(ox,oy);
trans_path.add_path(ctell);
// 线段生成器
span_solid<rgba8> ss;
ss.color(rgba(1,0,0));
}
// 向renb的指定位置输出“C++编程”几个字,有镜象效果
void
DrawName(
HDC
dc, renb_type &renb,
double
x,
double
y)
{
using
namespace
agg;
// 字体引擎
fe_type font(dc);
fcman_type font_manager(font);
font.height(30.0);
font.flip_y(
true
);
font.hinting(
true
);
if
(!font.create_font(
"黑体"
,agg::glyph_ren_outline))
return
;
// 坐标转换管道
typedef
conv_curve<
fcman_type::path_adaptor_type
> cc_pa_type;
cc_pa_type ccpath(font_manager.path_adaptor());
typedef
conv_transform<cc_pa_type> ct_cc_pa_type;
trans_affine mtx;
ct_cc_pa_type ctpath( ccpath, mtx );
mtx.translate(50,50);
//线段生成器
span_solid<rgba8> ss;
ss.color(rgba(0,0,0));
AggDrawText(renb, font_manager, ctpath, ss, L
"C++编程"
);
// 改变坐标转换矩阵(镜像)
mtx.reset();
mtx.flip_y();
mtx.translate(50,60);
// 渐变线段生成器
typedef
span_interpolator_linear<> interpolator_type;
trans_affine img_mtx;
interpolator_type ip(img_mtx);
typedef
gradient_y gradientF_type;
gradientF_type grF;
typedef
gradient_linear_color<rgba8> colorF_type;
colorF_type colorF(rgba(0,0,0), rgba(0,0,0,0));
typedef
span_gradient<rgba8,
interpolator_type,
gradientF_type,
colorF_type> span_gen_type;
span_gen_type span_gen(ip,grF,colorF,30,80);
AggDrawText(renb, font_manager, ctpath, span_gen, L
"C++编程"
);
}
// 调用DrawUrl和DrawName向renb输出文字
void
DrawIt(
HDC
dc, renb_type &renb)
{
// 以透明色填充
renb.clear(rgba(0,0,0,0));
// 输出文字
DrawUrl(dc, renb, 100, 50, 80, 40);
DrawName(dc, renb, 50, 50);
}
// 使用AGG处理图片后与hwnd关联
void
SetLayoutWin(
HWND
hwnd)
{
// 起始位置和窗体大小
POINT ptWinPos = {500,200};
SIZE sizeWindow = {200, 100};
// 建立DIB
BITMAPINFO bmp_info;
::ZeroMemory(&bmp_info,
sizeof
(bmp_info));
bmp_info.bmiHeader.biSize =
sizeof
(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = sizeWindow.cx;
bmp_info.bmiHeader.biHeight = sizeWindow.cy;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 32;
bmp_info.bmiHeader.biCompression = BI_RGB;
HDC
hdcTemp = GetDC(0);
HDC
mem_dc = ::CreateCompatibleDC(hdcTemp);
ReleaseDC(0, hdcTemp);
void
* buf = NULL;
HBITMAP
bmp = ::CreateDIBSection(
mem_dc,
&bmp_info,
DIB_RGB_COLORS,
&buf,
0, 0
);
// 把bmp与mem_dc关联,这样AGG就可以和原生GDI一起工作了
HBITMAP
temp = (
HBITMAP
)::SelectObject(mem_dc, bmp);
{
// AGG处理
agg::rendering_buffer rbuf(
(unsigned
char
*)buf,
sizeWindow.cx, sizeWindow.cy,
-sizeWindow.cx*4);
agg::pixfmt_bgra32 pixf(rbuf);
renb_type renb(pixf);
DrawIt(mem_dc,renb);
}
// 把画好的mem_dc与hwnd关联到一起
BLENDFUNCTION m_Blend={AC_SRC_OVER,0,255,AC_SRC_ALPHA};
POINT ptSrc = {0, 0};
BOOL
bRet = UpdateLayeredWindow(hwnd, 0, &ptWinPos,
&sizeWindow, mem_dc, &ptSrc,
0, &m_Blend, ULW_ALPHA);
// 回收
::DeleteObject(bmp);
::DeleteDC(mem_dc);
}
// Windows消息处理
LRESULT
CALLBACK WndProc (
HWND
hwnd,
UINT
umsg,
WPARAM
wParam,
LPARAM
lParam)
{
switch
(umsg)
{
case
WM_CLOSE:
DestroyWindow (hwnd);
return
0;
case
WM_DESTROY:
PostQuitMessage (0);
return
0;
case
WM_NCHITTEST:
return
HTCAPTION;
}
return
DefWindowProc (hwnd, umsg, wParam, lParam);
}
int
APIENTRY WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPTSTR
lpCmdLine,
int
nCmdShow)
{
WNDCLASS wc={
0,WndProc,
0,0,
hInstance,
NULL,LoadCursor(NULL, IDC_ARROW),
(
HBRUSH
)(COLOR_WINDOW+1),
0,
"AGGWIN"
};
::RegisterClass(&wc);
HWND
hWnd = ::CreateWindowEx(WS_EX_LAYERED,
"AGGWIN"
, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if
(!hWnd)
return
-1;
SetLayoutWin(hWnd);
::ShowWindow(hWnd, nCmdShow);
// 主消息循环:
MSG msg;
while
(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return
(
int
) msg.wParam;
}
|
<<完>>