給圖片添加文字水印

rkqp9592 8年前發布 | 10K 次閱讀 Bitmap Java開發

功能需求

  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);
}

/// <summary>
/// 繪制文字水印,文字大小以像素(Pixel)為計量單位
/// </summary>
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);
}

/// <summary>
/// 繪制文字水印,文字大小以像素(Pixel)為計量單位
/// </summary>
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;
}

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;
}

}</code></pre>

水印圖片透明度設置和旋轉(下面這段代碼和上面一段代碼都位于 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;
        }

    /// <summary>
    /// 設置圖像透明度,0:全透明,255:不透明
    /// </summary>
    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);
    }

}</code></pre> 

整個 Watermark 類對外只暴露 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 0 效果

旋轉前后,水印圖像的寬和高會發生變化,如下圖所示:

水印圖片旋轉前后寬高變化

擴展

上面的代碼很好的實現了在圖片上添加單行水印的效果,若要實現多行水印可以通過對 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;
        }
    }

具體的使用方式和調用 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,單位的不同導致水印圖像尺寸的計算出現偏差,這一點折磨我很久。

 

 

 

來自:http://www.jianshu.com/p/bc182bdfea73

 

 本文由用戶 rkqp9592 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!