用 C# 创建花哨的文本效果






4.72/5 (24投票s)
2006年3月16日
5分钟阅读

107471

4433
编写代码以创建供您的应用程序使用的炫酷文本。
引言
我们的设计师给了我一些关于她认为我们的应用程序应该是什么样子的 Photoshop 草图。草图中有几个例子,她用光晕轮廓围绕文字进行了处理。像所有优秀的设计师一样,她将图像切分成离散的部分,方便我轻松集成。这种方法的缺点是集成她给我的每个图像都很耗时,而且当我们要本地化产品时,我们需要重新做所有这些。我需要一种动态生成它们的方法。
计划
WinForms 的 System.Drawing.*
提供了非常强大的方法来创建带有 Alpha 混合的图像。计划是创建一个文本的位图。文本将在位图上的背景色中被涂抹开,然后最后在前景色中写在位图的中央。Alpha 混合将应用于背景的、涂抹过的版本,使其边缘看起来羽毛状,并且可以渲染在任何其他背景上。(Alpha 值颜色叠加在 Alpha 值颜色之上会增加强度,所以最外围应该是暗淡的,越靠近文本,背景色就越强烈)。
我预计这会花一点时间,所以我计划让一个函数返回最终文本图像的位图,这样我就可以将其缓存起来,以后使用,从而只遭受一次性能影响。
这似乎是一个可行的解决方案,所以我开始着手实现了……
实现
这将需要一些画笔、一些位图、一些图形上下文等,我们可能会调用此函数数百次,因此明智的资源管理很重要。对于不知道的人来说,当托管对象超出范围时,它会被标记为“未引用”,以便稍后自动进行垃圾回收。什么时候?如果你不自己调用垃圾回收(不一定推荐,如果你使用诸如弱引用等),你可能不知道。这些托管对象,例如 SolidBrush
类,实际上是 GDI+ 非托管对象的托管包装器,这些对象在后台可能会消耗宝贵的系统资源和内存。因此,您不应仅仅允许 Brush
或 Pen
或 Bitmap
或任何这些非托管绘图对象的包装器超出范围,希望它们很快被清理干净,因为它们会悄悄地消耗宝贵的资源。您需要调用 Dispose
来清理每个非托管资源。首选方法是使用 using
构造。当您离开 using()
范围时,“using
”将导致对象的 Dispose
方法被调用(这反过来会释放底层的 GDI+ 对象)。您可以在函数末尾自己调用 Dispose
,但如果您在调用 Dispose
之前退出了函数,或者在 Dispose()
之前发生了异常,那么您就会遇到之前同样的问题。 using
确保无论发生什么导致控件离开 using()
语句的作用域,该对象的 Dispose
方法都会被调用。
using (SolidBrush brFore=new SolidBrush(clrFore))
{
... use the brush inside of here
}
为了创建包含的位图,我们需要知道文本的像素大小。我们使用 Graphics
方法 MeasureString
来确定这一点。MeasureString
有多种变体,有些会考虑文本的对齐方式和间距等。我们使用基本调用,因为我们最终调用 Graphics.DrawString
时不打算设置任何特殊的标志。根据返回的大小,我们可以创建一个包含文本单个渲染的位图,我们将使用它作为在第二个目标位图上渲染的基础,多次渲染,以制作模糊的背景。
SizeF sz=g.MeasureString(strText, fnt);
GDI+ 是为了速度和美观而设计的,默认情况下,它会选择一个最佳的折衷点。您可以设置 Graphics
对象上的属性,以确保获得最佳的渲染效果。我们使用 SmoothingMode
、InterpolationMode
和 TextRenderingHint
来控制所需的输出级别。
gBmp.SmoothingMode=SmoothingMode.HighQuality;
gBmp.InterpolationMode=InterpolationMode.HighQualityBilinear;
gBmp.TextRenderingHint=TextRenderingHint.AntiAliasGridFit;
从位图中,我们创建一个图形上下文 (Graphics.FromImage
)。这将允许我们将字符串直接绘制到位图上。我们在这里创建一些我们知道稍后会需要的画笔,以便将所有 using
语句集中在一起,并避免过度嵌套此函数,以提高可读性。此外,我们完全期望此函数始终能够完全执行,因此在此对象不需要时**精确地**将其处置的边际开销不值得为此函数的易读性和可维护性付出代价。注意 brBack
画笔的 alpha 值设置为 **16**,这是一个非常透明的值,但我们会将其叠加多次来涂抹背景。
using (Bitmap bmp=new Bitmap((int)sz.Width,(int)sz.Height))
using (Graphics gBmp=Graphics.FromImage(bmp))
using (SolidBrush brBack=new
SolidBrush(Color.FromArgb(16,clrBack.R,
clrBack.G, clrBack.B)))
using (SolidBrush brFore=new SolidBrush(clrFore))
{
...
}
现在我们创建另一个图像,比它大 blurAmount
,以适应涂抹,然后从中获取一个 Graphics
,这样我们就可以将第一个位图渲染到它上面,并在 X 方向上绘制 blurAmount
次,在 Y 方向上绘制 blurAmount
次。这种矩形模糊近似于更传统的圆形模糊,因为外部的 alpha 值足够低,使其羽化;更准确地说,您应该基于从中心点的圆形渲染。在此情况下,为了易读性、易于编码以及因为差异会很小,这种简化是合理的。
bmpOut=new Bitmap(bmp.Width+blurAmount,bmp.Height+blurAmount);
...
// smear image of background of text about
// to make blurred background "halo"
for (int x=0;x<=blurAmount;x++)
for (int y=0;y<=blurAmount;y++)
gBmpOut.DrawImageUnscaled(bmp,x,y);
在渲染完模糊效果后,我们最后将实际文本再次渲染出来,位于中心(X 和 Y 位置偏移 blurAmount
/2),使用前景色。
// draw actual text
gBmpOut.DrawString(strText, fnt, brFore,
blurAmount/2, blurAmount/2);
使用代码
这是如何调用代码来生成文本图像,以及如何将最终的炫酷文本图像渲染到 Windows 窗体上
public class Form1 : System.Windows.Forms.Form
{
private Bitmap _bmpText;
public Form1()
{
this.BackColor = System.Drawing.Color.IndianRed;
this.ClientSize = new System.Drawing.Size(358, 126);
using (Font fnt=new Font("Arial", 20, FontStyle.Bold))
_bmpText = (Bitmap) FancyText.ImageFromText("Hello Code" +
" Project Fans!", fnt, Color.Green, Color.Yellow);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(_bmpText, 10, 40);
}
}
历史
- 创建日期 - 2006/03/16。