C#版可调节的文字阴影特效

 本来春节前不准备写BLOG文章了,可前几天有几个搞C#的朋友来信说,对文章《 GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》的内容很感兴趣,但苦于对Delphi不熟悉,想请我帮忙将其改为C#版的。可他们哪里知道,我从未用C#写过代码(因我只是个业余编程爱好者,C#好像不适合我,我儿子是搞java的,对C#也不怎么熟),好在五年前我买过一本《C#入门经典》,只好赶鸭子上架,对着书边琢磨边改写,费了好大的功夫,才勉强改编成下面这个样子(也幸亏我对C/C++还熟悉,也幸好C#是此系列的语言),请那些朋友以及对本文内容有兴趣的朋友再根据自己的需要去改造。

        既然第一次实际接触了C#,有个问题正好请教大家,就是关于C#垃圾回收的问题,因我写的TextShadow类中2个函数中用到了GDI+的局部对象,而且有可能被用户反复使用,这些局部对象是等系统自动回收好呢,还是我在函数结束时提前回收呢?在我的那本《C#入门经典》第一版中多处强调,对GDI+对象,“总是要调用Dispose()”,“或使用using结构”,“否则应用程序就可能耗尽Windows资源”。对于函数中频繁使用的对象不要等系统自动回收,这个道理我是明白的,但函数中只使用一次的对象(特别是GDI+对象)是否也要自己Dispose()呢,我在网上也看了一些C#代码,一般都没有自己释放,所以我糊涂了,但小心无大错,所以我在2个函数中还是自己释放了(肯定不会错,但是否有必要),请各位C#高手们看我下面的代码后指点指点,我是否多此一举了呢?先在这里谢了。

        下面是我改写的C#文字阴影类TextShadow:

using  System;
using  System.Drawing;
using  System.Drawing.Imaging;

    
///   <summary>
    
///  Summary description for TextShadow
    
