[WPF]WPF中材质制作——图片和矢量图之争
如果要做下面這樣的一個東西作為背景。你會如何做呢?
圖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>
?
雖然看上去代碼更多了,但是由于它對齊到了像素點,所以其實性能更好。根據肉眼觀測,完全看不到閃爍現象。
?
但是這樣的向量圖已經失去了向量圖的意義了,向量圖的一個重點特點就是無極縮放。放大之后圖像依然平滑。但是這個向量圖其實就和一個標量圖的效果是一樣的了——放大之后就會有鋸齒出現。但是如果一個向量圖不會放大,用這樣方式還是很不錯,既提前了性能,又保證了圖像的清晰、銳利。但是這樣做的成本很高,做個小圖還可以,做個大圖就很不現實了。
圖片模糊
無論是標量圖還是矢量圖,實現項目中常常會出現模糊。下一篇將會為大家介紹幾種常見的導致模糊的情況和解決辦法。
總結
以上是生活随笔為你收集整理的[WPF]WPF中材质制作——图片和矢量图之争的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Web 单点登录系统
- 下一篇: 预告