Introduction
This article shall describe an approach to building a simple watermarking utility that may be used to add watermarks to any supported image file format. The resulting application shall permit the user to open any supported image file format into a scrollable picture box, to define the text to be applied as a watermark (with a default version supplied), to set the font and color of the watermark, to define the opacity of the watermark, to determine whether or not the watermark appears at the top or bottom of the image, and to preview the watermark prior to saving it to the image.
Figure 1: The Utility Application showing a Watermarked Image.
If you are at all curious about the image, it is of an American Goldfinch, this time of year, I have about 50 or so such birds hanging around my bird feeders. I took the picture with my Fuji S700 digital camera through the kitchen window.
Getting Started
The solution contains a single Windows Forms project called Watermarking
written in C#; the application contains only a single form (frmWatermark.cs) and all of the code necessary to drive the application is contained in that single Form
class.
Figure 2: Solution Explorer with the Project Visible.
Code: Watermarking Main Form (frmWatermark.cs)
All of the code necessary for this project is contained in this single form; the form shall be described entirely in this section.
The code for this Form
class begins with the following: (Note the highlighted text showing additional libraries added to the project; these additions to the default configuration were added to support the manipulation of the image files and the rendering of the watermark onto an image).
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
{
public partial class frmWatermark : Form
{
Following the declaration of the namespace and the Form
class, the next order of business in the application is to declare a collection of member variables requiring form wide scope; these variables are contained in a defined region
entitled, Member Variables
. The declaration of the variables follows; as can be determined, the variables are used to keep track of the current file location and image, codec and encoder information used to translate the image from one format to another, and the color and font used to display the image watermark.
#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
The next block of code in the Form
class is the constructor; in this instance, the constructor is used to establish a default configuration including the definition of the watermark color, the opacity level of the watermark, the positioning option (top or bottom), the text contained in the watermark, and the font used to display the watermark.
#region Constructor
public frmWatermark()
{
InitializeComponent();
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
Following the constructor, there is a region defined to handle file input/output operations. This region contains two event handlers, one for the selection of the Open File menu strip option, and one for the Save buttons’ click event. The Open File menu option is used to allow the user to navigate to and select an image file for load; the selected image file is placed into a scrollable picturebox.
The Save button click event handler is used to save the image file with the watermark; the original file may be renamed or overwritten.
#region File IO
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
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 (openFileDialog1.FileName == "")
return;
CurrentFile = openFileDialog1.FileName.ToString();
this.Text = "Watermark Utility: " + CurrentFile.ToString();
try
{
img = Image.FromFile(openFileDialog1.FileName, true);
picContainer.Image = img;
picContainer.Size = img.Size;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "File Open Error");
}
}
private void btnSave_Click(object sender, EventArgs e)
{
try
{
string strExt;
strExt = System.IO.Path.GetExtension(CurrentFile);
strExt = strExt.ToUpper();
strExt = strExt.Remove(0, 1);
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
{
picContainer.Image.Save(SaveFileDialog1.FileName);
}
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
After handling the file IO operations, the next region of code is the Image Format Conversion section. In this region, the methods provided are used to convert the open image into an alternative format (e.g., bitmap to JPEG, or JPEG to GIF, etc.). Each section of code is annotated and may be reviewed in the following:
#region Image Format Conversion
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;
}
private void bitmapToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".bmp";
try
{
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);
}
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);
}
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);
}
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);
}
private void jPEGFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string newName =
System.IO.Path.GetFileNameWithoutExtension(CurrentFile);
newName = newName + ".jpg";
myImageCodecInfo = GetEncoderInfo("image/jpeg");
myEncoder = System.Drawing.Imaging.Encoder.Quality;
myEncoderParameters = new EncoderParameters(1);
try
{
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);
}
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);
}
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);
}
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
Following the image conversion routines, the next region of code is used to perform the actual watermarking functions.
#region Watermarking
private void btnPreview_Click(object sender, EventArgs e)
{
picContainer.Image = Image.FromFile(CurrentFile);
int opac = 0;
string sOpacity = cboOpacity.Text;
switch (sOpacity)
{
case "100%":
opac = 255;
break;
case "75%":
opac = 191;
break;
case "50%":
opac = 127;
break;
case "25%":
opac = 64;
break;
case "10%":
opac = 25;
break;
default:
opac = 127;
break;
}
Graphics g = Graphics.FromImage(picContainer.Image);
Brush myBrush = new SolidBrush(Color.FromArgb(opac,
myWatermarkColor));
SizeF sz = g.MeasureString(txtWaterMark.Text, myFont);
int X;
int Y;
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);
}
g.DrawString(txtWaterMark.Text, myFont, myBrush,
new Point(X, Y));
}
private void btnFont_Click(object sender, EventArgs e)
{
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
That wraps up the sum of the code needed to drive the Watermarking utility. With the code provided, it is possible to convert from one image format to another and to apply a user defined watermark on an existing image file.
Summary
While this article was written to demonstrate an approach to watermarking an image file in the context of a WinFoms utility application; the code used in the project could be modified to batch process image files, or to watermark image files programmatically and without user intervention. Even used as a utility, the application could have some value to anyone wishing to watermark or caption an image file before exposing the image file on the Web.