首先我們強調一下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>
From:http://tw.wingwit.com/Article/program/net/201311/13652.html