///   </summary>
public   class  TextShadow
{
    
private   int  radius  =   5 ;
    
private   int  distance  =   10 ;
    
private   double  angle  =   60 ;
    
private   byte  alpha  =   192 ;

    
///   <summary>
    
///  高斯卷积矩阵
    
///   </summary>
     private   int [] gaussMatrix; 
    
///   <summary>
    
///  卷积核
    
///   </summary>
     private   int  nuclear  =   0 ;   

    
///   <summary>
    
///  阴影半径
    
///   </summary>
     public   int  Radius
    {
        
get
        {
            
return  radius;
        }
        
set
        {
            
if  (radius  !=  value)
            {
                radius 
=  value;
                MakeGaussMatrix();
            }
        }
    }

    
///   <summary>
    
///   阴影距离
    
///   </summary>
     public   int  Distance
    {
        
get
        {
            
return  distance;
        }
        
set
        {
            distance 
=  value;
        }
    }

    
///   <summary>
    
///   阴影输出角度(左边平行处为0度。顺时针方向)
    
///   </summary>
     public   double  Angle
    {
        
get
        {
            
return  angle;
        }
        
set
        {
            angle 
=  value;
        }
    }

    
///   <summary>
    
///  阴影文字的不透明度
    
///   </summary>
     public   byte  Alpha
    {
        
get
        {
            
return  alpha;
        }
        
set
        {
            alpha  =  value;
        }
    }

    
///   <summary>
    
///  对文字阴影位图按阴影半径计算的高斯矩阵进行卷积模糊
    
///   </summary>
    
///   <param name="bmp"> 文字阴影位图 </param>
     private   unsafe   void  MaskShadow(Bitmap bmp)
    {
        
if  (nuclear  ==   0 )
            MakeGaussMatrix();
        Rectangle r 
=   new  Rectangle( 0 0 , bmp.Width, bmp.Height);
        
//  克隆临时位图,作为卷积源
        Bitmap tmp  =  (Bitmap)bmp.Clone();
        BitmapData dest 
=  bmp.LockBits(r, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
        BitmapData source 
=  tmp.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        
try
        {
            
//  源首地址(0, 0)的Alpha字节,也就是目标首像素的第一个卷积乘数的像素点
             byte *  ps  =  ( byte * )source.Scan0;
            ps 
+=   3 ;
            
//  目标地址为卷积半径点(radius, radius)的Alpha字节
             byte *  pd  =  ( byte * )dest.Scan0;
            pd 
+=  (radius  *  (dest.Stride  +   4 +   3 );
            
//  位图实际卷积的部分
             int  width  =  dest.Width  -  radius  *   2 ;
            
int  height  =  dest.Height  -  radius  *   2 ;
            
int  matrixSize  =  radius  *   2   +   1 ;
            
//  卷积矩阵字节偏移
             int  mOffset  =  dest.Stride  -  matrixSize  *   4 ;
            
//  行尾卷积半径(radius)的偏移
             int  rOffset  =  radius  *   8 ;
            
int  count  =  matrixSize  *  matrixSize;

            
for  ( int  y  =   0 ; y  <  height; y ++ )
            {
                
for  ( int  x  =   0 ; x  <  width; x ++ )
                {
                   
                    
byte *  s  =  ps  -  mOffset;
                    
int  v  =   0 ;
                    
for  ( int  i  =   0 ; i  <  count; i ++ , s  +=   4 )
                    {
                        
if  ((i  %  matrixSize)  ==   0
                            s 
+=  mOffset;            //  卷积矩阵的换行
                        v  +=  gaussMatrix[i]  *   * s;    //  位图像素点Alpha的卷积值求和
                    }
                    
//  目标位图被卷积像素点Alpha等于卷积和除以卷积核
                     * pd  =  ( byte )(v  /  nuclear);      
                    pd 
+=   4 ;
                    ps 
+=   4 ;
                }
                pd 
+=  rOffset;
                ps 
+=  rOffset;
            }
        }
        
finally
        {
            tmp.UnlockBits(source);
            bmp.UnlockBits(dest);
            tmp.Dispose();
        }
    }

    
///   <summary>
    
///  按给定的阴影半径生成高斯卷积矩阵
    
///   </summary>
     protected   virtual   void  MakeGaussMatrix()
    {
        
double  Q  =  ( double )radius  /   2.0 ;
        
if  (Q  ==   0.0 )
            Q 
=   0.1 ;
        
int  n  =  radius  *   2   +   1 ;        
        
int  index  =   0 ;
        nuclear 
=   0 ;
        gaussMatrix 
=   new   int [n  *  n];

        
for  ( int  x  =   - radius; x  <=  radius; x ++ )
        {
            
for  ( int  y  =   - radius; y  <=  radius; y ++ )
            {
                gaussMatrix[index] 
=  ( int )Math.Round(Math.Exp( - (( double )x  *  x  +  y  *  y)  /  ( 2.0   *  Q  *  Q))  /  
                                                     (
2.0   *  Math.PI  *  Q  *  Q)  *   1000.0 );
                nuclear 
+=  gaussMatrix[index];
                index 
++ ;
            }
        }
    }

    
public  TextShadow()
    {
        
//
        
//  TODO: Add constructor logic here
        
//
    }

    
///   <summary>
    
///  画文字阴影
    
///   </summary>
    
///   <param name="g"> 画布 </param>
    
///   <param name="text"> 文字串 </param>
    
///   <param name="font"> 字体 </param>
    
///   <param name="layoutRect"> 文字串的布局矩形 </param>
    
///   <param name="format"> 文字串输出格式 </param>
     public   void  Draw(Graphics g,  string  text, Font font, RectangleF layoutRect, StringFormat format)
    {
        RectangleF sr 
=   new  RectangleF(( float )(radius  *   2 ), ( float )(radius  *   2 ), layoutRect.Width, layoutRect.Height);
        
//  根据文字布局矩形长宽扩大文字阴影半径4倍建立一个32位ARGB格式的位图
        Bitmap bmp  =   new  Bitmap(( int )sr.Width  +  radius  *   4 , ( int )sr.Height  +  radius  *   4 , PixelFormat.Format32bppArgb);
        
//  按文字阴影不透明度建立阴影画刷
        Brush brush  =   new  SolidBrush(Color.FromArgb(alpha, Color.Black));
        Graphics bg 
=  Graphics.FromImage(bmp);
        
try
        {
            
//  在位图上画文字阴影
            bg.TextRenderingHint  =  System.Drawing.Text.TextRenderingHint.AntiAlias;
            bg.DrawString(text, font, brush, sr, format);
            
//  制造阴影模糊
            MaskShadow(bmp);
            
//  按文字阴影角度、半径和距离输出文字阴影到给定的画布
            RectangleF dr  =  layoutRect;
            dr.Offset((
float )(Math.Cos(Math.PI  *  angle  /   180.0 *  distance),
                      (
float )(Math.Sin(Math.PI  *  angle  /   180.0 *  distance));
            sr.Inflate((
float )radius, ( float )radius);
            dr.Inflate((
float )radius, ( float )radius);
            g.DrawImage(bmp, dr, sr, GraphicsUnit.Pixel);
        }
        
finally
        {
            bg.Dispose();
            brush.Dispose();
            bmp.Dispose();
        }
    }

    
///   <summary>
    
///  画文字阴影
    
///   </summary>
    
///   <param name="g"> 画布 </param>
    
///   <param name="text"> 文字串 </param>
    
///   <param name="font"> 字体 </param>
    
///   <param name="layoutRect"> 文字串的布局矩形 </param>
     public   void  Draw(Graphics g,  string  text, Font font, RectangleF layoutRect)
    {
        Draw(g, text, font, layoutRect, 
null );
    }

    
///   <summary>
    
///  画文字阴影
    
///   </summary>
    
///   <param name="g"> 画布 </param>
    
///   <param name="text"> 文字串 </param>
    
///   <param name="font"> 字体 </param>
    
///   <param name="origin"> 文字串的输出原点 </param>
    
///   <param name="format"> 文字串输出格式 </param>
     public   void  Draw(Graphics g,  string  text, Font font, PointF origin, StringFormat format)
    {
        RectangleF rect 
=   new  RectangleF(origin, g.MeasureString(text, font, origin, format));
        Draw(g, text, font, rect, format);
    }

    
///   <summary>
    
///  画文字阴影
    
///   </summary>
    
///   <param name="g"> 画布 </param>
    
///   <param name="text"> 文字串 </param>
    
///   <param name="font"> 字体 </param>
    
///   <param name="origin"> 文字串的输出原点 </param>
     public   void  Draw(Graphics g,  string  text, Font font, PointF origin)
    {
        Draw(g, text, font, origin, 
null );
    }
}

        我在代码中已经写了较详细的注释,应该不用再解释了,但是有一点可以在这里补充一下,有朋友在看了《GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》后,曾经问过我,为什么对文字阴影进行高斯卷积操作只是针对位图像素的Alpha,而不对其RGB部分操作了?这个问题其实很简单,因为我采用的文字阴影颜色是黑色的,在画了文字的地方,其ARGB值为0xFF000000(假定文字阴影的Alpha为255),而没有文字的地方是全透明色,其ARGB值为0x00000000。可见,除了Alpha,无论是文字部分,还是透明部分,其R、G、B部分都为0,对它们进行任何的卷积操作的结果只能是“0”!只有通过对Alpha卷积操作,使之产生不同的不透明度,从而显示出来的黑色文字边缘就产生了我们想要的半影调效果。换句话说,如果不采用黑色作阴影颜色,就必须对位图像素的ARGB全部进行卷积模糊,不过其它颜色做阴影色效果不怎么好。

        下面是个简单的C#演示程序:

using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Text;
using  System.Windows.Forms;
using  System.Drawing.Drawing2D;

namespace  TextShadowTest
{
    
public   partial   class  Form1 : Form
    {
        
public  Form1()
        {
            InitializeComponent();
        }

        
private   void  Form1_Paint( object  sender, PaintEventArgs e)
        {
            FontFamily family 
=   new  FontFamily( " Times New Roman " /* "华文行楷" */ );
            Font font 
=   new  Font(family,  50 , FontStyle.Bold, GraphicsUnit.Pixel);
            Brush brush 
=   new  LinearGradientBrush(ClientRectangle, Color.Blue, Color.AliceBlue,  90 );
            e.Graphics.FillRectangle(brush, ClientRectangle);
            TextShadow tShadow 
=   new  TextShadow();
            tShadow.Draw(e.Graphics, 
" 文字阴影特效 " , font,  new  PointF( 10 , ( float )ClientRectangle.Height  /   3 ));
            e.Graphics.TextRenderingHint 
=  System.Drawing.Text.TextRenderingHint.AntiAlias;
            e.Graphics.DrawString(
" 文字阴影特效 " , font, Brushes.White,  new  PointF( 10 , ( float )ClientRectangle.Height  /   3 ));
        }
    }
}

        效果图和《GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》是一样的,为避免你麻烦,我还是把那边的效果图做个链接,分别为2种字体的文字输出效果:

        我是2007年元月开始写BLOG的,到目前刚好一年,包括这篇,共凑合了50篇文章,把我业余编程生涯近20年的“老底”和“新得”几乎都抖光了,春节前,这也是最后一篇文章了,明年不知又“研究”点什么,呵呵,反正我是无事的闲人,总得找点事打发时光。在这里,我先向各位拜个早早年了!祝大家来年事事如意,心想事成!

        再次声明,第一次写C#代码,写得不好不要笑话我 ^_^。还是那句老话,有问题和指教,请留言或直接写信给我:maozefa@hotmail.com

来自:http://blog.csdn.net/maozefa/archive/2008/01/15/2044341.aspx

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值