调整 TrueType 字体文本质量(第 2 部分,共 2 部分)

本文是关于设置 TrueType 字体文本质量系列文章的一部分。有关上一篇文章,请参阅第 1 部分

在上一篇文章中,我们描述了使用常量设置更改 TrueType 字体文本质量的方法。本文描述了使用 TTF PolyDraw 模式动态更改质量的方法。

动态 TTF PolyDraw 模式

TTF PolyDraw 模式避免了文本字符的镶嵌,直到渲染器或 ODA 矢量化框架客户端需要实际的镶嵌数据。这意味着字体管理器将贝塞尔曲线信息传递给几何输送器,而不是预镶嵌几何体,因此客户端代码可以使用 TrueType 字体处理所需的任何质量来镶嵌文本。例如,ODA 矢量化模块可以使用当前可用的偏差来镶嵌此类文本字符,因此文本质量在任何缩放级别都很好。相比之下,常量文本质量字体管理器不需要在系统内存中保留大量的预镶嵌顶点数组,因此字体字符在准备图形缓存时会以所需的质量进行镶嵌。

在 OdaMfcApp 示例应用程序中使用 TTF PolyDraw 模式

要在 OdaMfcApp 示例应用程序中启用 TTF PolyDraw 模式,请选择“工具”->“选项”->“TTF: PolyDraw 模式”:

 

image1

缩小以使文本远离:

 

image2

 

调用 REGEN 命令更新图形缓存,然后缩放至文本:

 

image3

 

当文本质量变得太粗糙时,再次调用 REGEN 命令以更好的质量更新图形缓存:

 

image4

 

接下来缩放至文本以使其更近:

 

image5

 

image6

在任何缩放级别,我们都可以调用 REGEN 命令以当前缩放级别的最佳质量更新图形缓存:

 

image7

 

为 .dwg 数据库启用 TTF PolyDraw 模式

要为 .dwg 数据库启用 TTF PolyDraw 模式,只需调用 OdGiContextForDbDatabase::setTtfPolyDrawMode() 方法(参见 ODA 文档,需要登录):

pGiContextForDwgDatabase->setTtfPolyDrawMode(true);

通用地启用 TTF PolyDraw 模式

启用 TTF PolyDraw 模式的通用方法(适用于所有基于 Kernel SDK 的 ODA 产品)是重写 OdGiContext::ttfPolyDraw() 方法(参见 ODA 文档,需要登录)。OdGiContext 由所有 ODA 矢量化过程调用,因此 ttfPolyDraw() 方法可用于为其中任何一个启用 TTF PolyDraw 模式。默认情况下,ttfPolyDraw() 方法返回 false。每个 ODA 产品,包括 Drawings(用于 .dwg 和 .dgn 文件)、BIM 和 PRC,都有自己的 OdGiContext 实现;例如,在 Drawings SDK 中,此类的名称是 OdGiContextForDbDatabase。每个 OdGiContext 实现都可以有自己的 ttfPolyDraw() 方法的重写和实现,如 OdGiContextForDbDatabase 中所示,但没有问题可以在继承自所需 OdGiContext 实现的自己的类中再次重写此方法,并将其传递给 ODA 矢量化框架。

在矢量化传送带中支持 TTF PolyDraw 数据

TrueType 字体文本字符通常使用壳几何图元绘制,这些图元可通过 shellProc 方法重写在几何传送带中访问。如果启用了 TTF PolyDraw 模式,则会为 TrueType 字体字符调用 ttfPolyDrawProc 几何传送带方法,而不是 shellProc。如果客户端代码未重写 ttfPolyDrawProc 方法,则由 OdGiGeometrySimplifier::ttfPolyDrawProc 方法实现处理,并转换为具有当前设置偏差的 shellProc 几何图元(有关更多详细信息,请参阅 ODA 博客)。

客户端代码可以重写 OdGiConveyorGeometry::ttfPolyDrawProc 方法以独立处理 PolyDraw 数据。例如,在 TXTEXP 命令中就是这样做的,以特定精度对 TrueType 字体文本轮廓进行细分。

void ttfPolyDrawProc(OdInt32 numVertices, const OdGePoint3d* vertexList,
OdInt32 faceListSize, const OdInt32* faceList,
const OdUInt8* pBezierTypes, const OdGiFaceData* pFaceData = 0)

