首先我們強調壹下opacity的概念 是 不透明度 它表示的是兩個圖層之間的關系 即該屬性隸屬於本圖層 它表示與本圖層下面圖層之間的像素合成關系 當opacity= %時 表示本圖層完全不透明 因此下面的像素完全被遮擋 當opacity= 時 表示本圖層完全透明 即能看到下面的圖層 這是photoshop中最基本的壹個算法 表達如下
像素結果=底部圖層*( opacity)+本圖層*opacity;
當有多個圖層x x x 混合時 表達如下 (最底層的不透明度為 )
x=(( k )x +k *x )( k )+k *x = ( k )( k )x + k ( k )x + k *x ;
那麽繪制透明度水印的方法也就非常直觀了 因為類庫中的ImageAttributes屬性裏面並沒有提供像素合成的繪制選項 因此我們自己實現上面的算法 方法是 首先準備壹個小的水印圖片 我們先把原圖在水印下面的部分繪制上去 然後在吧水印的文本或圖片繪制上去 然後把原圖和水印圖片的內存數據鎖定(防止操作系統移動內存) 然後直接用上面的算法改寫原圖的位圖數據 解鎖內存即得到最終加了水印的圖片
代碼如下 下面是繪制文本類型的水印 只需提供水印文本內容 繪制起始坐標即可 註意 為了簡單直觀起見 代碼中都沒有做參數驗證 例如水印是否超出原圖範圍 如果超出範圍將引發對超過內存邊界的訪問限制(引發異常) 下面使用了unsafe代碼 因此項目屬性 >Build中 應勾選允許不安全代碼 否則無法編譯 在下面代碼中的定位方式是非常熟悉的 再次強調的是以下的概念
scan 指針 內存數據的起始地址 (換句話說 就是指向第壹個掃描行第壹個像素的Blue)
bpp bit 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 image string text Font font Brush brush int x int y double opacity)
{
Bitmap bm = new Bitmap(image);
Graphics g =Graphics FromImage(bm );
//測量水印文字的大小 然後申請壹個新的位圖
SizeF sizef=g MeasureString(text font);
Bitmap bm =new Bitmap((int)sizef Width (int)sizef Height);
Graphics g =Graphics FromImage(bm );
g DrawImage(bm new Rectangle(x y bm Width bm Height) GraphicsUnit Pixel);
g DrawString(text font brush );
BitmapData data =bm LockBits(new Rectangle( bm Width bm Height) ImageLockMode ReadWrite PixelFormat Format bppRgb);
BitmapData data =bm LockBits(new Rectangle( bm Width bm Height) ImageLockMode ReadWrite PixelFormat Format bppRgb);
unsafe
{
byte* p =(byte*)(void*)data Scan ;
byte* p =(byte*)(void*)data Scan ;
for(int j= ;j<bm Height;j++)
{
for(int i= ;i<bm Width* ;i++)
{
p [(y+j)*data Stride+i]=(byte)(p [(y+j)*data Stride+i]*( opacity)+opacity*p [j*data Stride+i]);
}
}
bm UnlockBits(data );
bm UnlockBits(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 key int x int y double opacity)
{
Bitmap bm = new Bitmap(image); //克隆原圖 它也是我們的返回值
Bitmap bm = new Bitmap(wmImg Width wmImg Height); //準備的水印圖片
Graphics g = Graphics FromImage(bm );
ImageAttributes att = new ImageAttributes();
att SetColorKey(key key ColorAdjustType Bitmap); //設定透明色
g DrawImage(bm new Rectangle(x y bm Width bm Height) GraphicsUnit Pixel);
g DrawImage(wmImg new Rectangle( bm Width bm Height) bm Width bm Height GraphicsUnit Pixel att);
g Dispose();
這裏的代碼和上面的方法代碼相同 因此省略
return bm ;
}
以上兩種效果的截圖
?( ) ( )
ASP NET中如何使用unsafe選項
實際需要在ASP NET中使用unsafe選項
集體的方法是 找到工程的nfig文件 在configuration節中加入:
<dedom>
<pilers>
<piler
language= c#;cs;csharp extension= cs
pilerOptions= /unsafe
type= Microsoft CSharp CSharpCodeProvider System Version= Culture=neutral PublicKeyToken=b a c e />
</pilers>
lishixinzhi/Article/program/net/201311/13652