65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 GDI+ 为 .NET 创建带水印的照片

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (88投票s)

2002年9月23日

5分钟阅读

viewsIcon

425685

downloadIcon

12683

本文介绍了使用 GDI+ 和 C# for .NET 将水印图像叠加在照片上的过程。

引言

当我们在网站上放置照片图像时,通常需要在图像上永久叠加水印和/或版权声明。这种插入有助于识别拍摄照片的人并表明拥有版权的组织。手动完成此任务可能非常耗时且常常不一致。通过一些简单的技术,可以使用 C# 和 GDI+ 以编程方式轻松完成此任务。

概述

我将向您展示各种图像处理技术。以下是一些技术的高级列表:

  • 在图像上插入文本,文本相对于图像大小进行定位
  • 动态选择 System.Drawing.Font 的大小以最大化可读性
  • 修改文本字符串的不透明度
  • 将位图中特定颜色替换为透明色
  • 通过 5x5 ColorMatrix 更改图像的不透明度

定义图像

Small photo
(照片由美联社提供)

此过程的第一步是加载您想要应用水印的照片。该图像的尺寸和分辨率几乎可以是任意的。在此示例中,我们将使用一个宽度为 449 像素、高度为 346 像素的图像。分辨率为 72 dpi。

Main 方法被实例化时,会定义两个 string 类型的变量。第一个变量将定义照片的来源、水印以及新图像的输出位置。第二个变量将定义我们将用作水印一部分的 Copyright string(版权字符串)。

string WorkingDirectory = @"C:\Projects\WaterMark";
string Copyright = "Copyright © 2002 
                 - AP Photo/David Zalubowski";

以下代码从指定文件创建一个 Image 对象,然后为该图像的 Width(宽度)和 Height(高度)定义变量。然后,这些尺寸用于构建一个具有 24 位每像素颜色数据格式的 Bitmap 对象。最后,此 Bitmap 用于从指定的 Bitmap 图像创建一个新的 Graphics 对象。

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);

 

Watermark Image 此代码加载以 BMP 格式保存的水印图像,并将其背景色设置为绿色 (A=0, R=0, G=255, B=0)。同样,它为水印图像的 Width(宽度)和 Height(高度)定义了变量。
(图像由 MLB.com 提供)
Image imgWatermark = new Bitmap(WorkingDirectory
                     + "\\watermark.bmp");
int wmWidth = imgWatermark.Width;
int wmHeight = imgWatermark.Height;

步骤 #1 - 水印文本

此代码将 imgPhoto(照片图像)绘制到 Graphics 对象上,将其定位在 (x= 0, y=0),尺寸为原始大小的 100%。所有后续的绘制都将发生在原始照片之上。

grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.DrawImage(
	imgPhoto,                               
	new Rectangle(0, 0, phWidth, phHeight), 
	0,                                      
	0,                                       
	phWidth,                                
	phHeight,                               
	GraphicsUnit.Pixel);

为了最大化 Copyright(版权)消息的大小,我们将测试 7 种不同的 Font(字体)大小,以确定我们可以用于照片宽度的最大可能尺寸。要有效地做到这一点,我们将定义一个整数数组,然后遍历这些值,在不同的磅值下测量 Copyright string。一旦确定了最大可能尺寸,我们将退出循环并绘制文本。

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;
}

由于所有照片的高度都不同,我们将位置确定在图像底部 5% 的位置。我们将使用 Copyright string 的高度来确定绘制 string 的合适 y 坐标。通过计算图像的中心来确定其 x 坐标,然后定义一个 StringFormat 对象并将 StringAlignment 设置为 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;

现在我们有了所有必需的定位坐标,创建一个 SolidBrush(实心画笔),其 Color(颜色)为 60% 的黑色(alpha 值为 153)。将 Copyright string 绘制在合适的位置,向右偏移 1 像素,向下偏移 1 像素。此偏移量将创建阴影效果。重复此过程,使用白色的 Brush(画笔)将相同的文本绘制在先前绘制的 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);

步骤 #2 - 水印图像

基于先前修改的照片创建一个 Bitmap。将此 Bitmap 加载到一个新的 Graphic 对象中。

Bitmap bmWatermark = new Bitmap(bmPhoto); 
bmWatermark.SetResolution(
                imgPhoto.HorizontalResolution, 
                    imgPhoto.VerticalResolution);

Graphics grWatermark =
                 Graphics.FromImage(bmWatermark);

为了实现半透明水印,我们将应用两次颜色操作,方法是定义一个 ImageAttributes 对象并设置其两个属性。操作水印图像的第一步是将背景颜色替换为透明色 (Alpha=0, R=0, G=0, B=0)。为此,我们将使用 Colormap(颜色映射)并定义一个 RemapTable(重映射表)。如前所示,我的水印的背景色设置为 100% 绿色 - 这将是我们搜索并替换为透明的颜色。

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);

第二次颜色操作用于更改水印的不透明度。这是通过应用一个包含 RGBA 空间坐标的 5x5 矩阵来完成的。通过将第 3 行和第 3 列设置为 0.3f,我们实现了一定程度的不透明度。结果是水印稍微显示出下面的图像。

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);	

将这两个颜色操作添加到 imageAttributes 对象后,我们现在可以将水印绘制在照片的右上角。我们将图像向下偏移 10 像素,向左偏移 10 像素。

int xPosOfWm = ((phWidth - wmWidth)-10);
int yPosOfWm = 10;

grWatermark.DrawImage(imgWatermark, 
    new Rectangle(xPosOfWm,yPosOfWm,wmWidth,
                                     wmHeight),
    0,                  
    0,                   
    wmWidth,            
    wmHeight,		    
    GraphicsUnit.Pixel, 
    imageAttributes);

最后一步是将原始图像替换为新的 Bitmap,释放两个 Graphic 对象,然后将此 Image 保存到文件系统。

imgPhoto = bmWatermark;
grPhoto.Dispose();
grWatermark.Dispose();

\\watermark_final.jpg", 
imgPhoto.Save(WorkingDirectory + "
	ImageFormat.Jpeg);
imgPhoto.Dispose();
imgWatermark.Dispose();

就是这样!编译项目,运行它,看看会发生什么!代码相当简单,如果一切都说得通,那么这些技术可以用于数百种不同的图像处理。可能性是无限的。

修订历史

  • 2002 年 9 月 26 日 - 初次修订

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.