zoukankan      html  css  js  c++  java
  • 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

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/maozefa/archive/2008/01/15/2044341.aspx

  • 相关阅读:
    HDU 5642 King's Order 动态规划
    HDU 5640 King's Cake GCD
    HDU 5641 King's Phone 模拟
    HDU 5299 Circles Game 博弈论 暴力
    HDU 5294 Tricks Device 网络流 最短路
    HDU 5289 Assignment rmq
    HDU 5288 OO’s Sequence 水题
    星际争霸 虚空之遗 人族5BB 操作流程
    Codeforces Beta Round #3 D. Least Cost Bracket Sequence 优先队列
    Codeforces Beta Round #3 C. Tic-tac-toe 模拟题
  • 原文地址:https://www.cnblogs.com/Yjianyong/p/2024400.html
Copyright © 2011-2022 走看看