6.透明,半透明和不透明
這是個大題目。在WinForm/WPF裡面我們經常會看到一些關於透明的屬性,比如Backcolor 裡面可以選擇Transparant, Form裡面有一個叫Opacity的屬性。都是和透明以及透明度相關 的。在其實是在GDI+應用層上的一些東西,在這裡我就不講了。主要從更基本的地方講起, 其中還包括兩塊完全不同的內容。
6.1 Alpha
我們在上一講中提到了PixelFormat,當時我們在LockBits的時候把PixelFormat設定成為 Format24bppRgb。但是如果你仔細研究,會發現其實裡面有各種各樣的圖片格式,其中有一 種叫做Format32bppArgb。這個意思是說除了RGB,在圖像中還存在一個通道,叫做A。這個A 就是用來描述當前像素是透明,半透明,還是全透明的分量。這個通道是2個叫Catmull和 Smith在上世紀70年代初發明的。通過這個分量,我們可以進行alpha混合的一些計算。從而 使表面的圖像和背景圖像混合,從而造成透明半透明的效果。在這種格式下A作為一個byte, 取值可以從0到255,那麼0表示圖像完全透明,則完全不可見,255則表示圖像完全不透明。 每個像素都可以實現這種透明或者半透明的效果。更詳細解釋可以參考 http://en.wikipedia.org/wiki/Alpha_compositing,或者去買本數字圖像處理的書回來看 。讓我們來看看下面這段代碼,這個函數可以把圖像變成半透明的。
1 public unsafe Bitmap GenerateBitmap(byte alpha)
2 {
3 FileStream fs = new FileStream (image, FileMode.Open, FileAccess.Read);
4 Image img = Image.FromStream(fs, false, false);
5 Bitmap bmp = new Bitmap(img);
6 img.Dispose();
7 fs.Close();
8
9 int width = bmp.Width;
10 int height = bmp.Height;
11
12 BitmapData bmData = bmp.LockBits(
13 new Rectangle(0, 0, width, height),
14 ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
15
16 byte* p = (byte*)bmData.Scan0;
17 int offset = bmData.Stride - width * 4;
18 for (int j = 0; j < height; j++)
19 {
20 for (int i = 0; i < width; i++)
21 {
22 p[3] = alpha;
23 p += 4;
24 }
25 p += offset;
26 }
27
28 bmp.UnlockBits(bmData);
29
30 return bmp;
31 }
32
大家可以注意一下第17,22和23行,由於圖像格式不對了,所以我們計算offset和遞加的 操作都該了,此外由於用小數端存儲方式,Alpha通道在p[3]的位置。
順便提一句,還有一種格式叫做Format32bppPArgb,這叫做premultiplied alpha,就是 說在RGB分量裡面,alpha分量的數據已經被預先乘進去了。比如說,一個半透明的紅色點, 在ARGB下,矢量是(255,0,0,128),而在PARGB下就變成了(128,0,0,128)。這是為了不要每次 都做乘法。
還有要注意的是,如果你想把這個Bitmap保存成為一個文件,那麼必須用png格式,才能 夠保存alpha通道的信息。如果你存為JPG/BMP/GIF,那麼alpha通道的信息將會被丟失。如果 存為BMP,那麼文件格式將變成Format32bppRgb,其中1個字節不再使用;如果保存為JPEG,那 麼是Format24bppRgb;存為GIF,格式將變成Format8bppIndexed。根據標准,BMP/JPG本來就 不支持透明通道,所以沒有可能保留透明信息。GIF倒是支持透明,但是GIF中顏色的信息都 是索引,所以Alpha的解釋對GIF完全沒有效果,接下去我們來分析怎麼樣使用GIF的透明。