
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+.
常常在网站上放一张照片的时候,就需要放上一个持久的水印 和 / 或 版权声明在图片顶部。这样的一个插入帮助认识拍摄照片的人以及指出拥有版权的组织。手动做这项任务比较浪费时间,往往(多次插入)不一致。使用一些简单的编程技术(比如) C# 和 GDI+ 就可以完成这项任务。
过程中的第一步是载入你想应用水印的图片。图片实际上可以是任何大小和分辨率。这个例子里我们将使用一张 449 像素宽 346 像素高的图片,分辨率是 72dpi 。)
string
WorkingDirectory
=
@"
C:/Projects/WaterMark
"
;
string
Copyright
=
"
Copyright © 2002
-
AP Photo
/
David Zalubowski
"
;
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);
(Image courtesy of MLB.com)
这个代码载入水印图片,水印图片已经被保存为一个 BMP 文件,以绿色 (A=0,R=0,G=255,B=0) 作为背景颜色。再一次,会为它的 Width 和 Height 定义一个变量。
Image imgWatermark
=
new
Bitmap(WorkingDirectory
+
"
//watermark.bmp
"
);
int
wmWidth
=
imgWatermark.Width;
int
wmHeight
=
imgWatermark.Height;
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);
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;
}
int
yPixlesFromBottom
=
(
int
)(phHeight
*
.
05
);
float
yPosFromBottom
=
((phHeight
-
yPixlesFromBottom)
-
(crSize.Height
/
2
));
float
xCenterOfImg
=
(phWidth
/
2
);

StringFormat StrFormat
=
new
StringFormat();
StrFormat.Alignment
=
StringAlignment.Center;
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);
Bitmap bmWatermark
=
new
Bitmap(bmPhoto);
bmWatermark.SetResolution(
imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);

Graphics grWatermark
=
Graphics.FromImage(bmWatermark);
常常在网站上放一张照片的时候,就需要放上一个持久的水印 和 / 或 版权声明在图片顶部。这样的一个插入帮助认识拍摄照片的人以及指出拥有版权的组织。手动做这项任务比较浪费时间,往往(多次插入)不一致。使用一些简单的编程技术(比如) C# 和 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:
我将为你展示操作图片的各种各样的技术。以下是这些技术的
high level list
:
- 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
- 根据图片大小在图片顶部插入文本
- 动态选择一个System.Drawing.Font大小以达到最大的可读性
- 操作字符串文本的不透明性
- 在位图上替换一种特定的颜色以达到透明效果
- 在一个5x5彩色矩阵上改变图片的透明度
Define Images

(Photo courtesy of AP wire)

(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.
过程中的第一步是载入你想应用水印的图片。图片实际上可以是任何大小和分辨率。这个例子里我们将使用一张 449 像素宽 346 像素高的图片,分辨率是 72dpi 。)
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
类型变量。第一个将定义哪里去找照片,水印和输出新图片(的位置)。第二个将定义要用来作为我们的水印的一部分的版权字符串。




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
对象,然后为它的
Width
和
Height
定义变量。这些长度待会被用来建立一个以
24 bits
每像素的格式作为颜色数据的
Bitmap
对象。












(Image courtesy of MLB.com)
这个代码载入水印图片,水印图片已经被保存为一个 BMP 文件,以绿色 (A=0,R=0,G=255,B=0) 作为背景颜色。再一次,会为它的 Width 和 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.
这个代码以
100%
它的原始大小绘制
imgPhoto
到
Graphics
对象的(
x=0,y=0
)位置。以后所有的绘图都将发生在原来照片的顶部。










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.
为了最大化版权信息的大小,我们将测试
7
种不同的字体大小来决定我们能为我们的照片宽度使用的可能的最大大小。为了有效地完成这个,我们将定义一个整型数组,接着遍历这些整型值测量不同大小的版权字符串。一旦我们决定了可能的最大大小,我们就退出循环,绘制文本。


















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.
因为所有的照片都有各种各样的高度,所以就决定了从图象底部开始的
5%
的位置开始。使用
Copyright
字符串的高度来决定绘制字符串合适的
Y
坐标轴。通过计算图像的中心来决定
X
轴,然后定义一个
StringFormat
对象,设置
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.
现在我们已经有了所有所需的位置坐标来使用
60%
黑色的一个
Color
(alpha
值
153)
创建一个
SolidBrush
。在偏离右边
1
像素,底部
1
像素的合适位置绘制版权字符串。这段偏离将用来创建阴影效果。使用
Brush
重复这样一个过程,在前一个绘制的文本顶部绘制同样的文本。
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);
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);
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
对象并设置它的两个属性,我们就是实现了两个颜色的处理,以达到半透明的水印效果。处理水印图象的第一步是把背景图案变为透明的
(Alpha=0, R=0, G=0, B=0)
。我们使用一个
Colormap
和定义一个
RemapTable
来做这个。就像前面展示的,我的水印被定义为
100%
绿色背景,我们将搜到这个颜色,然后取代为透明。
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);
imgPhoto
=
bmWatermark;
grPhoto.Dispose();
grWatermark.Dispose();

//watermark_final.jpg
"
,
imgPhoto.Save(WorkingDirectory
+
"
ImageFormat.Jpeg);
imgPhoto.Dispose();
imgWatermark.Dispose();
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.
随着两个颜色处理加入到
imageAttributes
对象,我们现在就能在照片右手边上绘制水印了。我们会偏离
10
像素到底部,
10
像素到左边。
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.
就是它了!编译工程,运行它,看看发生了什么!如果代码都按步骤作了,它会是清楚易懂的,然后这些技术能被用作
100
种不同的图像操作。可能是无限的。














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.
最后的步骤将是使用新的
Bitmap
取代原来的
Image
。
销毁两个
Graphic
对象,然后把
Image
保存到文件系统。























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.
第二个颜色处理用来改变水印的不透明性。通过应用包含提供了坐标的
RGBA
空间的
5x5
矩阵来做这个。通过设定第三行、第三列为
0.3f
我们就达到了一个不透明的水平。结果是水印会轻微地显示在图象底下一些。















































Step #2 - Watermark Image
Create a
Bitmap
based on the previously modified photograph. Load this Bitmap into a new
Graphic
Object
根据前面修改后的照片创建一个
Bitmap
。把这个
Bitmap
载入到一个新的
Graphic
对象。







