【VB.NET】GDI+部分文字特效的原理解析

vb.net

今天翻找出了很久以前一篇微软官方的GDI+文字特效的示例,分析了一下里面关于GDI文字特效的原理,先放源码。

本篇示例演示了在GDI+环境下的文字画笔、文字颜色渐变(文字渐变色)、文字阴影、文字浮雕、文字3D块、文字反射、文字扭曲、多行文字(文字自动换行)

 

工程下载(VS2008)

关键代码:

View Code
#Region "块文本"
' 此 Sub 创建具有浮雕效果的示例文本。
' 为了创建出效果,示例文本将分两次绘制。
' 第一次用黑色绘制并带有偏移,然后以水绿色绘制。
' 这样使文本具有了提升的效果。
' 为了产生雕刻效果,只需使用
' 偏移量的反相值。
Private Sub DrawBlockText()
Dim textSize As SizeF
Dim g As Graphics
Dim myBackBrush As Brush = Brushes.Black
Dim myForeBrush As Brush = Brushes.Aquamarine
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)
Dim xLocation, yLocation As Single ' 用于位置
Dim i As Integer

' 从图片框创建 Graphics 对象并清除它
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 找出绘制示例文本所需要的大小
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 获取一次位置以消除冗余计算
xLocation = (picDemoArea.Width - textSize.Width) / 2
yLocation
= (picDemoArea.Height - textSize.Height) / 2

' 首先绘制黑色背景
' 为了达到效果,必须从偏移量
' 开始向上重复绘制文本,直至到达将绘制主
' 文本的地方。
' 由于人们通常认为光来自
' 右上角,从 X 维度减去偏移深度
' 而不是增加偏移深度会更合理。
' 否则,它看起来更像是阴影。
For i = CInt (effectDepth.Value) To 0 Step - 1
g.DrawString(txtShortText.Text, myFont, myBackBrush, _
xLocation
- i, yLocation + i)
Next

' 在黑色文本之上绘制水绿色主文本
g.DrawString(txtShortText.Text, myFont, myForeBrush, xLocation, yLocation)
End Sub
#End Region

#Region "画笔文本"
' 此 Sub 使用画笔(阴影或渐变)创建示例文本。
Private Sub DrawBrushText()
Dim textSize As SizeF
Dim g As Graphics
Dim myBrush As Brush
Dim gradientRectangle As RectangleF
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)

' 从图片框创建 Graphics 对象并将其清除。
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 查找绘制示例文本所必需的 Size。
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 创建所需的画笔。
If Me .optHatch.Checked Then
' 创建对角砖块 HatchBrush。
myBrush = New HatchBrush(HatchStyle.DiagonalBrick, _
Color.Blue, Color.Yellow)
Else
' 创建对角渐变 LinearGradientBrush。
gradientRectangle = New RectangleF( New PointF( 0 , 0 ), textSize)
myBrush
= New LinearGradientBrush(gradientRectangle, Color.Blue, _
Color.Yellow, LinearGradientMode.ForwardDiagonal)
End If

' 绘制文本。
g.DrawString(txtShortText.Text, myFont, myBrush, _
(picDemoArea.Width
- textSize.Width) / 2 , _
(picDemoArea.Height
- textSize.Height) / 2 )
End Sub
#End Region

#Region "浮雕文本"
' 此 Sub 创建具有浮雕外观的示例文本。
' 为了创建出效果,示例文本将分两次绘制。
' 第一次用黑色绘制并带有偏移,然后以白色(当前
' 背景色)绘制。
' 这样使文本具有提升的效果。
' 为了产生雕刻效果,只需使用
' 偏移量的反相值。
Private Sub DrawEmbossedText()
Dim textSize As SizeF
Dim g As Graphics
Dim myBackBrush As Brush = Brushes.Black
Dim myForeBrush As Brush = Brushes.White
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)
Dim xLocation, yLocation As Single

' 从图片框创建 Graphics 对象并将其清除。
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 查找绘制示例文本所必需的 Size。
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 一次获得位置并以此来消除多余的计算。
xLocation = (picDemoArea.Width - textSize.Width) / 2
yLocation
= (picDemoArea.Height - textSize.Height) / 2

' 首先绘制黑色背景。
' (注意:如果选择减去 mudEmbossDepth,则会获得
' 雕刻效果。)
g.DrawString(txtShortText.Text, myFont, myBackBrush, _
xLocation
+ Me .effectDepth.Value, _
yLocation
+ Me .effectDepth.Value)

' 在黑色文本之上绘制白色主文本
g.DrawString(txtShortText.Text, myFont, myForeBrush, xLocation, yLocation)
End Sub
#End Region

