以P0(0,0)、P1(2,3)、P2(4,0)为特征多边形,能生成几次Bezier曲线?

      TrueType字体用machintosh的轮廓字体资源的格式编碼有一个唯一的标记名"sfnt"。windows没有macintosh的位图字体资源格式字体目录包含了字体格式的版本号和几个表,每个表都有一个tableentry结构项tableentry结构包含了資源标记、校验和、偏移量和每个表的大小。

TrueType 字体中的所有数据都使用big-endian编码最高位字节在最前面(因为TrueType字体最初是由apple公司定义的,而apple公司的os 运行在motorola的cpu上)如果一人TrueType字体以00 01 00 00 ,00 17开头,我们就可以知道它的格式是轮廓字体资源("sfnt")版本1.0的格式有23个表。

TableDirectory结构的最后一个字段是可變长度的tableentry结构的数组安体中的每个表对应其中一项。TrueType字体中的每个表都保存了不同的逻辑信息-----如图元中数据、字符到图元的映射、字距調整信息等等有表是必须的,有些是可选的下表列出了TrueType字体中常见的表。

head 字体头 字体的全局信息
cmap 字符代码到图元的映射 把字符代码映射为图元索引
glyf 图元数据 图元轮廓定义以及网格调整指令
maxp 最大需求表 字体中所需内存分配情况的汇总数据
mmtx 水平规格 图元水平规格
loca 位置表索引 紦元索引转换为图元的位置
name 命名表 版权说明、字体名、字体族名、风格名等等
hmtx 水平布局 字体水平布局星系:上高、下高、行间距、最大前進宽度、最小左支撑、最小右支撑
kerm 字距调整表 字距调整对的数组

     GetFontData使得应用程序能够在自己的文档中内嵌TrueType字体以确保这些文档能在没有相應字体的其他机器上显示。它的做法是允许应用程序查询字体数据然后写入到文档中作为文档的一部分,在文档被打于时再安装该字体鉯确保文档能以创建时同样的方式显示比如,Windows NT/2000的假脱机程序在打印到远端服务器时会在假脱机文件中内嵌入TrueType字体以保证文档能在另一台機器上正确地打印
    一旦接受到TrueType字体的原始数据,它的头中的TableDirectory结构很容易分析需要检查的只有版本号和表的数目,然后就可以检查单个嘚表我们来看一些重要的和有趣的表。

字体设计时是针对一个参考网格设计的该网格被称为em-square,字体中的图元用网格中的坐标表示因此em-squrare的大小决定胃该字体的图元被缩放的方式,同时也反映胃该字体的质量字体头中保存了每个em-square的格数和能包含所有图元的边界框。Em-square的有效值是从16到16384常见的值是2048、4096和8192。比如Windings字体的em- 字体头表中的其他信息包括最小可读像素大小、字体方向、在位置表中图元索引的格式和图え数据格式等等。

numGlyphs字段保存了字体中图元的总数这决定了到位置表的图元索引的数量,可以用于严正图元索引的有效性TrueType字体中的每个圖元都可以是合成图元或简单图元。简单图元可以有一条或多大体上轮廓中国条用一些控制点定义。合成图元用几个其他图元的组合来萣义 maxPoints/maxCountors/maxCompositePoints 除了图元的定义,TrueType字体还使用了图元指令用于提示字体扫描器如何对控制点进行调整以得到更均衡更漂亮的光栅化后的图元图元指令也可以出现在字体程序表(fpgm表)以及控制值程序表(“prep”)的全局字体层中。TrueType图元指令是一个伪计算机字节指令该机类似于Java的虚拟機,这些指令可以用堆栈计算机执行MaxStackElements 以Windings字体为例,该字体有226个图元图元最多有47条轮廓线,简单图元最多有268个点合成图元最多有141个点,合成图元最多有14条轮廓线最坏情况下需要492层堆栈,最长的指令有1119个字节
字符到图元索引的映射表(cmap表)定义了从不同代码页中的字苻 代码到图元索引的映射关系,这是在TrueType字体中存取图元信息的关键

cmap表 包含几个了表以支持不同的平台和不同的字符编码方案。

下面是cmap表嘚结构

通常一种字体只提供UNICODE字符集中的字符的一个子集。这些字符可以被分组为多个区域cmap映射表中就是这么做的。 GetFontUnicodeRanges函数在一个GLYPHSET结构中返回支持的图元的数量、支持的UNICODE区域的数量以及设备上下文中字体的这些区域的详细信息GLYPHSET是一个可变长的结构 ,其大小取决于所支持的UNICODE區域的数量因此,和Win32 API中支持可变长结构一样 GetFontUnicodeRanges函数通常需要调用两次。第一次调用时得到以NULL指针作为最后一莜参数GDI会返回所需窨的大尛。调用者然后分配所需的内存再次调用以得到真正的数据。这两种情况下GetFontUnicodeRanges函数都会返回保存整个结构所需的数据大小。MSDN文档可能还昰错误地描述成了如果第二个参数是

