65.9K
CodeProject 正在变化。 阅读更多。
Home

绘制倾斜/斜体文本字符串

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (8投票s)

2007年4月19日

3分钟阅读

viewsIcon

58448

downloadIcon

580

本文展示了如何绘制倾斜/斜体文本字符串

引言

这里提供的函数可用于绘制具有倾斜或斜角的文本字符串。这种文本输出在等距或透视3D视图中非常有用,使文本字符串看起来像在它们的3D空间中一样。以下图片显示了一个例子

Screenshot - obluqie1.jpg

背景

Windows GDI 函数 TextOut() 不允许文本倾斜角。要绘制这种倾斜的字符串,我们需要使用 SetWorldTransform() 设置转换。然后,Windows 绘图函数将负责输出的剪切和旋转。此过程被整合到一个类似于 Windows TextOut() 函数的新函数中

void ObliqueTextOut( CDC *dc, int oblique, int x,int y,const CString &Text )

此函数具有与 Windows TextOut() 函数相同的参数,外加一个附加参数 oblique,用于指定文本倾斜角。该函数可以放置在通常使用 TextOut() 的地方。

使用代码

将函数源代码插入到您的源代码文件中。在通常调用 Windows TextOut() 函数的地方调用该函数。请记住选择字体,设置文本背景模式、颜色和背景颜色等,就像在调用 TextOut() 之前通常所做的那样。

如果文本向前倾斜(向右),则角度 oblique 为正,如果文本向后倾斜(向左),则角度 oblique 为负。下图中的 oblique 角度 s 为正。角度以 1/10 度为单位。因此,如果文本向前倾斜 15 度,则 oblique=150。

关注点

问题的关键是在 DC 中设置转换。函数 SetWorldTransform() 需要一个 XFORM 结构来进行转换。因此,我们需要在调用 SetWorldTransform( ) 之前准备 XFORM 结构。XFORM 具有 6 个成员数据。它们是 eM11eM21eM12eM22eDxeDx。它们定义为

X = eM11 * x + eM21 * y + eDx
Y = eM12 * x + 2M22 * y + eDy

其中 (x,y) 是世界坐标,(X,Y) 是纸张空间坐标。

在下图中,x,y 是世界空间轴。字符串将始终在世界空间中的 (0,0) 处水平绘制。xs,ys 是剪切空间轴。从世界空间到剪切空间的变换是

xs = x - y * tan(s)
ys = y

其中 s 是倾斜或斜角。

Screenshot - oblique3.jpg

纸张空间表示为 X,Y。从剪切空间到纸张空间,变换是一个旋转(角度 r)和平移 (Xo,Yo)

X = Xo + xs * cos(r) + ys * sin(r)
Y = Yo + ys * cos(r) - xs * sin(r)

其中 (Xo,Yo) 只是纸张空间中的文本插入点。将 (xs,ys) 代入以上公式,我们得到

X = cos(r) * x + (sin(r)-tan(s)*cos(r)) * y + Xo
Y = -sin(r) * x + (cos(r)+tan(s)*sin(r)) * y + Yo

将其与 XFORM 结构进行比较,很明显

eM11 = cos(r)
eM21 = sin(r) - tan(s) * cos(r)
eM12 = -sin(r)
eM22 = cos(r) + tan(s) * sin(r)
eDx = Xo
eDy = Yo

以上内容被翻译成函数代码(dc 是输入设备上下文)

XFORM xForm;
xForm.eDx = (float) x;
xForm.eDy = (float) y;
xForm.eM11 = (float) cos(txtRotate);
xForm.eM21 = (float) (sin(txtRotate) - tan(txtOblique)*cos(txtRotate));
xForm.eM12 = (float) -sin(txtRotate);
xForm.eM22 = (float) (cos(txtRotate) + tan(txtOblique)*sin(txtRotate));
SetGraphicsMode( dc->m_hDC, GM_ADVANCED );
SetWorldTransform( dc->m_hDC, &xForm );

需要调用 SetGraphicsMode()。否则,函数 SetWorldTranform() 将不起作用。由于现在我们在世界空间中绘图,我们需要调整字体的旋转 (lfEscapement) 以使其水平,并将字符方向 (lfOrintation) 设置为从世界 X 轴开始。

LOGFONT lgf;
dc->GetCurrentFont()->GetLogFont( &lgf );
...
lgf.lfOrientation -= lgf.lfEscapement;
lgf.lfEscapement = 0;
CFont horFont;
horFont.CreateFontIndirect( &lgf );
CFont *OldFont = dc->SelectObject( &horFont );

现在,我们可以调用

dc->TextOut( 0,0, Text );

工作完成了。但在返回之前,我们需要恢复图形模式和字体

ModifyWorldTransform( dc->m_hDC, &xForm, MWT_IDENTITY );
SetGraphicsMode( dc->m_hDC, GM_COMPATIBLE );
dc->SelectObject( OldFont );

历史

  • 2007 年 4 月 19 日:通过操作位图的版本 1
  • 2007 年 4 月 25 日:版本 2 - 按照 Goran Mitrovics 的建议完全重写。它更简单,输出质量更好。非常感谢 Goran!
© . All rights reserved.