#Region "反射文本"
' 此 Sub 围绕字符的基线反射文本。
' 这是需要仔细测量文本的第一个
' 示例,比其他大多数示例都高级。
Private Sub DrawReflectText()
Dim textSize As SizeF
Dim g As Graphics
Dim myBackBrush As Brush = Brushes.Gray
Dim myForeBrush As Brush = Brushes.Black
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)
Dim myState As GraphicsState ' 用于存储图形的当前状态
Dim xLocation, yLocation As Single ' 用于位置
Dim textHeight As Single

' 从图片框创建 Graphics 对象并将其清除。
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 找出绘制示例文本所需要的大小
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 一次获取位置以消除多余的计算
xLocation = (picDemoArea.Width - textSize.Width) / 2
yLocation
= (picDemoArea.Height - textSize.Height) / 2

' 由于我们将缩放,而缩放影响整个
' 图形对象而不只是文本,因此需要将 Graphics 对象
' 的原点 (0,0) 重新定位于 (xLocation,
' yLocation) 点。如果不这样,那么在尝试使用
' 某个缩放转换翻转文本时,它只是在
' (xLocation, -yLocation) 处绘制反射的文本,
' 而该位置在可视区域之外。
g.TranslateTransform(xLocation, yLocation)

' 围绕原点反射仍然存在问题。
' 原点表示矩形的左上角。
' 这意味着反射将在原始图画
' 的顶部发生。这不是人们查看反射
' 文本的习惯方式。因此,我们需要确定在何处
' 绘制文本。这只能在计算绘制所需的高度
' 之后确定。
' 这没有看起来那么简单。
' MeasureString 方法返回的高度包括一些
' 用于下行字母和空白的额外空间。我们想要的只是从基线
' (所有线帽所在的线)算起的高度。 任何
' 带有下行字母的字符都将落在基线之下。为了计算
' 基线之上的高度,可使用
' GetCellAscent 方法。由于 GetCellAscent 返回
' 设计量度值,必须将它转换为像素,并针对字体大小
' 进行缩放。
' 注意:使用能够在基线之上完美地
' 反射的字符(比如箭头)效果最好。带有下行字母的字符
' 看起来会很奇怪。为解决这点,请取消对下面两行的注释,
' 这样将跨过最低下行字母高度进行反射。

Dim lineAscent As Integer
Dim lineSpacing As Integer
Dim lineHeight As Single

lineAscent
= myFont.FontFamily.GetCellAscent(myFont.Style)
lineSpacing
= myFont.FontFamily.GetLineSpacing(myFont.Style)
lineHeight
= myFont.GetHeight(g)
textHeight
= lineHeight * lineAscent / lineSpacing

' ' 取消这些行的注释以便在字符的
' ' 最低部分之上反射。
' Dim lineDescent As Integer ' 用于反射下行字符
' lineDescent = myFont.FontFamily.GetCellDescent(myFont.Style)
' textHeight = lineHeight * (lineAscent + lineDescent) / lineSpacing


' 首先绘制反射的文本。这么做的
' 唯一原因是为了演示
' GraphicsState 对象的使用。
' GraphicsState 对象维护 Graphics 对象
' 当前所处的状态。然后可以缩放、调整和
' 转换 Graphics 对象。可以使用
' Graphics 对象的 Restore 方法立即返回
' 前一个状态。
' 如果首先绘制了主文本,我们将不再需要
' Restore 方法或 GraphicsState 对象。

' 首先保存图形状态
myState = g.Save()

' 为了绘制反射,应使用带反相值的 ScaleTransform。
' 使用 -1 将不失真地反射文本。
' 记住考虑原点已被重置的事实。
g.ScaleTransform( 1 , - 1 .0F) ' 仅在 Y 方向反射
g.DrawString(txtShortText.Text, myFont, myBackBrush, 0 , - textHeight)

' 将图形状态重置到转换之前的状态
g.Restore(myState)

' 绘制主文本
g.DrawString(txtShortText.Text, myFont, myForeBrush, 0 , - textHeight)

End Sub
#End Region

#Region "阴影文本"
' 此 Sub 使用纯色画笔和阴影绘制示例文本
' 为了创建阴影,示例文本将分两次绘制。
' 第一次用黑色绘制并带有偏移,然后以黑色正常绘制。
' 当然也可以使用其他颜色。
Private Sub DrawShadowText()
Dim textSize As SizeF
Dim g As Graphics
Dim myShadowBrush As Brush = Brushes.Gray
Dim myForeBrush As Brush = Brushes.Black
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)
Dim xLocation, yLocation As Single

' 从图片框创建 Graphics 对象并将其清除。
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 查找绘制示例文本所必需的 Size。
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 一次获得位置并以此来消除多余的计算。
xLocation = (picDemoArea.Width - textSize.Width) / 2
yLocation
= (picDemoArea.Height - textSize.Height) / 2