GetGlyphIndices函数则能真正使用这些映射关系把一个字符串转换为一个图元索引的数组它接收一个设备上下文句柄、一个字符串指针、字符串长度、一个WORD数组的指针和一个标志。生成的图元索引将保存在WORD数组中如果标志为GGI_MASK_NONEXISTING_GLYPHS, 找不到的字符的图元索引會被标注成0xFFFF。此函数得到的图元索引可以传给其他GDI函数,如ExtTextOut函数

TrueType字体中最有用的信息是glyf表中的图元数据。有了图元索引要找到相应的图え,需要表(loca表)索引以把图元索引转换为图元数据表内的偏移量
位置索引表中保存了n+1个图元数据表的索引,其中n是保存在最大需求表Φ的图元数量最后一个额外 的偏移量并不指向一个新图元,而是指向最后一个图元的偏移量和当前图元的偏移量和当前图元的偏移量间嘚差值得到图元的长度
位置索引表中的每一个索引以无符号短整数对齐的,如果使用了短整数格式索引表实际存储的是WORD偏移量,而不昰BYTE偏移量这合得短整数格式的位置索引表能 支持128KB大小的图元数据表。

对于简单图元numberOfContours字段中保存的是当前图元的轮廓线的树木;对于合荿图元,numberOfContours字段是一个负值后者的轮廓线的总数必须基于组成该合成图元的所有图元的数据计算得到。GlyphHeader结构中后四个字段记录了图元的边堺框
对于简单图元,图元的描述紧跟在GlyphHeader结构之后图元的描述由几部分信息组成:所有轮廓线结束点的索引、图元指令和一系列的控制點。每个控制点包括一个标志以x和y坐标概念上而言,控制所需的信息和GDI函数PolyDraw函数所需的信息相同:一组标志和一组点的坐标但 TrueType字体中嘚控制点的编码要复杂得多。下面是图元描述信息的概述:


图元可以包含一条或多条轮廓线比如,字母"O"有两条轮廓线一条是内部的轮廓,另一条是外部的轮廓对于每一条轮廓线,endPtsOfContours数组保存了其终点的索引从该索引中可以计算出轮廓线中点的数量。比如endPtsOfContours[0]是第一休轮廓线上点的数量,endPtsOfContours[1]-endPtsOfContours[0]是第二条轮廓线上点的数量
终点数组后是图元指令通知度和图元指令数组。我们先跳过它们先来讨论冬至点。图元嘚控制点保存在三个数组中:标志获得组、x坐标数组和y坐标数组找到标志数组的起始点很简单,但是标志数组没有相应的长度字也没囿直接其他两个数组的方法,你必须先解码标志数组才能解释x和y坐标数组
我们提到棕em-square被限制为最大为16384个网格,因此通常情况下需要各两個字节来表示x坐标和y坐标为了节省空间,图元中保存的是相对坐标第一个点的坐标是相对(0,0)记录的所有随后的点记录者是和上┅个点的坐标差值。有些差值可以用一个字节表示有些差值为0,另外一些差值则无法用耽搁字节表示标志数组保存了每个坐标的编码信息以及其他一些信息。下面是标志中各个位的含义的总结:

在第8章中我们讨论了直线和曲线我们提到了一段三阶Bezier曲线有四个控制点定義:位于曲线上(on-curve)的起始点、两个不在曲线上(off-curve)的控制点和一个曲线上的结束点。TureType字体中的图元轮廓是用二阶Bezier曲线定义的有三个点:┅个曲线上的点,一个曲线外的点和另一个曲线上的点多个连续的不在曲线上的点是允许的,但不是用来定义三阶或更高阶的Bezier曲线而昰为了减少控制点的数目。比如对于on-off-off-on模式的四个点,会加入一个隐含的点使之成为on-off-on-off-on,因此定义的是两段二阶Bezier曲线
如果设置了G_ONCURVE位,那么控淛点在曲线上否则不在曲线上。如果设置了G_REPEAT,标志数组中的下一字节表示重复次数当前标志应该重复指定的次数。因此标志数组中实際使用了某种类型的行程编码。标志中的其他位用于描述相应的x坐标和y坐标的编码方式它们可以表示当前相寻坐标是否和上一个相同、囸的单字节值、负的单字节值或有符号两字节值。
解码图元的描述是一个两次扫描的起始点然后再遍历图元定义中的每一个点把它转换為更容易管理的格式。程序清单14-2列出了解码TrueType图元的函数它是KTrueType类的一个方法。

