© 版權所有 conmajia 2012
在色彩豐富的圖片上顯示文字的時候,由於背景色變化的關系,文字往往不能清晰呈現。就像很多早期電影使用純白色字幕,在藍天白雲的畫面下常常看不清楚字幕。這時候,就需要文字描邊這種能夠突出顯示文字的效果了,就像這樣。
(動畫截圖 by Wong Shao Voon)
怎樣實現這樣的效果呢?下面我粗略介紹下目前常見的幾種方法。最后介紹我構思的一種簡單而實用的實現方法。
一般方法
第一種思路:文字即圖片
- 將TextBlock 轉換為WriteableBitmap
- 對WriteableBitmap 的Pixels 進行循環,判斷每一個像素點的值最終達到描邊的效果。
參考《Silverlight字體描邊》
這種思路將文字轉為圖片,根據判斷圖片上每個像素點,效率低下,最終效果簡單,如下圖所示。
第二種思路:像素着色器
該方法基本思路為:假定只會對TextBlock應用像素着色器,那么TextBlock是一個矩形,文字所在像素的 alpha分量必定大於0,否則必定是透明像素。判斷如果當前像素的上,下,左,右任意一個像素不透明,則說明本像素需要被描邊,否則就輸出文字顏色。由於需要知道相鄰像素,所以還需要傳入TextBlock的ActualWidth和ActualHeight。 這樣, 當前位置的 x+ 1/width 就是相鄰像素的坐標,就可以用tex2D函數來提取它的顏色值。還需要輸入描邊的顏色,還有文字的顏色。
參考《SILVERLIGHT像素着色器編寫簡明指南 附送文字描邊效果》
仍然是一個「大炮打蚊子」的思路,將文字進行逐像素的處理。和思路1唯一的區別在於使用了像素着色器,讓把部分工作交給GPU來完成,「看起來」很高效很快速。但是如果禁用了硬件加速就變成和1一樣了。下面是該方法的效果截圖。
第三種思路:GDI+路徑繪圖
這種方法不再是逐像素處理了。其基本思路是將文字字符串添加到GDI+的繪圖路徑中(GraphicsPath),然后再DrawPath()。利用不同的筆刷,這種方法可以「畫」出非常華麗的描邊效果,就像這樣。
這是擴展性最好的方法。由於利用了GDI+的高級特性,所以可以利用不同的筆刷如紋理、漸變,以及多次繪圖等方法做出非常精美的描邊、陰影效果。唯一的遺憾就是代碼量較大(比前面2種要少很多了)。
參考文章《C# 水印圖片+文字描邊+發光文字。看示圖及Demo》
關於該方法的高級效果設計,參考《Outline Text》一文(Code Project「Best C++/MFC article of Sep 2009」比賽獲獎文章)。
簡易方法
看了前面幾種設計思路后,你有沒有一頭霧水的感覺?或者眼花繚亂的感覺?難道我們只有這樣實現嗎?需要「描邊」文字,就一定要「描」嗎?
其實完全沒有必要。曾經我在山寨Safari時,介紹過一種簡單的通過重復繪制文字實現高光效果的方法。
如下圖所示。當底層文字和頂層文字相差1px時,就會呈現出不同的陰影/高光效果。如果把這個思路拓展下,把上、下、左、右四個方向的偏差結合到一起,就會像下圖最后顯示的效果一樣,得到了「描邊」文字的效果。
接下來在GDI+里面實現它。
1 // Code by Conmajia 2 // txtPoint是繪制文字的定位點 3 txtPoint.Offset(-1, 0); // 繪制左背景文字 4 e.Graphics.DrawString(this.Text, this.Font, backBrush, txtPoint); 5 txtPoint.Offset(2, 0); // 繪制右背景文字 6 e.Graphics.DrawString(this.Text, this.Font, backBrush, txtPoint); 7 txtPoint.Offset(-1, -1); // 繪制下背景文字 8 e.Graphics.DrawString(this.Text, this.Font, backBrush, txtPoint); 9 txtPoint.Offset(0, 2); // 繪制上背景文字 10 e.Graphics.DrawString(this.Text, this.Font, backBrush, txtPoint); 11 txtPoint.Offset(0, -1); // 定位點歸位 12 13 // 繪制前景文字 14 e.Graphics.DrawString(this.Text, this.Font, foreBrush, txtPoint);
下面請欣賞效果
結語
遇到問題,不一定需要完全跟着問題走。就像這次,需要「描邊」,但誰說的,非得要「描」邊呢?你要的只是效果,而不是過程,所以要跳出問題表象的禁錮,就能獲得更加寬廣的視野。
© 版權所有 conmajia 2012
(全文完)