' 首先绘制阴影。
g.DrawString(txtShortText.Text, myFont, myShadowBrush, _
xLocation
+ Me .effectDepth.Value, _
yLocation
+ Me .effectDepth.Value)

' 在阴影之上绘制主文本。
g.DrawString(txtShortText.Text, myFont, myForeBrush, xLocation, yLocation)
End Sub
#End Region

#Region "剪切文本"
' 此子例程剪切文本以使它具有棱角。这需要
' 使用定义剪切的矩阵。
Private Sub DrawShearText()
Dim textSize As SizeF
Dim g As Graphics
Dim myForeBrush As Brush = Brushes.Black
Dim myFont As New Font( " Times New Roman " , Me .nudFontSize.Value, FontStyle.Regular)
Dim myTransform As Matrix
Dim xLocation, yLocation As Single

' 从图片框创建 Graphics 对象并将其清除。
g = picDemoArea.CreateGraphics()
g.Clear(Color.White)

' 查找绘制示例文本所必需的 Size。
textSize = g.MeasureString( Me .txtShortText.Text, myFont)

' 一次获得位置并以此来消除多余的计算。
xLocation = (picDemoArea.Width - textSize.Width) / 2
yLocation
= (picDemoArea.Height - textSize.Height) / 2

' 由于我们将缩放,而缩放影响整个
' 图形对象而不只是文本,因此需要将 Graphics 对象
' 的原点 (0,0) 重新定位到 (xLocation,
' yLocation) 点。
g.TranslateTransform(xLocation, yLocation)

' 获取当前 Graphics 对象的转换,并将它
' 剪切掉指定的量。
myTransform = g.Transform
myTransform.Shear(nudSkew.Value,
0 )
g.Transform
= myTransform

' 绘制主文本。
g.DrawString(txtShortText.Text, myFont, myForeBrush, 0 , 0 )
End Sub
#End Region

#Region "多行文本"
' 此子例程只接受文本框中的文本行,并将它们
' 放在 picDemoArea 图片框中。它将根据需要自动换行,但是不会滚动。
Private Sub DrawMultiLine()
Dim textSize As SizeF
Dim g As Graphics
Dim myForeBrush As Brush = Brushes.Black
Dim myFont As New Font( " Times New Roman " , Me .multilineSize.Value, FontStyle.Regular)

' 从图片框创建 Graphics 对象并将其清除。
g = picMultiLine.CreateGraphics()
g.Clear(Color.White)

' 查找绘制示例文本所必需的 Size。
textSize = g.MeasureString(txtLongText.Text, myFont)

' 绘制主文本。
g.DrawString(txtLongText.Text, myFont, myForeBrush, _
New RectangleF( 0 , 0 , picDemoArea.Width, picDemoArea.Height))
End Sub
#End Region



源码里面有比较详尽的注释,我就简单介绍一下。

首先是画笔文本,通过myBrush = New HatchBrush(HatchStyle.DiagonalBrick, Color.Blue, Color.Yellow) 创建一个砖块画笔,指定HatchStyle的画笔风格,然后设置块的边框和填充颜色即可。

渐变文本则需要用myBrush = New LinearGradientBrush(gradientRectangle, Color.Blue, Color.Yellow, LinearGradientMode.ForwardDiagonal) 创建一个渐变画笔,指定渐变的区域范围和渐变色。

阴影文本实际上是先描画一个偏移值暗色的阴影文本,然后再把正文描绘到阴影上层即可。浮雕效果也是差不多。

至于块文本,就是阴影文本的升级版,利用偏移不断的写阴影,最后再把正文写在阴影层上。



关键我们来讲讲扭曲文本

首先我们需要设置Graphics的起点,然后从Graphics中获取Matrix对象,通过这个对象设置画板的扭曲值,然后写入文本,如果有需要,最后重置起点和扭曲值。

g.TranslateTransform(xLocation, yLocation) ' 设置扭曲起点

myTransform
= g.Transform ' 获取Matrix对象

myTransform.Shear(nudSkew.Value,
0 ) ' 设置画板的扭曲值

g.Transform
= myTransform ' 将变量写回Graphics对象中,必须要有这一步,不能直接操纵g.Transform对象

这之后就可以用DrawString写入文本对象,注意,因为已经用TranslateTransform设置过新的起点,所以写入的位置要考虑起点偏移

顺便演示一下重置画板

g.TranslateTransform( 0 , 0 )

myTransform
= g.Transform

myTransform.Shear(
- nudSkew.value, 0 ) ' 注意这里的偏移是根据当前的偏移值取反而设定的

g.Transform
= myTransform



反射文本还没有研究,自己看源码的介绍吧。

至于多行文本,里面的解释也很详细,不多做介绍了。

你可能感兴趣的:(VB.NET)