博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
给图片添加文字水印
阅读量:6217 次
发布时间:2019-06-21

本文共 10786 字,大约阅读时间需要 35 分钟。

功能需求

  1. 在图片的给定位置上添加文字水印
  2. 水印可以旋转和设置透明度

先说说自己的实现思路:

  1. 先创建具有透明背景色的文字水印图像
  2. 将水印图像添加到原图像中

实现

首先创建一个接口,用于约束水印的创建方式:

public interface IWatermark{    Bitmap CreateWatermark(string markText, Font font, Brush brush, Rectangle rectangle);}

 

具体实现:

public class Watermark : IWatermark{    //水印画布    protected virtual Rectangle WatermarkCanvas { set; get; }    protected Watermark(){}    public Watermark(string markText, Font font)    {        int width = (int)((markText.Length + 1) * font.Size);        int height = font.Height;        WatermarkCanvas = new Rectangle(0, 0, width, height);    }    ///     /// 给图片添加水印,文字大小以像素(Pixel)为计量单位    ///     /// 图片文件全名    public Bitmap Mark(string filename, string markText, Font font, Brush brush, float positionX, float positionY, int angle, int transparency)    {        return CreateMarkCore(filename, markText, font, brush, positionX, positionY, angle, transparency);    }    ///     /// 绘制文字水印,文字大小以像素(Pixel)为计量单位    ///     public virtual Bitmap CreateWatermark(string markText, Font font, Brush brush, Rectangle rectangle)    {        Bitmap watermark = new Bitmap(rectangle.Width, rectangle.Height);        Graphics graphics = Graphics.FromImage(watermark);        //消除锯齿        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;        graphics.DrawString(markText, font, brush, rectangle);        graphics.Dispose();        return watermark;    }    ///     /// 给图片添加水印,文字大小以像素(Pixel)为计量单位    ///     /// 图片文件全名    protected virtual Bitmap CreateMarkCore(string filename, string markText, Font font, Brush brush, float positionX, float positionY, int angle, int transparency)    {        if (!File.Exists(filename))        {            throw new FileNotFoundException("文件不存在!");        }        Bitmap resultImg;        using (Bitmap rawImg = new Bitmap(filename))        {            using (Bitmap watermarkImg = CreateWatermark(markText, font, brush, WatermarkCanvas))            using (Bitmap rotateImg = Rotate(watermarkImg, angle))            {                using (Bitmap temp = SetAlpha(rotateImg, transparency))                {                    resultImg = new Bitmap(rawImg.Width, rawImg.Height);                    using (Graphics newGraphics = Graphics.FromImage(resultImg))                    {                        newGraphics.DrawImage(rawImg, 0, 0);                        newGraphics.DrawImage(temp, positionX, positionY);                    }                }            }        }        return resultImg;    }}
View Code

水印图片透明度设置和旋转(下面这段代码和上面一段代码都位于Watermark类中,因为代码量较大,所以分开来展示):

public class Watermark : IWatermark{        protected Bitmap Rotate(Bitmap rawImg, int angle)        {            angle = angle % 360;            //弧度转换            double radian = TranslateAngleToRadian(angle);            //原图的宽和高            int width = rawImg.Width;            int height = rawImg.Height;            //旋转之后图像的宽和高            Rectangle rotateRec = RecalculateRectangleSize(width, height, angle);            int rotateWidth = rotateRec.Width;            int rotateHeight = rotateRec.Height;            //目标位图            Bitmap targetImg = new Bitmap(rotateWidth, rotateHeight);            Graphics targetGraphics = Graphics.FromImage(targetImg);            //计算偏            Point Offset = new Point((rotateWidth - width) / 2, (rotateHeight - height) / 2);            //构造图像显示区域:让图像的中心与窗口的中心点一致            Rectangle rect = new Rectangle(Offset.X, Offset.Y, width, height);            Point centerPoint = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);            targetGraphics.TranslateTransform(centerPoint.X, centerPoint.Y);            targetGraphics.RotateTransform(angle);            //恢复图像在水平和垂直方向的平移            targetGraphics.TranslateTransform(-centerPoint.X, -centerPoint.Y);            targetGraphics.DrawImage(rawImg, rect);            //重至绘图的所有变换            targetGraphics.ResetTransform();            targetGraphics.Save();            targetGraphics.Dispose();            return targetImg;        }        ///         /// 设置图像透明度,0:全透明,255:不透明        ///         protected Bitmap SetAlpha(Bitmap rawImg, int alpha)        {            if (!(0 <= alpha) && alpha <= 255)            {                throw new ArgumentOutOfRangeException("alpha ranges from 0 to 255.");            }            //颜色矩阵            float[][] matrixItems =            {                new float[]{1,0,0,0,0},                new float[]{0,1,0,0,0},                new float[]{0,0,1,0,0},                new float[]{0,0,0,alpha/255f,0},                new float[]{0,0,0,0,1}            };            ColorMatrix colorMatrix = new ColorMatrix(matrixItems);            ImageAttributes imageAtt = new ImageAttributes();            imageAtt.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);            Bitmap resultImg = new Bitmap(rawImg.Width, rawImg.Height);            Graphics g = Graphics.FromImage(resultImg);            g.DrawImage(rawImg, new Rectangle(0, 0, rawImg.Width, rawImg.Height),                    0, 0, rawImg.Width, rawImg.Height, GraphicsUnit.Pixel, imageAtt);            g.Dispose();            return resultImg;        }        protected double TranslateAngleToRadian(float angle)        {            return angle * Math.PI / 180;        }        protected Rectangle RecalculateRectangleSize(int width, int height, float angle)        {            double radian = TranslateAngleToRadian(angle);            double cos = Math.Cos(radian);            double sin = Math.Sin(radian);            double newWidth = (int)(Math.Max(Math.Abs(width * cos - height * sin), Math.Abs(width * cos + height * sin)));            double newHeight = (int)(Math.Max(Math.Abs(width * sin - height * cos), Math.Abs(width * sin + height * cos)));            return new Rectangle(0, 0, (int)newWidth, (int)newHeight);        }    }
View Code

Watermark类对外暴露了API: Bitmap Mark(string filename, string markText, Font font, Brush brush, float positionX, float positionY, int angle, int transparency) ,向图片中添加水印只需创建Watermark实例,然后调用该方法即可。具体实现代码如下:

//.NET中,Font尺寸的默认单位是Point,这里统一使用Pixel作为计量单位string path = @"C:\Users\chiwenjun\Desktop\1.PNG";string markText = "字体:微软雅黑";Font font = new Font("微软雅黑", 40, FontStyle.Bold, GraphicsUnit.Pixel);Watermark watermark = new Watermark(markText, font);Bitmap img = watermark.Mark(path, markText, font, new SolidBrush(Color.FromArgb(0, 0, 0)), 160, 535, 0, 180);img.Save(path, ImageFormat.Png);

原图:

 

 
添加水印效果图:

 

 
水印顺时针旋转55°效果:

 

 

旋转前后,水印图像的宽和高会发生变化,如下图所示:

 

扩展

上面的代码很好的实现了在图片上添加单行水印的效果,若要实现多行水印可以通过对Watermark类的扩展来实现。

创建类MultiLineWatermark继承自Watermark,然后覆写属性WatermarkCanvas来指定水印画布的大小;覆写方法CreateWatermark来实现多行水印效果。

 

public class MultiLineWatermark : Watermark    {        protected int _canvasWidth = 0;        protected int _canvasHeight = 0;        //每行水印所允许的最大字数        protected int _lineMaxLength = 0;        //水印所允许的最大字数        protected int _wordMaxLength = 0;        protected override Rectangle WatermarkCanvas        {            get            {                return new Rectangle(0, 0, this._canvasWidth, this._canvasHeight);            }        }        public MultiLineWatermark(int canvasWidth, int canvasHeight, int lineMaxLength, int wordMaxLength)        {            this._canvasWidth = canvasWidth;            this._canvasHeight = canvasHeight;            this._lineMaxLength = lineMaxLength;            this._wordMaxLength = wordMaxLength;        }        public override Bitmap CreateWatermark(string markText, Font font, Brush brush, Rectangle rectangle)        {            Bitmap watermark = new Bitmap(rectangle.Width, rectangle.Height);            Graphics graphics = Graphics.FromImage(watermark);            //消除锯齿            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;            int lineHeight = _canvasHeight / (_wordMaxLength / _lineMaxLength);            if (markText.Contains('#'))            {                string[] textList = markText.Split('#');                int count = (int)Math.Min(textList.Length, Math.Ceiling(_wordMaxLength * 1.0 / _lineMaxLength));                for (int i = 0; i < count; i++)                {                    if (textList[i].Length > _lineMaxLength)                    {                        textList[i] = textList[i].Substring(0, _lineMaxLength);                    }                    //文字居中                    graphics.DrawString(textList[i], font, brush, (rectangle.Width - textList[i].Length * font.Size) / 2, i * lineHeight);                }            }            else            {                //文字居中                if (markText.Length <= _lineMaxLength)                {                    graphics.DrawString(markText, font, brush, (rectangle.Width - markText.Length * font.Size) / 2, 0);                }                else                {                    int count = (int)Math.Min(Math.Ceiling(_wordMaxLength * 1.0 / _lineMaxLength), Math.Ceiling(markText.Length * 1.0 / _lineMaxLength));                    string[] temp = new string[count];                    for (int i = 0; i < count; i++)                    {                        if (i * _lineMaxLength + _lineMaxLength <= markText.Length)                        {                            temp[i] = markText.Substring(i * _lineMaxLength, _lineMaxLength);                        }                        else                        {                            temp[i] = markText.Substring(i * _lineMaxLength, markText.Length - i * _lineMaxLength);                        }                        graphics.DrawString(temp[i], font, brush, (rectangle.Width - temp[i].Length * font.Size) / 2, i * lineHeight);                    }                }            }            graphics.Dispose();            return watermark;        }    }
View Code

 

具体的使用方式和调用Watermark类似,具体代码如下:

string path = @"C:\Users\chiwenjun\Desktop\1.PNG";//以#作为换行标记string markText = "字体:#微软雅黑雅黑雅黑";Font font = new Font("微软雅黑", 40, FontStyle.Bold, GraphicsUnit.Pixel);//若字数超过每行所允许的最大值,超出部分被忽略int lineMaxLength = 7;//超出的字数会被忽略int wordMaxLength = 14;//行高,用于计算水印图像的高int lineHeight = 55;int width = (int)((lineMaxLength + 1) * font.Size);int height = (int)(Math.Ceiling(wordMaxLength * 1.0 / lineMaxLength) * lineHeight);Watermark watermark = new MultiLineWatermark(width, height, lineMaxLength, wordMaxLength);Bitmap img = watermark.Mark(path, markText, font, new SolidBrush(Color.FromArgb(0, 0, 0)), 150, 535, 0, 180);img.Save(path, ImageFormat.Png);

多行水印的文字是居中显示的:

 

 

若没有使用#标记换行,当一行字数超过指定的最大字数时,会自动换行:

 

这篇文章是对自己项目中添加水印功能的记录,通篇以代码为主,看起来可能会感觉比较枯燥。

功能的实现没有太多难点,唯有一点感受较深,就是水印图像宽和高的计算。.NET(.NET Framework 4.5)中字体大小的度量单位默认是Point,而图像的度量单位是Pixel,单位的不同导致水印图像尺寸的计算出现偏差,这一点折磨我很久。
图像旋转和透明度设置的两个方法RotateSetAlpha是在网友代码基础上修改得到,非本人原创,代码原文已在参考文章中列出,在此对两位网友表示感谢。

参考文章:

版权声明

本文为作者原创,版权归作者所有。 转载必须保留文章的完整性,且在页面明显位置处标明。

如有问题, 请和作者联系。

 

你可能感兴趣的文章
源码实现lamp环境搭建的详细过程
查看>>
转vim用法
查看>>
Linux xargs命令
查看>>
我的友情链接
查看>>
Django中的request.GET和request.POST
查看>>
Android开发文档学习:NFC(近场通讯)
查看>>
站长工具导航
查看>>
Java基础-字符串
查看>>
Eclipse快捷键个人记录
查看>>
版本管理工具——Git和TortoiseGit(乌龟Git)
查看>>
【转】iOS 消息推送原理及实现Demo
查看>>
交换机VTP通告配置
查看>>
linux创建普通用户和管理员用户
查看>>
继承中构造函数和析构函数的调用顺序
查看>>
MySQL-MySQL中int(M)和tinyint(M)数值类型中M值的意义
查看>>
初始ant
查看>>
Web登录中的信心安全问题
查看>>
MySQL Proxy 实现 MySQL 读写分离提高并发负载
查看>>
linux特殊权限SUID, SGID, STICKY管理
查看>>
FTP 服务器 下载目录下的所有文件到本地(FTP模式 非 SFTP模式)
查看>>