为了让
这篇文章说的东西能够落实,无法躲避的基本东西还是要先准备一下的。今天花了6个小时查了无数资料终于把文字的边框弄出来了。
在此贴出代码和效果图,不作过多解释。熟悉Win32API中的GDI部分的朋友们可以很容易看懂。
效果图:
提取的轮廓:红色和黑色为直线,蓝色为四次贝塞尔曲线。其中上面是先TextOut后自己画,下面是先自己画后TextOut。四次贝塞尔曲线转换成三次贝塞尔曲线之后使用PolyBezier绘制。
代码:
代码使用的框架是我自己寒假无聊的时候封装API的结果,暂时有窗口、菜单、组合键以及菜单,附带GDI。事件自己弄了一个跟C#差不多的可以同时Bind很多不同种类函数的东西。不过这个不是重点。
需要重点阅读的是如何使用GetGlyphOutline。
dtof将double转换成FIXED,ftod相反。
GetPoint进行点的变换,主要是因为画字符的时候需要偏移。
DrawCurve绘制边框。
DrawString绘制文字。
1 #include "..\..\..\..\Library\Windows\VL_WinMain.h"
2 #include "..\..\..\..\Library\Windows\VL_WinGDI.h"
3
4 using namespace vl;
5 using namespace vl::windows;
6
7 class MyForm : public VL_WinForm
8 {
9 protected:
10 VL_WinDIB* FBuffer;
11 VL_WinDC* FFormDC;
12 VL_WinDC* FBufferDC;
13
14 FIXED dtof(VDouble D)
15 {
16 VInt l;
17 l=(VInt)(D * 65536L);
18 return *(FIXED *)&l;
19 }
20
21 VDouble ftod(FIXED F)
22 {
23 return F.value+(VDouble)F.fract/65536;
24 }
25
26 POINT GetPoint(POINTFX fx , GLYPHMETRICS& gm , TEXTMETRIC& TextMetric , POINT Origin)
27 {
28 /*Y值比最高点搞出了Ascent,即Top与Baseline的距离*/
29 POINT Result;
30 Result.x=(VInt)ftod(fx.x)+Origin.x;
31 Result.y=(VInt)ftod(fx.y)+Origin.y+TextMetric.tmAscent;
32 return Result;
33 }
34
35 void DrawCurve(VBuffer Buffer , VInt Count , GLYPHMETRICS gm , POINT Origin)
36 {
37 VL_WinPen::Ptr Pen_Line=new VL_WinPen(PS_SOLID,1,RGB(255,0,0));
38 VL_WinPen::Ptr Pen_Bezier=new VL_WinPen(PS_SOLID,1,RGB(0,0,255));
39 VL_WinPen::Ptr Pen_Close=new VL_WinPen(PS_SOLID,1,RGB(0,0,0));
40
41 TEXTMETRIC TextMetric;
42 GetTextMetrics(FBufferDC->GetHandle(),&TextMetric);
43
44 while(Count>0)
45 {
46 TTPOLYGONHEADER* Header=(TTPOLYGONHEADER*)Buffer;
47 VInt Remain=Header->cb-sizeof(TTPOLYGONHEADER);
48 VBuffer Start=Buffer+sizeof(TTPOLYGONHEADER);
49 POINTFX StartPoint=Header->pfxStart;
50 POINTFX InitPoint=StartPoint;
51 while(Remain>0)
52 {
53 TTPOLYCURVE* Curve=(TTPOLYCURVE*)Start;
54 switch(Curve->wType)
55 {
56 case TT_PRIM_LINE:
57 {
58 POINT* Points=new POINT[Curve->cpfx+1];
59 Points[0]=GetPoint(StartPoint,gm,TextMetric,Origin);
60 for(VInt i=0;i<Curve->cpfx;i++)
61 {
62 Points[i+1]=GetPoint(Curve->apfx[i],gm,TextMetric,Origin);
63 }
64 /*红*/
65 FBufferDC->SetPen(Pen_Line);
66 FBufferDC->PolyLine(Points,Curve->cpfx+1);
67 delete[] Points;
68 }
69 break;
70 case TT_PRIM_QSPLINE:
71 {
72 /*
73 Quadratic Bezier转Cubic Bezier
74 假设一共有n个点,从1开始,那么在2-3,3-4,,(n-2)-(n-1)中间插入中点
75 遍历每三个点(1,2,3) (3,4,5) ((n-2),(n-1),n),
76 将A,B,C变成A,2(A+B)/3,(B+C)/3,C
77 这个时候得到一个完整的点数组,直接PolyBezier
78 */
79 VInt PointCount=1+(Curve->cpfx-1)*3;
80 POINT* Points=new POINT[PointCount];
81 POINT P0=GetPoint(StartPoint,gm,TextMetric,Origin);
82 POINT P1,P2;
83 Points[0]=P0;
84 VInt Current=1;
85
86 for(VInt i=0;i<Curve->cpfx;)
87 {
88 P1=GetPoint(Curve->apfx[i++],gm,TextMetric,Origin);
89 if(i==Curve->cpfx-1)
90 {
91 P2=GetPoint(Curve->apfx[i++],gm,TextMetric,Origin);
92 }
93 else
94 {
95 P2=GetPoint(Curve->apfx[i],gm,TextMetric,Origin);
96 P2.x=(P1.x+P2.x)/2;
97 P2.y=(P1.y+P2.y)/2;
98 }
99
100 Points[Current+0].x=P0.x + 2*(P1.x - P0.x)/3;
101 Points[Current+0].y=P0.y + 2*(P1.y - P0.y)/3;
102 Points[Current+1].x = P1.x + 1*(P2.x - P1.x)/3;
103 Points[Current+1].y = P1.y + 1*(P2.y - P1.y)/3;
104 Points[Current+2]=P2;
105 Current+=3;
106 P0=P2;
107 }
108 /*蓝*/
109 FBufferDC->SetPen(Pen_Bezier);
110 FBufferDC->PolyBezier(Points,PointCount);
111 delete[] Points;
112 }
113 break;
114 case TT_PRIM_CSPLINE:
115 {
116 POINT* Points=new POINT[Curve->cpfx+1];
117 Points[0]=GetPoint(StartPoint,gm,TextMetric,Origin);
118 for(VInt i=0;i<Curve->cpfx;i++)
119 {
120 Points[i+1]=GetPoint(Curve->apfx[i],gm,TextMetric,Origin);
121 }
122 /*蓝*/
123 FBufferDC->SetPen(Pen_Bezier);
124 FBufferDC->PolyLine(Points,Curve->cpfx+1);
125 delete[] Points;
126 }
127 break;
128 }
129 StartPoint=Curve->apfx[Curve->cpfx-1];
130 Start+=sizeof(TTPOLYCURVE)+(Curve->cpfx-1)*sizeof(POINTFX);
131 Remain-=sizeof(TTPOLYCURVE)+(Curve->cpfx-1)*sizeof(POINTFX);
132 }
133 if(
134 (StartPoint.x.value!=InitPoint.x.value)||
135 (StartPoint.x.fract!=InitPoint.x.fract)||
136 (StartPoint.y.value!=InitPoint.y.value)||
137 (StartPoint.y.fract!=InitPoint.y.fract)
138 )
139 {
140 POINT P1=GetPoint(StartPoint,gm,TextMetric,Origin);
141 POINT P2=GetPoint(InitPoint,gm,TextMetric,Origin);
142 FBufferDC->SetPen(Pen_Close);
143 FBufferDC->MoveTo(P1.x,P1.y);
144 FBufferDC->LineTo(P2.x,P2.y);
145 }
146 InitPoint=StartPoint;
147 Buffer+=Header->cb;
148 Count-=Header->cb;
149 }
150 }
151
152 void DrawString(VInt X , VInt Y , PWChar String , VBool GDIFirst)
153 {
154 if(GDIFirst)
155 {
156 FBufferDC->SetTextColor(RGB(255,255,0));
157 FBufferDC->DrawString(X,Y,String);
158 }
159 {
160 GLYPHMETRICS gm;
161 /*初始化变换矩阵:上下倒转*/
162 MAT2 mat;
163 mat.eM11=dtof(1);
164 mat.eM12=dtof(0);
165 mat.eM21=dtof(0);
166 mat.eM22=dtof(-1);
167 POINT Origin={X,Y};
168 for(VInt i=0;String[i];i++)
169 {
170 DWORD BufferLength=GetGlyphOutline(FBufferDC->GetHandle(),String[i],GGO_NATIVE,&gm,0,0,&mat);
171 if(BufferLength!=GDI_ERROR)
172 {
173 VBuffer Buffer=new VByte[BufferLength];
174 DWORD Result=GetGlyphOutline(FBufferDC->GetHandle(),String[i],GGO_NATIVE,&gm,BufferLength,Buffer,&mat);
175 DrawCurve(Buffer,BufferLength,gm,Origin);
176 Origin.x+=gm.gmCellIncX;
177 Origin.y+=gm.gmCellIncY;
178 delete[] Buffer;
179 }
180 }
181 }
182 if(!GDIFirst)
183 {
184 FBufferDC->SetTextColor(RGB(255,255,0));
185 FBufferDC->DrawString(X,Y,String);
186 }
187 }
188 public:
189
190 MyForm():VL_WinForm(true)
191 {
192 FBuffer=new VL_WinDIB(800,600);
193 FBufferDC=&FBuffer->DC;
194 FBufferDC->FillRect(0,0,800,600);
195 FBufferDC->SetFont(new VL_WinFont(L"宋体",200,0,0,0,400,false,false,false,false));
196 FBufferDC->SetBackTransparent(true);
197 FFormDC=new VL_WinControlDC(GetHandle());
198
199 PWChar String=L"汉字ABC";
200 DrawString(50,50,String,true);
201 DrawString(50,350,String,false);
202
203 SetMaximizeBox(false);
204 SetMinimizeBox(false);
205 SetBorder(vwfbSingle);
206 SetClientWidth(800);
207 SetClientHeight(600);
208 SetText(L"Text Outline");
209 MoveCenter();
210
211 OnPaint.Bind(this,&MyForm::Form_OnPaint);
212
213 Show();
214 }
215
216 ~MyForm()
217 {
218 delete FFormDC;
219 delete FBuffer;
220 }
221
222 void Form_OnPaint(VL_Base* Sender)
223 {
224 FFormDC->Draw(0,0,FBuffer);
225 }
226 };
227
228 void main()
229 {
230 new MyForm();
231 GetApplication()->Run();
232 }
posted on 2008-06-11 07:48
陈梓瀚(vczh) 阅读(12057)
评论(12) 编辑 收藏 引用 所属分类:
2D