KTrueType类处理TrueType字体的装入和解码随书光盘中有它的完整源代码。DecodeGlyph給出图元索引和可选的变换矩阵处理的是单个图元的解码。参数curve是KCurve类用于把TrueType图元定义保存为32位的点的赎罪以及一个标志数组,以梗用GDI進行显示这些代码可以作为简单TrueType字体编辑器的基础。
代码中调用了GetGlyph方法该方法用位置表索引找到该图元的GlyphHeader结构。从中得到图元的轮廓線数目注意必须反转该值的字节序,因为TrueType字体用的是Big-Endian字节序如果该值为负值,说明这是一个合成图元应该转而调用 DecodeCompositeGlyph方法。接下支的玳码定位了endPtsOfContours数组找出点的总数,然后跳过指令找到标志数组的起始位置
接下去需要长到的是x坐标数组的始位置和长度,这需要遍历标誌数组一次对于每一个控制点,它在x坐标数组中所占空间可能为0到2个字节这取决于它的相对坐标是0、单个字节还是两个字节。
根据x坐標数组的地址和长度可以得到y坐标的地址接下去的代码遍历所有的轮廓线,解码其中的控制点把相对坐标转换为绝对坐标,然后把它加入到曲线对象中如果需要的话,会对每个控制点做变换
回想一下,TrueType使用的是二阶Bezier曲线允许在两个曲线上的点之间有多个不在曲线仩的点。为了简化曲线绘制算法KCurve::Add方法在每两个不在曲线上的点之间加入一个额外的在曲线上的点。

处理了简单图元之后我们来看看合荿图元。合成图元用一个经变换的图元序列定义每个经变换的图元的定义包括三个部分:一个标志、一个图元索引和一个变换矩阵。标誌字段决定了变换矩阵的编码方式编码的目的也是为了节省一些空间,加外还说明了是否已到达序列的终点一个完整的2D affine变换需要6个值。但如果只是平移的话只需要两个值(dx,dy),这两个值可以保存为两个字节或两个字如果x和y以相同的值缩放,加外还需要一个缩放值取一般的情况下仍然需要6个值,但是很多时候可以节省几个字节用于变换的值以2.14的有符号定点格式保存,dx和dy值除外这两个值以整数形式保存。得到合成图元的过程实际上是变换和组合几个图元的过程比如,如果字体中的一个图元是另一个图元的精确镜像它只需定义為一个合成图元,可以通过对另一个图像做镜像变换即可程序清单14-3列出了解码合成图元的代码。

DecodeCompositeGlyph方法解码每个图元的标志、图元索引和變换矩阵然后调用DecodeGlypgh方法进行解码。注意对 DecodeGlyph方法的调用包含一个有效的变换矩阵参数。当MORE_COMPONENTS标志结束时该方法随之结束。随书光盘中有該方法完整的源代码
解码后的TrueType字体的图元要用GDI绘制还有一个小问题需要处理。GDI只绘制三阶Bezier曲线因此从图元表解码所得的二阶Bezier 曲线的控淛点需要转换为三阶Bezier曲线的控制点。通过对Bezier曲线原始数学定义的研究可以得到如下用GDI绘制二阶Bezier曲线的简单例程。


       程序清单14-2和14-3给人的印象昰TrueType字体的栅格器可以通过扫描和转换图元的轮廓来轻松地实现比如,用GDI和 StrokeAndFillPath函数来填充图元轮廓绘制出来的路径这种简单的字体栅格器嘚实现并不是很有用,除非它只用于高分辨诣的设备如打印机等
简单栅格器得到的图像笔画粗细不一,有信息的遗漏有字符特征的损夨以及不对称等缺陷。当点阵变小是情况不会更糟。总之简单字体栅格器在小尺寸时会产生字迹模糊的结果。在大尺寸时会产生不好看的结果只有在点阵增大时结果才会改善。
当在大的em-square(典型的是2048)中定义的图元轮廓缩小为小得多的网格时(如32*32)不可避免会损失精度并引入误差。

TrueType解决这个问题的方法是控制图元轮廓从em-square到栅格网格的缩放过程使得到的结果看起来效果更好,和原始图元的设计尽量接近这种技术被称为网格调整(grid fitting),它想达到的目标有:
消除网格位置的可能影响保证笔画的粗细和网格的相对位置无关。
控制图元Φ关键位置的尺寸
保持对称性和衬线等 重要的图元设计细节

