终于提取到了TrueType字体的轮廓了

终于提取到了TrueType字体的轮廓了
    为了让 这篇文章说的东西能够落实,无法躲避的基本东西还是要先准备一下的。今天花了6个小时查了无数资料终于把文字的边框弄出来了。

    在此贴出代码和效果图,不作过多解释。熟悉Win32API中的GDI部分的朋友们可以很容易看懂。

    效果图:
    提取的轮廓:红色和黑色为直线,蓝色为四次贝塞尔曲线。其中上面是先TextOut后自己画,下面是先自己画后TextOut。四次贝塞尔曲线转换成三次贝塞尔曲线之后使用PolyBezier绘制。
终于提取到了TrueType字体的轮廓了_第1张图片

    代码:
    代码使用的框架是我自己寒假无聊的时候封装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  }

你可能感兴趣的:(终于提取到了TrueType字体的轮廓了)