首先我们强调一下opacity的概念是不透明度它表示的是两个图层之间的关系即该属性隶属于本图层它表示与本图层下面图层之间的像素合成关系当opacity=%时表示本图层完全不透明因此下面的像素完全被遮挡当opacity=时表示本图层完全透明即能看到下面的图层这是photoshop中最基本的一个算法表达如下 像素结果=底部图层*(opacity)+本图层*opacity; 当有多个图层xxx混合时表达如下(最底层的不透明度为) x=((k)x+k*x)(k)+k*x= (k)(k)x + k(k)x + k*x; 那么绘制透明度水印的方法也就非常直观了因为类库中的ImageAttributes属性里面并没有提供像素合成的绘制选项因此我们自己实现上面的算法方法是首先准备一个小的水印图片我们先把原图在水印下面的部分绘制上去然后在吧水印的文本或图片绘制上去然后把原图和水印图片的内存数据锁定(防止操作系统移动内存)然后直接用上面的算法改写原图的位图数据解锁内存即得到最终加了水印的图片 代码如下下面是绘制文本类型的水印只需提供水印文本内容绘制起始坐标即可注意为了简单直观起见代码中都没有做参数验证例如水印是否超出原图范围如果超出范围将引发对超过内存边界的访问限制(引发异常)下面使用了unsafe代码因此项目属性>Build中应勾选允许不安全代码否则无法编译 在下面代码中的定位方式是非常熟悉的再次强调的是以下的概念 scan指针内存数据的起始地址(换句话说就是指向第一个扫描行第一个像素的Blue) bppbit per pixel stride扫描行宽度=width*bpp/ 并在结尾补~个字节的以凑齐到字节整数倍 之所以横坐标乘以是因为我们锁定的方式是bppRgb(最后一个参数指定了数据的bpp)这意味这每个像素占据了个字节因此i要乘以来跳跃到下一个像素如果用bppRGB锁定则每个像素在内存占据字节相应的i应该乘以 Code文本水印 /// <summary> /// 给一个位图绘制水印文字(没有验证水印是否超出图片边界!) /// </summary> /// <param name=text>水印文本</param> /// <param name=x>起始点</param> /// <param name=y>起始点</param> /// <param name=opacity>不透明度~</param> private Bitmap DrawWatermark(Image imagestring text Font fontBrush brushint xint ydouble opacity) { Bitmap bm = new Bitmap(image); Graphics g=GraphicsFromImage(bm); //测量水印文字的大小然后申请一个新的位图 SizeF sizef=gMeasureString(textfont); Bitmap bm=new Bitmap((int)sizefWidth(int)sizefHeight); Graphics g=GraphicsFromImage(bm); gDrawImage(bm new Rectangle(x y bmWidth bmHeight)GraphicsUnitPixel); gDrawString(textfontbrush); BitmapData data=bmLockBits(new Rectangle(bmWidthbmHeight)ImageLockModeReadWritePixelFormatFormatbppRgb); BitmapData data=bmLockBits(new Rectangle(bmWidthbmHeight)ImageLockModeReadWritePixelFormatFormatbppRgb); unsafe { byte* p=(byte*)(void*)dataScan; byte* p=(byte*)(void*)dataScan; for(int j=;j<bmHeight;j++) { for(int i=;i<bmWidth*;i++) { p[(y+j)*dataStride+i]=(byte)(p[(y+j)*dataStride+i]*(opacity)+opacity*p[j*dataStride+i]); } } bmUnlockBits(data); bmUnlockBits(data); } return bm; } 还有一种情况是我们事先做作好水印它是一个图片更多的人在photoshop中使用一个自己设计好的logo保存为一个画笔形状制作时只要选中此画笔一盖就好了实际上这种类型的水印是一个图片为了加这种类型的水印引入下面的overload方法指定水印图片和透明色 Code图片水印 /// <param name=image>原图</param> /// <param name=wmImg>水印图片</param> /// <param name=key>透明色</param> /// <param name=x>起始点</param> /// <param name=y></param> /// <param name=opacity>不透明度</param> /// <returns></returns> private Bitmap DrawWatermark(Image image Bitmap wmImg Color keyint x int y double opacity) { Bitmap bm = new Bitmap(image); //克隆原图它也是我们的返回值 Bitmap bm = new Bitmap(wmImgWidth wmImgHeight); //准备的水印图片 Graphics g = GraphicsFromImage(bm); ImageAttributes att = new ImageAttributes(); attSetColorKey(key key ColorAdjustTypeBitmap); //设定透明色 gDrawImage(bm new Rectangle(x y bmWidth bmHeight) GraphicsUnitPixel); gDrawImage(wmImg new Rectangle(bmWidthbmHeight) bmWidthbmHeightGraphicsUnitPixelatt); gDispose(); 这里的代码和上面的方法代码相同因此省略 return bm; } 以上两种效果的截图 () () ASPNET中如何使用unsafe选项 实际需要在ASPNET中使用unsafe选项 集体的方法是找到工程的nfig文件在configuration节中加入: <dedom> <compilers> <compiler language=c#;cs;csharp extension=cs compilerOptions=/unsafe type=MicrosoftCSharpCSharpCodeProvider System Version= Culture=neutral PublicKeyToken=bace /> </compilers> </dedom> |