基于C#实现数字水印工具
摘要
基于C#提供的Image类,实现数字水印比较容易;这里强调的是如何做可release的一个工具。
照片添加
利用C#的FolderBrowserDialog类,可以方便地的读取图像文件。本文是基于目录添加照片,所以需要进行目录遍历。如果打开的是目录,还需要打开子目录读取照片;在本文实现中,只读取JPG文件,并且在添加前试着读一下,避免一些图片不能读取,影响后续操作的有效性。
另外,从FolderBrowserDialog类得到路径后,不要忘记读取该目录下的文件,这是一个容易忽略的小问题,即
get_file(fbdlg.SelectedPath)
后再调用get_dir(fbdlg.SelectedPath)。
private
void get_dir(string parent)
{
DirectoryInfo di = new DirectoryInfo(parent);
foreach (DirectoryInfo subdir in di.GetDirectories())
{
get_file(subdir.FullName);
}
}
private void get_file(string parent)
{
DirectoryInfo di = new DirectoryInfo(parent);
foreach (FileInfo fi in di.GetFiles())
{
if (fi.Extension.ToUpper() == ".JPG")
{
try
{
Image test = Image.FromFile(fi.FullName);
if (test != null)
{
insertPhoto(fi);
test.Dispose();
}
}
catch (OutOfMemoryException ome)
{
MessageBox.Show(fi.FullName + " can not be added into album" + Environment.NewLine + "reason[" + ome.Message + "]");
}
catch (FileNotFoundException fnfe)
{
MessageBox.Show(fi.FullName + " can not be added into album" + Environment.NewLine + "reason[" + fnfe.Message + "]");
}
catch (ExternalException)
{
continue;
}
}
}
}
|
接下来的问题是:读入的照片,如何显示?考虑到本工具不是图片处理,需求Scope比较容易确定;缩略图是个最佳选择。想到缩略图,就想到了利用ListView来显示添加的图片:LargeImageList刚好满足要求;另外,ListViewItem的CheckBox正好用来选定图片。
this
.listView1.LargeImageList = il;
for (int i = 0; i < il.Images.Count; i++)
{
ListViewItem lvi = new ListViewItem();
lvi.Name = new FileInfo(img_files[i]).Name;
lvi.ImageIndex = i;
this.listView1.Items.Add(lvi);
}
|
图片选取
ListView的ListViewItem有个名为
Checked
的属性,只要改变这个属性,就能全部选择和全部取消选择。
private
void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (ListViewItem lvi in this.listView1.Items)
{
if (!lvi.Checked)
{
lvi.Checked = true;
}
}
}
private void unselectAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (ListViewItem lvi in this.listView1.Items)
{
if (lvi.Checked)
{
lvi.Checked = false;
}
}
}
|
水印定制
字符水印可以改变字体的大小、颜色、字体名和水印内容;这里提供定制模态对话框。在该对话框中有个RichTextBox来显示定制的实际效果;当然会使用FontDialog类。
FontDialog
fdlg = new FontDialog();
fdlg.Font = m_font;
fdlg.ShowColor = true;
fdlg.ShowEffects = true;
if (DialogResult.OK == fdlg.ShowDialog())
{
this.richTextBox1.Font = fdlg.Font;
this.richTextBox1.ForeColor = fdlg.Color;
}
fdlg.Dispose();
|
当对话框按确定按钮时,需要将定制信息保存下来,并持久化;本文使用注册表(关于注册表操作请参见《基于C#操作注册表》一文)。
Tips:关于模态对话框的实现。
在C#中,只有Form类;通过调用ShowDialog时指定parent,来实现模态。另外,还需要在对话框的确定和取消按钮的DialogResult属性,指定相应的返回值。
|
水印添加
添加水印的过程有点类似photoshop的图层:
1) 一个同样大小的Bitmap;new
2)复制图片属性,如分辨率信息,日期等;
3)先将该图层涂白;
4)再把原图画在上面;
5)然后把文字写在上面;
6)保存。
private
void watermarkphoto(string filename, Image img)
{
Bitmap b = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppRgb);
b.SetResolution(img.HorizontalResolution, img.VerticalResolution);
foreach (PropertyItem item in img.PropertyItems)
{
b.SetPropertyItem(item);
}
Graphics g = Graphics.FromImage(b);
g.Clear(Color.White);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
g.DrawImage(img, 0, 0, img.Width, img.Height);
SolidBrush brush = new SolidBrush(m_default_color);
StringFormat
format = new StringFormat(StringFormatFlags.NoClip);
format.LineAlignment = StringAlignment.Near;
format.Alignment = StringAlignment.Near;
SizeF stringSize = g.MeasureString(m_defalut_watermark, m_default_font);
g.DrawString(m_defalut_watermark, m_default_font, brush, new PointF((img.Width - stringSize.Width) / 2, img.Height *2 /3), format);
try
{
b.Save(filename, ImageFormat.Jpeg);
}
catch (ExternalException ee)
{
MessageBox.Show("fail to handle " + filename + " because " + ee.Message);
}
b
.Dispose();
brush.Dispose();
g.Dispose();
}
|
当然,改变字体后要计算一下水印宽度。
SizeF
stringSize = g.MeasureString(m_defalut_watermark, m_default_font);
|
结束语
基于上述思想,实现的数字水印工具已经发布给几个朋友使用,反映良好。拥有自己的水印工具后,展示照片就不担心侵权了。