请参阅 .\main\Drawing\Examples\ExCommands\ExTxtExp.cpp,TXTEXP 命令源代码。ttfPolyDrawProc 方法包含与 shellProc 类似的参数,但它通过 pBezierTypes 值数组进行了扩展。此数组中的条目数量与传递给 ttfPolyDrawProc 方法的顶点数量相似。下表描述了此数组中可能存在的值:

值(位标志) 描述
1(闭合图形) 此值通常与“直线到”或“贝塞尔曲线到”标志结合使用,表示当前绘制的图形必须闭合。在这种情况下,下一个(如果存在)值表示下一个图形的第一个点。
2(直线到) 从前一个顶点到当前顶点绘制的简单直线。
4(贝塞尔曲线到) 贝塞尔曲线的控制点。贝塞尔曲线由四个顶点组成,其中最后三个顶点由该标志标记。
6(移动到) 标记不相连图形的第一个点。

我们可以将此位标志形式化为 C++ 枚举:

enum pdFlags
{
kpdCloseFigure = 0x01,
kpdLineTo      = 0x02,
kpdBezierTo    = 0x04,
kpdMoveTo      = 0x06
};

以下来自 TXTEXP 命令示例的简化代码片段演示了如何处理此标志数组以创建一组折线:

int nVerts = 0; // Number of vertexes currently processed for this polyline
OdDbPolylinePtr pPl; // Currently processing polyline
int bzCnt = 0; // Number of points collected for Bezier curve
OdGePoint3d bzSpl[4]; // Collected Bezier curve points
for (OdInt32 i = 0; i < numVertices; i++)
{
if (pBezierTypes[i] == kpdMoveTo)
{ // Create new polyline and add first vertex
pPl = OdDbPolyline::createObject();
nVerts = 0;
pPl->addVertexAt(nVerts++, vertexList[i].convert2d());
}
else
{
if (GETBIT(pBezierTypes[i], kpdLineTo))
{ // Add line segment
pPl->addVertexAt(nVerts++, vertexList[i].convert2d());
}
else if (GETBIT(pBezierTypes[i], kpdBezierTo))
{
bzCnt++; // Collect next Bezier curve vertex
bzSpl[bzCnt] = vertexList[i];
if (bzCnt == 3)
{
const int nSegs = 8; // Set number of segment for Bezier curve tessellation
// This is a sequence of 3 bezierTo points + previous point, so when we collect 3rd bezierTo,
// we could construct cubic spline based on 4 points.
. . .
// Add last vertex
pPl->addVertexAt(nVerts++, vertexList[i].convert2d());
bzCnt = 0;
}
}
}
if (GETBIT(pBezierTypes[i], kpdCloseFigure))
{
appendEntity(pPl); // Add currently processed polyline
// Create next polyline for processing and add first vertex
pPl = OdDbPolyline::createObject();
nVerts = 0;
pPl->addVertexAt(nVerts++, vertexList[i].convert2d());
}
}

这段代码使用经典算法将由四个顶点组成的累积三次样条曲线细分为所需数量的折线段:

const int nSegs = 8; // Set number of segments for Bezier curve tessellation
// This is a sequence of 3 bezierTo points + previous point, so when we collect 3rd bezierTo,
// we could construct cubic spline based on 4 points.
pPl->getEndPoint(bzSpl[0]); // Get previous point for tessellation
double d = 1. / nSegs;
for (int n = 1; n < nSegs; n++)
{
double t = d * n;
double u = 1.0 - t;
double tt = t * t, uu = u * u;
double uuu = uu * u, ttt = tt * t;
double uut3 = uu * t * 3, utt3 = u * tt * 3;
          
pnt = OdGePoint3d(
bzSpl[0].x * uuu + bzSpl[1].x * uut3 + bzSpl[2].x * utt3 + bzSpl[3].x * ttt,
bzSpl[0].y * uuu + bzSpl[1].y * uut3 + bzSpl[2].y * utt3 + bzSpl[3].y * ttt,
bzSpl[0].z * uuu + bzSpl[1].z * uut3 + bzSpl[2].z * utt3 + bzSpl[3].z * ttt);
pPl->addVertexAt(nVerts++, pnt.convert2d());
}
// Add last vertex
pPl->addVertexAt(nVerts++, vertexList[i].convert2d());
bzCnt = 0;

执行此代码后,我们得到一组折线输出,它们代表具有给定质量(细分精度)的 TrueType 字体字符轮廓。

这是关于调整 TrueType 字体文本质量系列文章的最后一篇。

今天就开始行动

免费试用 ODA 软件 60 天。
无风险,无需信用卡。

免费试用