TrueType字体中网格调整的需求在两个地方中编码:控制值表(control value table)和每个图元的网格调整指令。
控制值表("cvt"表)用于保存一个数组这些值被称为网格调整指令。比如对于有衬线的字体,基线、衬线高度、大写字母笔划的宽喥等值都或以是被控制的值它们可以以字体设计者已知的次序保存在控制值表中,然后用它们的索引来引用在字体光栅化过程中,控淛值表中的值根据点阵的大小缩放在网络调整指令中引用这些值可以保证使用的值与网枸的位置无关。比如如果水平线[14,025,200]可以用CVT表中的两个值定义为 每一个图元的定义中附加有一个指令序列该指令序列被称为图元指令,该背景令序列用于控制此图元的网格高速圖元指令线用控制值表中的值,以保证在索引图元中这些值相同
图元指令是一种基于堆栈的伪计算机的指令。堆栈计算机常用于计算机語言的解释性实现比如,Forth(用于嵌入式系统的一种强大而简洁的语言)、RPL(HP计算器使用的语言)和Java虚拟机都是堆栈计算机
堆栈计算机通瑺没有寄存器,所有的计算都在堆栈上进行(有些堆栈计算机使用分开的控制堆栈和数据堆栈)比如,压入指令把一个值压入堆栈弹絀指令从堆栈中弹出上面的值,二元加法指令弹出上面的两 个值 然后把它们的和压入堆栈。

}

0阶参数连续性(C0):是指曲线的幾何位置连接即第一个曲线段的终点与第二个曲线段的起点x,y,z值相等;

1阶参数连续性(C1):在C0的基础上,该始末点的导数相等;

2阶参数连續性(C2):在C1的基础上该始末点的二阶导相等;

2. 几何连续性(条件不太苛刻)

0阶几何连续性(G0):同0阶参数连续性;

1阶几何连续性(G1):在满足G0条件下,两曲线结合处有公共切矢(方向相同大小成比例);

2阶几何连续性(G2):在满足G1条件下,两曲线结合处有公共曲率;

②、Bezier曲线与曲面

可以把曲线表示为许多小线段Φi(x)之和其中Φi(x)称为基(混合)函数;

其中系数矢量ai(i=0,1,...,n)顺序首尾相接;

3. 贝塞尔基函数的替换->伯恩斯坦(Bernstain)基函数

1972年,剑桥大学的博士生Forrest在《Computer Aided Design》发表了他一生中最著名的论文Forrest证明了Bezier曲线的基函数可以简化成伯恩斯坦基函数:

一个连续函数y=f(x),任何一个ξ>0总能找到一个多项式和这个函数足够逼近。伯恩斯坦这套逼近的理论的形式是:

针对Bezier曲线给定空间n+1個点的位置矢量Pi(i=0,1,2,...,n),则Bezier曲线段的参数方程表示如下:

二项式定理又称牛顿二项式定理。

这恰好是连接起点P0和终点P1的直线段!

二次Bezier曲线曲线为抛物线其矩阵形式为:

把Bezier三次曲线多项式写成矩阵形式:

1)端点性质:和Pn分别位于实际曲线段的起点和终点上;

2)一阶导数:Bezier曲線的起点和终点处的切线方向和特征多边形的第一条边及最后一条边的走向一致;

3)几何不变性:Bezier曲线的形状仅与控制多边形各定点的相對位置有关,而与坐标系的选择无关;

4)变差缩减性:若Bezier曲线的特征多边形是一个平面图形则平面内任意直线与p(t)的交点个数不多于該直线与其特征多边形的交点个数。

1)根据定义直接生成Bezier曲线

a)首先给出Cin的递归计算式:

b)将表示成分量坐标形式:

根据以上的公式可鉯直接写出绘制Bezier曲线的程序

以二次Bezier曲线为例,求曲线上t=1/3的点:

二次Bezier曲线P02可以定义为分别由前两个顶点(P0,P1)和后两个顶点(P1,P2)决定的一次Bezier曲线的线性组合

由(n+1)个控制点Pi(i=0,1,...,n)定义的n次Bezier曲线n可被定义为分别由前、后n个控制点定义的两条(n-1)次Bezier曲线P0n-1与P1n-1的线性组合:

由此得到Bezier曲線的递推计算公式:

 三、B样条曲线与曲面

1. 引入——Bezier曲线的不足:

1)一旦确定了特征多边形的顶点数(n+1),也就决定了曲线的阶次(n);

2)Bezier曲線或曲面的拼接比较复杂;

3)Bezier曲线或曲面不能作局部修改;

2. B样条的递推定义和性质

B样条曲线的数学表达式为:

Bi,k(u)称为k阶(k-1次)B样条基函数k昰刻画次数的。其中k可以是2到控制点个数n+1之间的任意整数对于Bezier曲线来说,阶数和次数是一样的;但对B样条阶数是次数加1。

3. B样条曲线的萣义:

}

你这个问题有点麻烦就是无法嘚到你要通过平滑得到的目标曲线函数。

如果你的曲线就是那三条线段的简单合成问题就简单了:


当用户输入 x 或 y 坐标时,你只需先查出咜落在那个区段然后采用相应线段参数来计算就可以了:

}

我要回帖

更多关于 P1P2 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信