為了讓
這篇文章說的東西能夠落實,無法躲避的基本東西還是要先準備一下的。今天花了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) 閱讀(12228)
評論(12) 編輯 收藏 引用 所屬分類:
2D