[Net] WinForm 中 PictureBox 的圖層

先畫一張簡圖如下:
 
紅框線代表 PictureBox ,藍框線代表 PictureBox.Image ,黃色底圖代表 Image 已經載入某個圖形,例如用 Image.FromFile。
 
.Net 的繪圖系統是包裝 GDI+ (gdiplus.dll) ,所以很多部分跟原始的 GDI 有點格格不入。平常寫程式時,還沒感覺,大部分配合 Paint 事件處理,不然就是輸出成圖檔,再指給 PictureBox.Image ,讓自動重繪來處理螢幕更新顯示的問題。
 
當碰上實做小畫家要做不規則範圍的填色 (API: ExtFloodFill) 或是使用 Graphics.DrawImage 沒提供的 BitBlt 來做疊圖的互斥運算時,會發現糗大了,因為畫的東西跟原來的圖疊不在一起。
 
GDI 系列的 API 需用 hDC 才能繪圖,但是 g.GetHdc 的圖層並非是直接在圖上 (RowData 層) ,所以 GetPixel(hDC, x, y) 根本沒意義,因此:
Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)
Dim hDC As IntPtr = g.HetHdc
得到的是上面黑色的圖層。
使用 Graphics.DrawXXXX 方法,會直接畫到 RowData 圖層,所以平常也感覺不出來,但若對 hDC 層有任何更動後,要嘛就是直接覆蓋掉 RowData ,要嘛就是被忽略。
 
若改用 PictureBox 的方法:
Dim g As Graphics = PictureBox1.CreateGraphics
Dim hDC As IntPtr = g.HetHdc 
 
這層基本上跟 Paint 同層,也就是說若不在 Paint 中來畫,隨便一個動作後,就會自己被清掉了。此外這個圖層是把螢幕上該位置的圖切出來,也就是說,若是 PictureBox 不顯示,或是前面還有其他視窗,就會被其他視窗干擾,造成圖片的內容不正確。
 
所以在 .Net 中,直接呼叫 GDI 函數,其實還好,只要 Image 的 RowData 是空的,就可以直接覆蓋,但是若是要對既有的圖片編修,使用 Graphics.DrawXXXX 還算簡單,要用 GDI 函數處理時,就千難萬難。
這邊有兩個範例:
函數 FloodFill 在封閉區域填色
函數 PaintImage 呼叫 API BitBlt 將兩張圖疊繪在一起
 
由於 Image 是屬於無介面類別,所以繞路走的方向就是把記憶體中的 Bitmap 畫到 DC 上,此時再對 hDC 作業,處理完畢後,將此 DC 轉回成 .Net 內建的 Bitmap 類別,最後取代原先的 Image ,完成 hDC 的操作。
 
 
廣告
Categories: 技術分享 | 發表留言

文章分頁導航

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

在WordPress.com寫網誌.

%d 位部落客按了讚: