Introduction
Often when placing photographic images
on a website it is necessary to permanently overlay a watermark
and/or Copyright statement on top of that image. Such an
insertion helps to identify the person that took the photograph
and indicate the organization that holds the Copyright.
Doing this task by hand can become time consuming and is
often inconsistent. Using some simple techniques there is
an easy way to accomplish this programmatically using C#
and GDI+.
Overview
I am going to show you a variety of techniques
for manipulating an image. The following is a high level
list of some of those techniques:
- Inserting text on top of an image positioned
relative to the size on an image
- Dynamically choosing a
System.Drawing.Font
size to maximize readability
- Manipulating the opacity of a String
of text
- Replacing a specific color in a bitmap
to achieve transparency
- Changing the opacity of an image through
a 5x5 ColorMatrix
Define Images

(Photo courtesy of
AP wire) |
The first step
in this process is to load a photographic image
for which you would like to apply the watermark.
This image can be virtually any size and resolution.
For this example we will use an image that has a
width of 449 pixels and a height of 346. The
resolution is 72 dpi. |
When the Main method is instantiated, the
two variable of type string are defined. The
first will define where to find the photograph, watermark
and output the new image. The second will define the Copyright
string we will use as part of our watermark.
string WorkingDirectory = @"C:\Projects\WaterMark";
string Copyright = "Copyright © 2002
- AP Photo/David Zalubowski";
The following creates an Image object
from the specified file and then defines a variable for
both its Width and Height . These
dimensions are then used to build a Bitmap object
with a 24 bits per pixel format for the color data.
Lastly this Bitmap is then used to create a
new Graphics object from the specified Bitmap
image.
Image imgPhoto = Image.FromFile(WorkingDirectory
+ "\\watermark_photo.jpg");
int phWidth = imgPhoto.Width; int phHeight =
imgPhoto.Height;
Bitmap bmPhoto = new Bitmap(phWidth, phHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(72, 72);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
 |
This code loads the watermark image
that has been saved as a BMP and set with a background
color of Green (A=0,R=0,G=255,B=0). Once again it
defines a variable for both its Width and
Height |
(Image
courtesy of MLB.com) |
Image imgWatermark = new Bitmap(WorkingDirectory
+ "\\watermark.bmp");
int wmWidth = imgWatermark.Width;
int wmHeight = imgWatermark.Height;
Step #1 - Watermark Text
This code draws the imgPhoto
to the Graphics object positioning it (x= 0,y=0)
at 100% of its original size. All future drawing will occur
on top of the original photograph.
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.DrawImage(
imgPhoto,
new Rectangle(0, 0, phWidth, phHeight),
0,
0,
phWidth,
phHeight,
GraphicsUnit.Pixel);
To maximize the size of the Copyright message
we will test 7 different Font sizes to determine the largest
possible size we can use for the width of our Photograph.
To effectively do this, we will define an array of integers
then iterate through those values measuring the Copyright
string in the various point sizes. Once we have determined
the largest possible size we will exit the loop and draw
the text.
int[] sizes = new int[]{16,14,12,10,8,6,4};
Font crFont = null;
SizeF crSize = new SizeF();
for (int i=0 ;i<7; i++)
{
crFont = new Font("arial", sizes[i],
FontStyle.Bold);
crSize = grPhoto.MeasureString(Copyright,
crFont);
if((ushort)crSize.Width < (ushort)phWidth)
break;
}
Since all photographs will have varying
heights, determine a position 5% from the bottom of the
image. Use the Copyright strings height
to determine an appropriate y-coordinate for which to draw
the string. Determine its x-coordinate by calculating
the centre of the image then define a StringFormat
object and set the StringAlignment to Center.
int yPixlesFromBottom = (int)(phHeight *.05);
float yPosFromBottom = ((phHeight -
yPixlesFromBottom)-(crSize.Height/2));
float xCenterOfImg = (phWidth/2);
StringFormat StrFormat = new StringFormat();
StrFormat.Alignment = StringAlignment.Center;
Now that we have all of the necessary positioning
coordinates create a SolidBrush with a Color
of 60% Black (alpha value of 153).
Draw the Copyright string at the appropriate position offset
1 pixel to the right and 1 pixel down. This offset will
create a shadow effect. Repeat this process using
a White Brush drawing the same text directly
on top of the previously drawn string.
SolidBrush semiTransBrush2 =
new SolidBrush(Color.FromArgb(153, 0, 0,0));
grPhoto.DrawString(Copyright,
crFont,
semiTransBrush2,
new PointF(xCenterOfImg+1,yPosFromBottom+1),
StrFormat);
SolidBrush semiTransBrush = new SolidBrush(
Color.FromArgb(153, 255, 255, 255));
grPhoto.DrawString(Copyright,
crFont,
semiTransBrush,
new PointF(xCenterOfImg,yPosFromBottom),
StrFormat);
Step #2 - Watermark Image
Create a Bitmap based
on the previously modified photograph. Load this Bitmap into
a new Graphic Object Bitmap bmWatermark = new Bitmap(bmPhoto);
bmWatermark.SetResolution(
imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);
Graphics grWatermark =
Graphics.FromImage(bmWatermark);
To achieve a translucent watermark we will
apply two color manipulations by defining an ImageAttributes
object and setting two of its properties. The first step
in manipulating the watermark image is to replace the background
color with one that is transparent (Alpha=0, R=0, G=0, B=0).
To do this we will use a Colormap and define
a RemapTable . As previously shown my watermark
was defined with a background of 100% Green this will be
the color we search for and replace with transparency.
ImageAttributes imageAttributes =
new ImageAttributes();
ColorMap colorMap = new ColorMap();
colorMap.OldColor=Color.FromArgb(255, 0, 255, 0);
colorMap.NewColor=Color.FromArgb(0, 0, 0, 0);
ColorMap[] remapTable = {colorMap};
imageAttributes.SetRemapTable(remapTable,
ColorAdjustType.Bitmap);
The second color manipulation is used to
change the opacity of the watermark. This is done by applying
a 5x5 matrix that contains the coordinates for the RGBA
space. By setting the 3rd row and 3rd column to 0.3f we
achieve a level of opacity. The result is a watermark
which slightly shows the underlying image.
float[][] colorMatrixElements = {
new float[] {1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, 0.3f, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f}
};
ColorMatrix wmColorMatrix = new
ColorMatrix(colorMatrixElements);
imageAttributes.SetColorMatrix(wmColorMatrix,
ColorMatrixFlag.Default,
ColorAdjustType.Bitmap);
With both color manipulations added to
the imageAttributes object we can now draw
the watermark in the upper right hand corner of the photograph.
We will offset the image 10 pixels down and 10 pixels to
the left.
int xPosOfWm = ((phWidth - wmWidth)-10);
int yPosOfWm = 10;
grWatermark.DrawImage(imgWatermark,
new Rectangle(xPosOfWm,yPosOfWm,wmWidth,
wmHeight),
0,
0,
wmWidth,
wmHeight,
GraphicsUnit.Pixel,
imageAttributes);
Or last and final step will be to replace
the original Image with the new Bitmap, dispose of both Graphic
objects then save this Image to the file system.
imgPhoto = bmWatermark;
grPhoto.Dispose();
grWatermark.Dispose();
\\watermark_final.jpg",
imgPhoto.Save(WorkingDirectory + "
ImageFormat.Jpeg);
imgPhoto.Dispose();
imgWatermark.Dispose();
That's it! Compile the project, run it,
and see what happens! The code is fairly straightforward
if it all makes sense then these techniques can be used
for 100's of different image manipulations. The possibilities
are endless. |