用 C# 构建简单的水印工具
一篇关于用 C# 构建简单的水印工具的文章

引言
本文将介绍一种构建简单水印实用程序的方法,该实用程序可用于向任何支持的图像文件格式添加水印。生成的应用程序将允许用户将任何支持的图像文件格式打开到可滚动的图片框中,定义要应用为水印的文本(提供默认版本),设置水印的字体和颜色,定义水印的透明度,确定水印是在图像的顶部还是底部显示,以及在将水印保存到图像之前预览水印。

如果您对这张图片感兴趣,它是一只美国金翅雀,现在是一年中的这个时候,我大约有 50 只左右的鸟儿在我的喂鸟器附近徘徊。我用我的富士 S700 数码相机透过厨房窗户拍了这张照片。
入门
该解决方案包含一个名为 Watermarking
的单个 Windows Forms 项目,用 C# 编写;该应用程序仅包含一个窗体 (frmWatermark.cs),驱动应用程序所需的所有代码都包含在该单个 Form
类中。

代码:水印主窗体 (frmWatermark.cs)
该项目所需的所有代码都包含在该单个窗体中;本节将完全描述该窗体。
该 Form
类的代码以以下内容开头:(请注意突出显示的文本,显示添加到项目中的其他库;这些添加到默认配置中的库用于支持图像文件的操作以及将水印渲染到图像上)。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
namespace Watermarking
{
/// <summary>
/// An Image Watermarking Utility
/// </summary>
public partial class frmWatermark : Form
{
在声明命名空间和 Form
类之后,应用程序中的下一个工作是声明需要窗体范围的成员变量的集合;这些变量包含在一个名为 Member Variables
的已定义的 region
中。变量的声明如下;可以确定的是,这些变量用于跟踪当前文件位置和图像、用于将图像从一种格式转换为另一种格式的编解码器和编码器信息,以及用于显示图像水印的颜色和字体。
#region Member Variables
string CurrentFile;
Image img;
ImageCodecInfo myImageCodecInfo;
System.Drawing.Imaging.Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
System.Drawing.Color myWatermarkColor;
System.Drawing.Font myFont;
#endregion
Form
类中的下一个代码块是构造函数;在这种情况下,构造函数用于建立默认配置,包括水印颜色、水印的透明度级别、定位选项(顶部或底部)、水印中包含的文本以及用于显示水印的字体。
#region Constructor
/// <summary>
/// constructor with default configuration settings
/// </summary>
public frmWatermark()
{
InitializeComponent();
// setup default settings
myWatermarkColor = Color.SteelBlue;
cboOpacity.SelectedIndex = 2;
optTop.Checked = true;
txtWaterMark.Text = "Your Name " +
char.ConvertFromUtf32(169).ToString() + " " +
DateTime.Now.Year.ToString() + ", All Rights Reserved";
myFont = txtWaterMark.Font;
}
#endregion
在构造函数之后,定义了一个区域来处理文件输入/输出操作。该区域包含两个事件处理程序,一个用于选择“打开文件”菜单选项,另一个用于“保存”按钮的单击事件。“打开文件”菜单选项用于允许用户导航到并选择要加载的图像文件;选定的图像文件被放置到可滚动的图片框中。
“保存”按钮单击事件处理程序用于保存带水印的图像文件;原始文件可以重命名或覆盖。
#region File IO
/// <summary>
/// Open an image file into the picture box control
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
// configure the open file dialog to point to some
// common (usable) image file formats
openFileDialog1.Title = "Open Image File";
openFileDialog1.Filter = "Bitmap Files|*.bmp" +
"|Enhanced Windows MetaFile|*.emf" +
"|Exchangeable Image File|*.exif" +
"|Gif Files|*.gif|JPEG Files|*.jpg" +
"|PNG Files|*.png|TIFF Files|*.tif|Windows MetaFile|*.wmf";
openFileDialog1.DefaultExt = "bmp";
openFileDialog1.FilterIndex = 1;
openFileDialog1.FileName = "";
openFileDialog1.ShowDialog();
// if the user did not select a file, return
if (openFileDialog1.FileName == "")
return;
// update the current file and form caption text
CurrentFile = openFileDialog1.FileName.ToString();
this.Text = "Watermark Utility: " + CurrentFile.ToString();
try
{
// open the image into the picture box
img = Image.FromFile(openFileDialog1.FileName, true);
picContainer.Image = img;
// resize the picture box to support scrolling
// large images
picContainer.Size = img.Size;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "File Open Error");
}
}
/// <summary>
/// Save the Current Image with the Watermark
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSave_Click(object sender, EventArgs e)
{
try
{
// get the extension to figure out how to limit the save
// option to the current image file type
string strExt;
strExt = System.IO.Path.GetExtension(CurrentFile);
strExt = strExt.ToUpper();
strExt = strExt.Remove(0, 1);
// if the current image is, for example, a gif, only
// allow the user to save the file with the watermark
// as a gif
SaveFileDialog1.Title = "Save File";
SaveFileDialog1.DefaultExt = strExt;
SaveFileDialog1.Filter = strExt + " Image Files|*." + strExt;
SaveFileDialog1.FilterIndex = 1;
if (SaveFileDialog1.ShowDialog() == DialogResult.OK)
{
if (SaveFileDialog1.FileName == "")
{
return;
}
else
{
// save the file with the name supplied by the user
picContainer.Image.Save(SaveFileDialog1.FileName);
}
// update the current image file to point to the newly
// saved image
CurrentFile = SaveFileDialog1.FileName;
this.Text = "Watermark Utility: " + CurrentFile;
MessageBox.Show(CurrentFile.ToString() + " saved.", "File
Save");
}
else
{
MessageBox.Show("The save file request was cancelled by
user.", "Save Cancelled");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString(), "Image Save Error");
}
}
#endregion
在处理文件 IO 操作之后,下一个代码区域是图像格式转换部分。在该区域中,提供的方法用于将打开的图像转换为另一种格式(例如,位图到 JPEG,或 JPEG 到 GIF 等)。代码的每个部分都已注释,可以在以下内容中查看
#region Image Format Conversion
/// <summary>
/// Return the available image encoders
/// </summary>
/// <param name="mimeType"></param>
/// <returns></returns>
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
/// <summary>
/// Convert the current file to a bitmap
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void bitmapToolStripMenuItem_Click(object sender, EventArgs e)
{
// create a new name with the bitmap extension
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".bmp";
try
{
// save the file as a bitmap
img.Save(newName, ImageFormat.Bmp);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to bitmap.", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to an EMF file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void emfToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".emf";
try
{
img.Save(newName, ImageFormat.Emf);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to EMF format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to an EXIF file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void exifToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".exif";
try
{
img.Save(newName, ImageFormat.Exif);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to EXIF format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to GIF format
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void gIFFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".gif";
try
{
img.Save(newName, ImageFormat.Gif);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to GIF format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to JPEG format
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void jPEGFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".jpg";
// Get an ImageCodecInfo object that represents the JPEG codec.
myImageCodecInfo = GetEncoderInfo("image/jpeg");
// for the Quality parameter category.
myEncoder = System.Drawing.Imaging.Encoder.Quality;
// EncoderParameter object in the array.
myEncoderParameters = new EncoderParameters(1);
try
{
// Save the bitmap as a JPEG file with quality level 75.
myEncoderParameter = new EncoderParameter(myEncoder, 75L);
myEncoderParameters.Param[0] = myEncoderParameter;
img.Save(newName, myImageCodecInfo, myEncoderParameters);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to JPEG format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to PNG format
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pNGFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".png";
try
{
img.Save(newName, ImageFormat.Png);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to PNG format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image file to TIFF format
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tIFFFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".tif";
try
{
img.Save(newName, ImageFormat.Tiff);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to TIFF format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Convert the current image to WMF format
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void windowsMetafileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".wmf";
try
{
img.Save(newName, ImageFormat.Wmf);
CurrentFile = newName;
picContainer.Image = Image.FromFile(CurrentFile);
this.Text = "Watermark Utility: " + CurrentFile.ToString();
}
catch
{
MessageBox.Show("Failed to save image to WMF format.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
MessageBox.Show("Image file saved to " + newName.ToString(),
"Image Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
#endregion
在图像转换例程之后,下一个代码区域用于执行实际的水印功能。
#region Watermarking
/// <summary>
/// Display the watermark as it would appear after the
/// watermark were saved to the file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPreview_Click(object sender, EventArgs e)
{
// Update the application by reloading the image
picContainer.Image = Image.FromFile(CurrentFile);
int opac = 0;
string sOpacity = cboOpacity.Text;
// Determine the opacity of the watermark
switch (sOpacity)
{
case "100%":
opac = 255; // 1 * 255
break;
case "75%":
opac = 191; // .75 * 255
break;
case "50%":
opac = 127; // .5 * 255
break;
case "25%":
opac = 64; // .25 * 255
break;
case "10%":
opac = 25; // .10 * 255
break;
default:
opac = 127; // default at 50%; .5 * 255
break;
}
// Get a graphics context
Graphics g = Graphics.FromImage(picContainer.Image);
// Create a solid brush to write the watermark text on the image
Brush myBrush = new SolidBrush(Color.FromArgb(opac,
myWatermarkColor));
// Calculate the size of the text
SizeF sz = g.MeasureString(txtWaterMark.Text, myFont);
// Create a copy of variables to keep track of the
// drawing position (X,Y)
int X;
int Y;
// Set the drawing position based on the users
// selection of placing the text at the bottom or
// top of the image
if (optTop.Checked == true)
{
X = (int)(picContainer.Image.Width - sz.Width) / 2;
Y = (int)(picContainer.Top + sz.Height) / 2;
}
else
{
X = (int)(picContainer.Image.Width - sz.Width) / 2;
Y = (int)(picContainer.Image.Height - sz.Height);
}
// draw the water mark text
g.DrawString(txtWaterMark.Text, myFont, myBrush,
new Point(X, Y));
}
/// <summary>
/// Set the font and color of the font for the watermark
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnFont_Click(object sender, EventArgs e)
{
// default the current font and color to that
// used in the watermark textbox
fontDialog1.ShowColor = true;
fontDialog1.Font = txtWaterMark.Font;
fontDialog1.Color = txtWaterMark.ForeColor;
if (fontDialog1.ShowDialog() != DialogResult.Cancel)
{
myFont = fontDialog1.Font;
myWatermarkColor = fontDialog1.Color;
txtWaterMark.Font = fontDialog1.Font;
txtWaterMark.ForeColor = fontDialog1.Color;
}
}
#endregion
这就结束了驱动水印实用程序所需的代码总和。使用提供的代码,可以从一种图像格式转换为另一种图像格式,并对现有图像文件应用用户定义的水印。
摘要
虽然本文旨在演示在 WinForms 实用程序应用程序的上下文中对图像文件进行水印处理的方法;但可以在项目中使用的代码进行修改,以批量处理图像文件,或以编程方式对图像文件进行水印处理,而无需用户干预。即使作为实用程序使用,该应用程序也可能对希望在 Web 上公开图像文件之前对图像文件进行水印处理或添加标题的任何人都有一定的价值。