终于提取到了TrueType字体的轮廓了
为了让 这篇文章说的东西能够落实,无法躲避的基本东西还是要先准备一下的。今天花了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 }
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 }