一般情况下,程序开发人员都希望自己的程序美观大方,为了达到这一个要求,想尽办法美化自己的程序,另一方面,文字是程序界面上不可或缺的一种传递信息的途径,如果在界面上适当的地方添加并显示各种特殊效果的文字,将不失为一个好的选择,可以达到事半功倍的效果,本例针对Visual C++中编程实现文字的特殊显示效果这一问题,通过编写一个简单的小程序,介绍如何实现这一目标。读者朋友可以稍加改动,将该方法应用到自己的程序中去。
图一 字符串的环绕显示
图二、3D文字效果
一、实现方法
在讲述文字的各种处理效果之前,我们首先来介绍Visual C++中的MFC提供的用来创建字体的CFont类。对于Windows程序开发人员来说,可以创建的字体有两种:库存字体(该字体直接使用字体的索引选入设备上下文就可以了,例如:CDC::CreateStockObject(int nIndex ))和自定义字体。
CFONT类从CgdiObject派生而来,该类的对象可以通过Createfont()、CreateFontIndirect()等函数创建自定义的字体。两个函数原型分别如下:
CFont::CreateFontIndirect(const LOGFONT* lpLogFont );
CFont::CreateFont( int nHeight, int nWidth, intnEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename )。
其中第二个函数的参数和第一个函数中的LOGFONT类型的参数的的分量有一一对应的关系。LOGFONT结构主要定义了字体的属性,其定义可通过MSDN帮助查到:
typedef struct tagLOGFONT {
LONG lfHeight; //字符字体高度 ;
LONG lfWidth; //字符平均宽度 ;
LONG lfEscapement; //文本行逆时针旋转角度;
LONG lfOrientation; //字体角度 ;
LONG lfWeight; //字体粗细程度 ;
BYTE lfItalic; //倾斜 ;
BYTE lfUnderline; //下划线 ;
BYTE lfStrikeOut; //删除线 ;
……
} LOGFONT;
在对LOGFONT结构进行设置时,lfOutPrecision、lfClipPrecision、lfQuality 以及lfPitchAndFamily等几个成员变量一般可如下设置而无需改动:
LOGFONT lf; //lf定义字体属性
lf.lfOutPrecision= OUT_STROKE_PRECIS;
lf.lfClipPrecision= CLIP_STROKE_PRECIS;
lf.lfQuality = DRAFT_QUALITY;
lf.lfPitchAndFamily= VARIABLE_
PITCH|FF_MODERN;
对于其他的成员变量则要根据实际需求进行具体的设定。例如,对于没有下划线和删除线并且没有倾斜处理的粗体幼圆汉字,可以采取如下设置:
lf.lfHeight = 50;
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_HEAVY;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = FALSE;
lf.lfCharSet = GB2312_CHARSET; strcpy(lf.lfFaceName,"幼圆");
(一)显示倾斜的文字
上文中的图一显示了通过显示一系列的倾斜字符串来实现环绕显示的效果,下面来详细说明如何实现这个效果。
实现上述的效果其实很容易,只要使用CFONT类和LOGFONT结构来灵活的创建字体,设置字体的属性,就可以实现倾斜文字的效果。LOGFONT结构中包含了所要创建的字体中的全部信息,其中的ifEscapement 成员制定了所创建的字体与水平方向所倾斜的角度,需要读者注意的是该成员变量角度的单位是十分之一度而不是度,例如,如果ifEscapement定义为450,它表示字体的倾斜角度为45度。为了保证所有的字体按照一个方向旋转,一定要设置ifEscapenent的CLIP_LH_ANGLES位,否则字体有可能向反方向旋转。
如同使用其它GDI(图形用户界面接口)对象一样,在使用你定义的字体以前,必须要将创建的字体选入DC中(设备上下文)。
(二)实现3D文字
计算机屏幕是平面二维的,我们之所以能欣赏到真如实物般的三维图像,是因为显示在计算机屏幕上时色彩灰度的不同而使人眼产生视觉上的错觉,而将二维的计算机屏幕感知为三维图像。基于色彩学的有关知识,三维物体边缘的凸出部分一般显高亮度色,而凹下去的部分由于受光线的遮挡而显暗色。这一认识被广泛应用于网页或其他应用中对按钮、3D线条的绘制。对于本文所要绘制的3D文字同样也适用,即在原始位置显示高亮度颜色,而在左下或右上等位置用低亮度颜色勾勒出其轮廓,这样在视觉上便会产生3D文字的效果。具体实现时,可用完全一样的字体在不同的位置分别绘制两个不同颜色的2D文字,只要使两个文字的坐标合适,就完全可以在视觉上产生出不同效果的3D文字。
具体实现的思想是通过CDC::SetTextColor()分别设置文字的颜色为高亮(3DHILIGHT)和阴影(3DSHADOW)的状态下显示文字;同时注意在两次显示文字时要错开几个像素,这样才能达到预期的效果。实现的效果上文的图二所示。
(三)文字的渐变效果
为了实现文字的渐变效果,需要设置一个定时器(使用SetTimer()函数),在定时器响应函数处理过程中,通过调用CDC:SetTextColor()函数不断改变设备上下文中文本的显示颜色,从而实现文字的渐变效果。文本的颜色是通过RGB(红、绿、兰)三种基本颜色的混合所形成的最终结果,RGB三基色的变化范围都是(0,255),R=G=B时,颜色的效果是灰色的,所谓灰色,就是在纯白和纯黑之间的一种过渡色,当R=G=B=0时,颜色为黑色,当R=G=B=255时,颜色为纯白色,可以定义一个修正变量,不断的对该三基色进行递增或递减,从而实现文字的渐变显示。这里仅仅给出实现的思路,读者自己可以很容易的实现代码。
(四)其它
另外,还可以通过设置路径对象来对普通的文字进行轮廓勾勒,使之具备特殊的效果。路径是Win32中新增的一个GDI对象,下面先从概念上谈起。
1)路径的概念
在Windows 95/NT 这样的Win32操作系统中,除了已有的位图,画笔,画刷,字体,调色板和区域之外,还增加了一个新的GDI对象:路径。路径是可以被填充,画出轮廓或同时被画出轮廓并填充的一个或多个图形。路径的引入,大大地丰富了Windows的图形功能,使得应用程序可以方便地建立复杂区域,绘制和填充不规则图形。这里说的不规则图形是指由直线和贝塞尔曲线组成的图形(相对于矩形,多边形,椭圆等规则图形)。
2)路径的使用
与其它原有的GDI对象不同的是,MFC类库没有专门用一个C++类来封装路径对象(或许在以后的版本中会得到支持)。有关路径的定义和使用等各种操作都必须通过调用API函数(或CDC类中对应的成员函数)来实现。路径的使用过程大致如下:
(1)调用BeginPath()函数开始路径定义;
(2)调用GDI绘图函数来定义路径;在Win32中,可以用于定义路径的GDI绘图函数包括:AngleArc()、 Arc()、 ArcToChord()、Ellipse ()、LineTo()、TextOut()等函数;
(3)调用EndPath()函数结束路径定义;
完成路径定义后,所定义的路径即被同时选进设备描述表,设备描述表中原有的路径对象在调用BeginPath()函数开始路径定义时即被废弃。
(4)使用路径对象。
完成路径定义工作之后,应用程序便可以利用有关GDI函数来使用路径,这些函数包括绘制路径轮廓StrokePath(),填充路径FillPath(),绘制轮廓并填充StrokeAndFillPath(),把路径转换成区域PathToRegion(),把路径直线化FlattenPath(),提取路径数据GetPath(),加宽路径WidenPath()和设置裁剪路径SelectClipPath()等。这些函数的具体使用方法可参阅有关的SDK文档。
下面的代码演示了如何实现字体的空心效果
////////////////////////////////////// 应用程序主窗口的重绘函数
void CMyWnd::OnPaint()
{
// 获得窗口的客户区设备上下文句柄
CPaintDC dc(this); // 更改当前字体
LOGFONT lf;
dc.GetCurrentFont()->GetLogFont(&lf);
CFont font;
CFont *pOldFont; // 保存设备上下文最初使用的字体对象
lf.lfCharSet=134;
lf.lfHeight=-150;
lf.lfHeight=-150;
lf.lfWidth=0;
strcpy(lf.lfFaceName, "隶书");
font.CreateFontIndirect(&lf);
pOldFont=dc.SelectObject(&font);
dc.SetBkMode(TRANSPARENT); // 更改当前画笔
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
CPen *pOldPen;
pOldPen=dc.SelectObject(&pen); // 开始一个路径
dc.BeginPath();
dc.TextOut(10, 10, "空心字");
dc.EndPath(); // 绘制路径
dc.StrokePath();
//可以用dc.StrokeAndFillPath()函数来代替,不过该函数会使用当前刷子填充路径的内部。
dc.SelectObject(pOldFont);
dc.SelectObject(pOldPen);
}
二、编程步骤
1、启动Visual C++6.0,生成一个单文档视图结构的程序(对于旋转字体的程序:项目名为ViewFont;对于3D效果的程序:项目名为 3DText);
2、重载应用程序的OnDraw(CDC* pDC)函数;
3、添加代码,编译运行程序;
三、程序代码
////////////////////////////////////旋转字体
void CViewFontView::OnDraw(CDC* pDC)
{
CViewFontDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CString str(_T("Visua C++6.0编程实例"));//定义要显示的字符串;
CRect rect;
GetClientRect(&rect);
pDC->SetBkMode(TRANSPARENT);//设置背景为透明效果;
pDC->SetTextColor(RGB(100,100,255));//设置显示的文本颜色;
CFont font;
LOGFONT stFont;//定义字体结构;
memset(&stFont,0,sizeof(stFont));//设置字体结构的属性;
stFont.lfHeight=30;
stFont.lfWeight=FW_NORMAL;
stFont.lfClipPrecision=CLIP_LH_ANGLES;
strcpy(stFont.lfFaceName,"Arial");
//下面的代码以视图中心为圆点,在半径100的圆周上每隔30度显示字符串;
for(double i=0;i<3600;i+=300)
{
stFont.lfEscapement=i;//设置字体的倾斜角度;
font.CreateFontIndirect(&stFont);//根据字体结构创建字体对象;
CFont *oldFont;
oldFont=pDC->SelectObject(&font);//将创建的字体选入设备上下文;
if(i<900)
pDC->TextOut(rect.left+rect.Width )/2+100*cos(i/1800*3.14),rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str);
if(i>=900&&i<1800)
pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14),
rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str);
if(i>=1800&&i<2700)
pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14),
rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str);
if(i>=2700&&i<=3600)
pDC->TextOut(rect.left+rect.Width ()/2+100*cos(i/1800*3.14),
rect.top+rect.Height ()/2-100*sin(i/1800*3.14),str);
pDC->SelectObject(oldFont);//恢复设备上下文的字体;
font.DeleteObject ();//删除创建的字体;
}
}
//////////////////////////////////////////显示3D效果的程序
void CMy3DTextView::OnDraw(CDC* pDC)
{
CMy3DTextDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CString string;
string="Visual C++6.0编程实例";
CFont m_fontLogo;
m_fontLogo.CreateFont(144, 0, 0, 0, 155, FALSE, FALSE,0,0,0,0,0,0, "Arial");
//创建字体;
pDC->SetBkMode(TRANSPARENT);
CRect rectText;
GetClientRect(&rectText);
CFont * OldFont = pDC->SelectObject(&m_fontLogo);
COLORREF OldColor = pDC->SetTextColor( ::GetSysColor( COLOR_3DSHADOW));
//阴影状态显示文字;
pDC->DrawText( string, rectText+CPoint(2,2) , DT_SINGLELINE | DT_LEFT |DT_VCENTER|DT_CENTER);
pDC->SetTextColor(::GetSysColor( COLOR_3DHILIGHT) );
//高亮状态显示文字;
pDC->DrawText( string, rectText, DT_SINGLELINE | DT_LEFT |DT_VCENTER|DT_CENTER);
pDC->SetTextColor( OldColor);
pDC->SelectObject(OldFont);
m_fontLogo.DeleteObject();
}
四、小结
读者只要掌握了上面的内容,灵活运用Cfont和LOGFONT结构创建不同的字体,再结合显示颜色、位置、时间的设置,相信一定还可以实现更多的动态效果来.