如果要做下面這樣的一個東西作為背景。你會如何做呢?
圖1. 目標背景效果
方案一,用PS畫出來。然後把這個PNG圖片作為背景色。這個方案可以,但是 如果想讓這個線的顏色可配置呢?如何線的粗細不確定呢?無論哪個問題,用像 素圖的方式都不好解決。
方案二,用WPF的矢量圖繪制。這個方案可以比較容易地解決上面兩個問題。
但是無論我們用哪種方式,都不會把整個圖的大小畫出來。而是畫是一個最小 的圖元,然後重復。
標量圖方案
對於PS畫的標量而言,有下面這樣的一個圖就可以了。然後在要繪制的區域內 無限重復。尤其是做網頁的上的材質都是這樣,要知道圖片的大小可不是比文字 ,全部繪制出來的話圖片可能會很大。而且Visual Designer改起來也會比較累。
圖2. PNG材質圖片(放大後)
比如這個圖叫Twill.png,大小是6*6。那麼在WPF中使用這個圖元來建立圖1中 材質的代碼如下。
<DrawingBrush x:Key="PicTwillBrush"
Stretch="Fill" TileMode="Tile"
Viewport="0,0,6,6" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<ImageDrawing ImageSource="Textures\Twill.png" Rect="0,0,6,6"/>
</DrawingBrush.Drawing>
</DrawingBrush>
默認情況下,這個用方案一實現的材質很完美,和圖1完美一樣。但是如果用 ViewBox把這個圖片放下之後會出現下面的現象。
圖3. 實現渲染效果(ViewBox放大)
能看出圖片已經是一塊塊的了。你可以把這個看作WPF的Bug。如果我是QA我就 會這麼認為。雖然我也很理解這是平滑處理+TitleMode渲染的結果——平滑算法 沒有把這個大圖當成整個的一個圖處理,而是單獨地處理每一小塊。但是我也相 信這不是什麼難以解決的問題。所以它就是一個Bug,沒有什麼好說的。只不過這 是個小Bug,不Fix也就算了(因為有其它更嚴重的Bug等著MS的Dev們),反正有 辦法解決的。
一個不完全解決方案,可以使這個分塊的視覺效果弱化50.00%。就是把圖元改 成這個樣子。
圖4. 更好的PNG材質
不一定是一塊倆像素,一塊4個像素。不過兩者個數之差越小越好。所以最好 是兩條3像素的線。然後視覺圖會變成下面這個樣子。
圖5. 顯示效果(ViewBox放大)
比之前要好一些。至於原因讀者自己分析一下吧。(包括那個50.00%是如何計 算出來的)
所以有經驗的材質設計者,即使能用簡單的角銜接,也不會用,而只使用邊銜 接。(當然他們主要並不是為了這個原因)
上面討論完了位圖的作法。如果你的圖片不需要支持縮放,而且又不嫌PNG占 用空間大的話,用PNG圖片還是很不錯的方案的。
矢量圖的實現方案
有人會覺得在WPF做個矢量圖太簡單了。不就是用Expression Design畫麼?對 ,沒有錯,但是問題你要畫成什麼個樣子?因為矢量圖的畫法就多了。矢量的一 個特點就是,圖像不再是以像素為單位的。而是一個完完整整的圖形。我的第一 感覺是這樣的。(也許你的不是)
圖6. 向量材質圖(根本不可行)
有人可能一眼就看出問題了,這種做法裡,線的兩頭不是方的。多個這樣的小 圖接起來,線看上去也不會是連續的。解決辦法和標量圖的是一樣的。就是使用 邊銜接。把圖中的一條線分成兩條。下面是兩條做法。請讀者自行判斷哪種更好 ,並解釋原因。
圖7. 可選向量材質圖
上圖的代碼分別是:
<StreamGeometry x:Key="SimpleTwill">M3,0 L4,0 0,4 0,3 Z M3,6 L4,6 6,4 6,3 Z</StreamGeometry>
和
<StreamGeometry x:Key="BestTwill">M2.5,0 L3.5,0 0,3.5 0,2.5Z M2.5,6 L3.5,6 6,3.5 6,2.5 Z</StreamGeometry>
如果一開始你是用Expression Design來畫這個東西,相信代碼一定不會這麼 簡潔的。Expression Design最讓人不能忍受的就是生成出來的數據,常常有 1.00001這樣的東西。
對應的Brush代碼是:
<DrawingBrush x:Key="SimpleTwillBrush">
Stretch="Fill" TileMode="Tile"
Viewport="0,0,6,6" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="Red" Geometry="{StaticResource SimpleTwill}"/>
</DrawingBrush.Drawing>
</DrawingBrush>
但是無論向量圖自身的定義是多麼的“平滑”,不幸的是它最終還是要被渲染 到以像素為單位的顯示器上。更不幸的是,由於向量圖的定義一般不與顯示器的 像素格匹配,所以在渲染的過程中,勢必要做處理。結果渲染出來的就是下面這 個熊樣了。
圖8. 向量材質渲染效果
最上面一條是期望的效果,下面兩條就分別是由那兩個向量圖元定義出來的渲 染效果。如果看不清楚,下面是他們倆的放大圖。(請讀者自行判斷上面的結果 分別是基於哪個向量圖元繪制的)
圖9. 向量材質渲染效果
從圖中可以看出,最終的渲染結果沒有對齊到像素,嚴格來講就是渲染結果不 正確。(順便說一下,請不要提SnapsToDevicePixels這個屬性,如果你覺得這個 屬性可以解決這個問題,只說明你還沒有搞清楚這個屬性是在什麼時候發揮作用 的。其實即使我這樣說了,我感覺還是會有人回復說要用這個屬性,因為他們沒 有認真看文章。)
關於向量圖的性能問題
而且,向量沒有對齊到像素的另一個嚴重問題就是性能。這個性能差異用肉眼 就可以看得出來。如果你拖拽窗口,會發布由上面這兩個向量渲染出來的背景在 閃。(此測試方法理論上只在低配置系統上可行。)為什麼會閃,因為它比對齊 到像素的方式至少多計算1倍的像素點,而且每個像素點的顏色都要重新計算一遍 。這個計算量是很大的。
所以很多用WPF做過開發的人,會發現如果直接用Design繪制出來的復雜向量 圖做整個程序的背景,程序就會變得很卡,如果先轉成PNG之類的圖片,再用圖片 做背景,性能就會好得多。原因就在於此。使用向量,每個像素點的信息都要重 新計算出來。而使用圖片,如果圖片大小和背景大小一樣,則只需要原模原樣地 把圖片上的像素信息搬到顯示器上就行了,不需要計算什麼。
但是這並不能說向量圖就一定性能低下。如果能把向量圖,做得與像素點能對 應起來,向量圖也一樣可以具有很好的性能。比如上面的向量圖閃,如果我們把 向量圖定義成下面這個樣子就不會閃了。
高性能向量圖方案
圖10. 像素對齊的向量材質圖
這個向量的定義需要更多的代碼,如下所示:
<StreamGeometry x:Key="PixelTwill">M0,2 L1,2 1,3 0,3 Z M1,1 L2,1 2,2 1,2 z M2,0 L3,0 3,1 2,1 Z M3,5 L4,5 4,6 3,6 Z M4,4 L5,4 5,5 4,5 Z M5,3 L6,3 6,4 5,4 Z</StreamGeometry>
雖然看上去代碼更多了,但是由於它對齊到了像素點,所以其實性能更好。根 據肉眼觀測,完全看不到閃爍現象。
但是這樣的向量圖已經失去了向量圖的意義了,向量圖的一個重點特點就是無 極縮放。放大之後圖像依然平滑。但是這個向量圖其實就和一個標量圖的效果是 一樣的了——放大之後就會有鋸齒出現。但是如果一個向量圖不會放大,用這樣 方式還是很不錯,既提前了性能,又保證了圖像的清晰、銳利。但是這樣做的成 本很高,做個小圖還可以,做個大圖就很不現實了。
圖片模糊
無論是標量圖還是矢量圖,實現項目中常常會出現模糊。下一篇將會為大家介 紹幾種常見的導致模糊的情況和解決辦法。