随笔-341  评论-2670  文章-0  trackbacks-0
    为了让这篇文章说的东西能够落实,无法躲避的基本东西还是要先准备一下的。今天花了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) 阅读(11861) 评论(12)  编辑 收藏 引用 所属分类: 2D

评论:
# re: 终于提取到了TrueType字体的轮廓了 2008-06-11 17:54 | Xw.Y
嗷嗷赞~有空试一下~  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-06-12 06:35 | ??
有点问题,你用的是truetype字体吗?

看代码是,但是啥时候truetype font开始使用4次bezier曲线了??

看代码,好像是做了bezier的升次,嘿嘿,这个差距有点大。
  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-06-12 07:28 | 陈梓瀚(vczh)
是truetype吧,不然不能返回曲线。truetype用的是4次,是我降次了……四次贝塞尔曲线降到三次还真是麻烦。  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-06-12 19:32 | ??
呵呵,实际上是做的升次,从二次到三次,代码里写的也是这个样子,

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;

turetype font制作软件多用二次B样条函数来表示曲线,也有三次的Bezier。

具体文章可见
http://support.microsoft.com/kb/243285
对应中文版
http://support.microsoft.com/kb/243285/zh-cn
以及
http://msdn.microsoft.com/en-us/library/ms534021(VS.85).aspx

四次的好像没有,从你代码里也看不到

呵呵。。。  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-06-12 22:06 | 陈梓瀚(vczh)
是不是真的四次那我就不知道啦,只不过所有的资料都显示那个东西的名字叫“Quadratic Bezier”。等什么时候有空我把公式还原了,然后再推导出那条方程才知道。不过我想应该是升次吧,怎么说那个“Quadratic Bezier”的定义都像是“Cubic Bezier”的子集。  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-06-13 01:28 | 梦在天涯
bucuo!
good!  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-07-07 20:13 | Jetricy
再弄一个笔顺库就可以写字了.:-)  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2008-11-13 21:21 | SpringSnow
不错耶  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了[未登录] 2010-01-21 01:24 | 无名
复制代码的时候能不能不复制行号啊  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2010-01-23 22:12 | 陈梓瀚(vczh)
@无名
写代码自己消  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2010-05-05 04:53 | messi
#include "..\..\..\..\Library\Windows\VL_WinMain.h"
2 #include "..\..\..\..\Library\Windows\VL_WinGDI.h"
这是什吗啊??用vc报错啊  回复  更多评论
  
# re: 终于提取到了TrueType字体的轮廓了 2013-02-20 17:21 | lalala
顶一个...
头文件这个还要弄一下...Include文件夹里貌似没有VL_WinMain.h和VL_WinGDI.h啊...我自己改别的试试...
2次B样条曲线...够了,这个够烦了...3次4次以后再说吧...  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理