簡介
給圖片添加水印是比較常用的功能,通常是用於給圖片添加版權的信息或者作者信息。
本文將重點向大家介紹怎麼使用GDI+(Graphics)給圖像添加圖片水印和文字水印。
圖片水印
技術要點
控制圖片水印位置
一般水印位置最多爲9個,如下圖所示,紅色矩形區域代表有效繪圖區域,9個位置就是根據紅色矩形寬高和水印圖片寬高(或者旋轉後所佔區域寬高)計算而來,切記,紅色矩形左上角的座標不是(0,0),這個座標怎麼得到後面的內容裏會提到。
代碼如下:
/// <summary>
/// 獲取圖片水印位置,及small在big裏的位置
/// 如果small的高度大於big的高度,返回big的高度
/// 如果small的寬度大於big的寬度,返回big的寬度
/// </summary>
/// <param name="pos">
/// 1左上,2中上,3右上
/// 4左中,5中, 6右中
/// 7左下,8中下,9右下
/// </param>
/// <returns></returns>
public Rectangle GetRectangleByPostion(Rectangle big, Rectangle small, int pos)
{
if (big.Width < small.Width)
{
small.Width = big.Width;
}
if (big.Height < small.Height)
{
small.Height = big.Height;
}
Rectangle retVal = small;
switch (pos)
{
case 1: retVal.X = 0; retVal.Y = 0; break;
case 2: retVal.X = (big.Width - small.Width) / 2; retVal.Y = 0; break;
case 3: retVal.X = big.Width - small.Width; retVal.Y = 0; break;
case 4: retVal.X = 0; retVal.Y = (big.Height - small.Height) / 2; break;
case 6: retVal.X = big.Width - small.Width; retVal.Y = (big.Height - small.Height) / 2; break;
case 7: retVal.X = 0; retVal.Y = big.Height - small.Height; break;
case 8: retVal.X = (big.Width - small.Width) / 2; retVal.Y = big.Height - small.Height; break;
case 9: retVal.X = big.Width - small.Width; retVal.Y = big.Height - small.Height; break;
default: retVal.X = (big.Width - small.Width) / 2; retVal.Y = (big.Height - small.Height) / 2; break;
}
retVal.X += big.X;
retVal.Y += big.Y;
return retVal;
}
控制圖片水印透明度
Graphics.DrawImage方法中有個參數可以傳入ImageAttributes,ImageAttributes可以設置顏色矩陣,從而實現DrawImage時透明效果,代碼如下:
/// <summary>
/// 獲取一個帶有透明度的ImageAttributes
/// </summary>
/// <param name="opcity"></param>
/// <returns></returns>
public ImageAttributes GetAlphaImgAttr(int opcity)
{
if (opcity < 0 || opcity > 100)
{
throw new ArgumentOutOfRangeException("opcity 值爲 0~100");
}
//顏色矩陣
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,(float)opcity / 100,0},
new float[]{0,0,0,0,1}
};
ColorMatrix colorMatrix = new ColorMatrix(matrixItems);
ImageAttributes imageAtt = new ImageAttributes();
imageAtt.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
return imageAtt;
}
控制圖片水印旋轉角度
這個問題要說清楚比較費勁,圖像處理工具類裏有個方法是直接獲取圖像旋轉N度後圖像,方法如下:
/// <summary>
/// 獲取原圖像繞中心任意角度旋轉後的圖像
/// </summary>
/// <param name="rawImg"></param>
/// <param name="angle"></param>
/// <returns></returns>
public Image GetRotateImage(Image srcImage, int angle)
///代碼略
獲取旋轉圖像的原理詳見本系列的另一篇文章《C#中基於GDI+(Graphics)圖像處理系列之任意角度旋轉圖像》
圖片水印方法的代碼
注意,傳入的參數中padding值決定有效繪圖區域,假如,圖片大小爲200*120,padding值爲10,那麼有繪圖區域就是(10,10,180,100),”控制圖片水印位置”章節中,紅色矩形左上角的座標就是(10,10),如下圖:
/// <summary>
/// 給圖片加入圖片水印,且設置水印透明度,旋轉角度
/// </summary>
/// <param name="destPath">保存地址</param>
/// <param name="srcPath">源文件地址,如果想覆蓋源圖片,兩個地址參數一定要一樣</param>
/// <param name="waterPath">水印圖片地址</param>
/// <param name="pos">設置水印位置,1左上,2中上,3右上
/// 4左中,5中, 6右中
/// 7左下,8中下,9右下</param>
/// <param name="padding">跟css裏的padding一個意思</param>
/// <param name="quality">1~100整數,無效值,則取默認值95</param>
/// <param name="opcity">不透明度 100 爲完全不透明,0爲完全透明</param>
/// <param name="angle">順時針旋轉角度</param>
/// <param name="error"></param>
/// <param name="mimeType"></param>
/// <returns></returns>
public bool DrawWaterImage(string destPath, string srcPath, string waterPath, int pos, int padding, int quality, int opcity, int angle, out string error, string mimeType = "image/jpeg")
{
bool retVal = false;
error = string.Empty;
Image srcImage = null;
Image waterImage = null;
Image destImage = null;
Graphics graphics = null;
try
{
//獲取原圖
srcImage = Image.FromFile(srcPath, false);
//獲取水印圖片
waterImage = Image.FromFile(waterPath, false);
var waterRect = new Rectangle(0, 0, waterImage.Width, waterImage.Height);
//定義畫布
destImage = new Bitmap(srcImage);
//獲取高清Graphics
graphics = GetGraphics(destImage);
//將源圖畫到畫布上
graphics.DrawImage(srcImage, new Rectangle(0, 0, destImage.Width, destImage.Height), new Rectangle(0, 0, srcImage.Width, srcImage.Height), GraphicsUnit.Pixel);
//不透明度大於0,則畫水印
if (opcity > 0)
{
//獲取可以用來繪製水印圖片的有效區域
Rectangle validRect = new Rectangle(padding, padding, srcImage.Width - padding * 2, srcImage.Height - padding * 2);
//如果要進行旋轉
if (angle != 0)
{
Image rotateImage = null;
try
{
//獲取水印圖像旋轉後的圖像
rotateImage = GetRotateImage(waterImage, angle);
if (rotateImage != null)
{
//旋轉後圖像的矩形區域
var rotateRect = new Rectangle(0, 0, rotateImage.Width, rotateImage.Height);
//計算水印圖片的繪製位置
var destRect = GetRectangleByPostion(validRect, rotateRect, pos);
//如果不透明度>=100,那麼直接將水印畫到當前畫布上.
if (opcity == 100)
{
graphics.DrawImage(rotateImage, destRect, rotateRect, GraphicsUnit.Pixel);
}
else
{
//如果不透明度在0到100之間,設置透明參數
ImageAttributes imageAtt = GetAlphaImgAttr(opcity);
//將旋轉後的圖片畫到畫布上
graphics.DrawImage(rotateImage, destRect, 0, 0, rotateRect.Width, rotateRect.Height, GraphicsUnit.Pixel, imageAtt);
}
}
}
catch (Exception ex)
{
error = ex.Message;
return retVal;
}
finally
{
if (rotateImage != null)
rotateImage.Dispose();
}
}
else
{
//計算水印圖片的繪製位置
var destRect = GetRectangleByPostion(validRect, waterRect, pos);
//如果不透明度=100,那麼直接將水印畫到當前畫布上.
if (opcity == 100)
{
graphics.DrawImage(waterImage, destRect, waterRect, GraphicsUnit.Pixel);
}
else
{
//如果不透明度在0到100之間,設置透明參數
ImageAttributes imageAtt = GetAlphaImgAttr(opcity);
//將水印圖片畫到畫布上
graphics.DrawImage(waterImage, destRect, 0, 0, waterRect.Width, waterRect.Height, GraphicsUnit.Pixel, imageAtt);
}
}
}
//如果兩個地址相同即覆蓋,則提前Dispose源資源
if (destPath.ToLower() == srcPath.ToLower())
{
srcImage.Dispose();
}
SaveImage2File(destPath, destImage, quality, mimeType);
retVal = true;
}
catch (Exception ex)
{
error = ex.Message;
}
finally
{
if (srcImage != null)
srcImage.Dispose();
if (destImage != null)
destImage.Dispose();
if (graphics != null)
graphics.Dispose();
if (waterImage != null)
waterImage.Dispose();
}
return retVal;
}
注意:如果發現有的方法沒有具體實現,請移步《C#中基於GDI+(Graphics)圖像處理系列之前言》獲取完整的圖像處理工具類源碼
文字水印
文字水印與圖片水印在實現上有所不同,主要原因是因爲圖片的寬高是固定的,而文字即使字數確定,顯示出來的行數無法確定,另外Graphics.DrawString本身是不支持透明的,後面有方法解決這個問題。
技術要點
控制文字水印位置
與圖片水印不同,文字的水印位置可以認爲是對齊方式,1左上,2中上,3右上,4左中等等,同也有設置內邊距的功能(padding)即控制有效繪圖區域,如下圖:
/// <summary>
/// 獲取文字水印位置
/// </summary>
/// <param name="pos">
/// 1左上,2中上,3右上
/// 4左中,5中, 6右中
/// 7左下,8中下,9右下
/// </param>
/// <returns></returns>
public StringFormat GetStringFormat(int pos)
{
StringFormat format = new StringFormat();
switch (pos)
{
case 1: format.Alignment = StringAlignment.Near; format.LineAlignment = StringAlignment.Near; break;
case 2: format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Near; break;
case 3: format.Alignment = StringAlignment.Far; format.LineAlignment = StringAlignment.Near; break;
case 4: format.Alignment = StringAlignment.Near; format.LineAlignment = StringAlignment.Center; break;
case 6: format.Alignment = StringAlignment.Far; format.LineAlignment = StringAlignment.Center; break;
case 7: format.Alignment = StringAlignment.Near; format.LineAlignment = StringAlignment.Far; break;
case 8: format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Far; break;
case 9: format.Alignment = StringAlignment.Far; format.LineAlignment = StringAlignment.Far; break;
default: format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; break;
}
return format;
}
控制文字水印透明度
由於DrawString不支持透明度,水印文字的透明需要藉助DrawImage,實現步驟如下:
(1)定義一個與目標圖片大小一樣的臨時畫布,var transImg = new Bitmap(destImage);
(2)在有效繪圖區域內,用DrawString將字畫到臨時畫布上(使用StringFormat控制對齊方式)
(3)將臨時畫布繪畫到目標圖布上(控制透明)。
文字水印方法的代碼
/// <summary>
/// 給圖片加入文字水印,且設置水印透明度
/// </summary>
/// <param name="destPath">保存地址</param>
/// <param name="srcPath">源文件地址,如果想覆蓋源圖片,兩個地址參數一定要一樣</param>
/// <param name="text">文字</param>
/// <param name="font">字體,爲空則使用默認,注意,在創建字體時 GraphicsUnit.Pixel </param>
/// <param name="brush">刷子,爲空則使用默認</param>
/// <param name="pos">設置水印位置,1左上,2中上,3右上
/// 4左中,5中, 6右中
/// 7左下,8中下,9右下</param>
/// <param name="padding">跟css裏的padding一個意思</param>
/// <param name="quality">1~100整數,無效值,則取默認值95</param>
/// <param name="opcity">不透明度 100 爲完全不透明,0爲完全透明</param>
/// <param name="error"></param>
/// <param name="mimeType"></param>
/// <returns></returns>
public bool DrawWaterText(string destPath, string srcPath, string text, Font font, Brush brush, int pos, int padding, int quality, int opcity, out string error, string mimeType = "image/jpeg")
{
bool retVal = false;
error = string.Empty;
Image srcImage = null;
Image destImage = null;
Graphics graphics = null;
if (font == null)
{
font = new Font("微軟雅黑", 20, FontStyle.Bold, GraphicsUnit.Pixel);//統一尺寸
}
if (brush == null)
{
brush = new SolidBrush(Color.White);
}
try
{
//獲取源圖像
srcImage = Image.FromFile(srcPath, false);
//定義畫布,大小與源圖像一樣
destImage = new Bitmap(srcImage);
//獲取高清Graphics
graphics = GetGraphics(destImage);
//將源圖像畫到畫布上,注意最後一個參數GraphicsUnit.Pixel
graphics.DrawImage(srcImage, new Rectangle(0, 0, destImage.Width, destImage.Height), new Rectangle(0, 0, srcImage.Width, srcImage.Height), GraphicsUnit.Pixel);
//如果水印字不爲空,且不透明度大於0,則畫水印
if (!string.IsNullOrEmpty(text) && opcity > 0)
{
//獲取可以用來繪製水印圖片的有效區域
Rectangle validRect = new Rectangle(padding, padding, srcImage.Width - padding * 2, srcImage.Height - padding * 2);
//獲取繪畫水印文字的格式,即文字對齊方式
StringFormat format = GetStringFormat(pos);
//如果不透明度==100,那麼直接將字畫到當前畫布上.
if (opcity == 100)
{
graphics.DrawString(text, font, brush, validRect, format);
}
else
{
//如果不透明度在0到100之間,就要實現透明效果,文字沒法透明,圖片才能透明
//則先將字畫到一塊臨時畫布,臨時畫布與destImage一樣大,先將字寫到這塊畫布上,再將臨時畫布畫到主畫布上,同時設置透明參數
Bitmap transImg = null;
Graphics gForTransImg = null;
try
{
//定義臨時畫布
transImg = new Bitmap(destImage);
//獲取高清Graphics
gForTransImg = GetGraphics(transImg);
//繪製文字
gForTransImg.DrawString(text, font, brush, validRect, format);
//**獲取帶有透明度的ImageAttributes
ImageAttributes imageAtt = GetAlphaImgAttr(opcity);
//將這塊臨時畫布畫在主畫布上
graphics.DrawImage(transImg, new Rectangle(0, 0, destImage.Width, destImage.Height), 0, 0, transImg.Width, transImg.Height, GraphicsUnit.Pixel, imageAtt);
}
catch (Exception ex)
{
error = ex.Message;
return retVal;
}
finally
{
if (transImg != null)
transImg.Dispose();
if (gForTransImg != null)
gForTransImg.Dispose();
}
}
}
//如果兩個地址相同即覆蓋,則提前Dispose源資源
if (destPath.ToLower() == srcPath.ToLower())
{
srcImage.Dispose();
}
//保存到文件,同時進一步控制質量
SaveImage2File(destPath, destImage, quality, mimeType);
retVal = true;
}
catch (Exception ex)
{
error = ex.Message;
}
finally
{
if (srcImage != null)
srcImage.Dispose();
if (destImage != null)
destImage.Dispose();
if (graphics != null)
graphics.Dispose();
}
return retVal;
}
注意:如果發現有的代碼中沒有具體實現,請移步《C#中基於GDI+(Graphics)圖像處理系列之前言》獲取完整的圖像處理工具源碼
完整示例程序源碼下載
http://download.csdn.net/detail/lhtzbj12/9730116
示例程序截圖
如果想查閱本系列其他文章,請移步《C#中基於GDI+(Graphics)圖像處理系列之前言》