何 宇,林曉煥
(西安工程大學 電信學院,陜西 西安 710048)
ios是蘋果公司研發的手持設備操作系統,是目前最流行的終端設備操作系統之一,由于蘋果手持設備強大的硬件功能,以及靈活的平臺應用,所以ios可以靈活的處理復雜的動畫。隨著手持設備的普及,越來越多的工作也需要在手持設備上來完成,自定義繪圖可以更有目標性的在終端設備上顯示出需要重點顯示或注意的內容。目前,基于ios的繪圖(即沒有用雙緩沖技術,后稱普通繪圖)在重繪時有屏幕閃爍和波形不連續的現象,而雙緩沖技術的應用可以讓手持設備的界面更清晰,更光滑,更符合人們的視覺效果。文中主要通過在ios操作系統平臺上研究和實現雙緩沖技術繪圖來解決普通繪圖的重繪時屏幕閃爍和波形不連續的問題。
基于ios的繪圖可以依靠兩個不同的庫來實現繪圖功能,一個是Quartz 2D庫,它是Core Graphics框架的一部分;另一個是OpenGl ES庫,它是跨平臺的庫[1]。本文將從容易著手,使用方便的Quartz 2D繪圖介紹。Quartz 2D是一個2D繪圖引擎,它起源于Mac OS X,但是在iPhone OS中也有充分的應用[2]。
要實現繪圖功能,不僅需要添加繪圖所需要的庫文件,而且還要繼承特定的抽象類。UIViewController是畫面控制的中心類,是組成應用程序的重要元素。UIView是組成畫面的基本元素,UIView是擁有位置屬性及一定尺寸的四邊形。在ios上繪圖時,需要重載UIView里的方法來實現[3]。
Quartz 2D在繪圖的過程中使用了繪畫者模型。在繪畫者模型中,每個連續的繪制操作都是將一個繪制層放置于一個畫布上[4]。有了畫布之后,就需要定義繪圖所在的區域,即繪圖上下文。繪圖上下文包括了所有的繪圖參數,是一個數據類型,用來封裝Quartz繪制圖像的輸出設備信息[5]。數據類型包含了繪圖所需要的線條的粗細,顏色,繪制的路徑方向等。當我們加載抽象類UIView子類的drawRect:方法時,就會得到畫布和繪圖上下文,接下來就需要在drawRect:方法中設置與繪圖上下文相關的畫筆顏色和線條的寬度,一切準備就緒后,就可以在繪圖上下文中繪圖,完成繪圖操作后,圖形上下文必須負責將圖形呈現到畫布上[6]。
當動態(從上到下、從左到右)的繪圖時,蘋果繪圖自帶的重繪功能可以幫助開發人員減少不必要的開銷。重繪就是通過不斷地調用drawRect:方法里面定義的一小塊繪圖區域來實現大面積的繪圖,這樣就減少了系統內存的使用,但是為了減少重載時,不必要的內存消耗,蘋果并不建議直接調用drawRect:方法,而是通過調用UIView子類的set Needs Display方法,系統自動調用drawRect:方法來避免資源的浪費。所謂“雙緩沖技術繪圖”是指在內存中建立一個“圖形設備上下文”,而所有的繪圖操作都將在這個“圖形上下文緩存區”上進行,在需要顯示這個“圖形上下文”的時候,再把它更新到屏幕設備上。

圖1 應用程序的類圖Fig.1 The application diagram
普通繪圖的重繪,是在創建好的圖形上下文中設置一段路徑,然后通過不斷地重繪該段路徑來完成想要的圖形,普通的重繪在重繪已定義的路徑和更新界面之間有時間間隔,所以出現了波形不連續和界面閃爍的現象,如圖2所示。

圖2 普通重繪的波形圖Fig.2 Waveform diagram of ordinary redraw
要用雙緩沖技術實現繪圖功能,首先需要創建圖形上下文 CGContextRef, 蘋果為雙緩沖繪圖提供了 API CGContextRef CGBitmapContextCreate(void*data,size_t width,size_t height,size_t bitsPerComponent,size_t bytesPerRow,CGColorSpaceRef colorspace,CGBitmapInfo bitmapInfo)其 中data是指繪圖操作被渲染的內存區域,width和height分別是被渲染內存區域的寬度和高度,bitsPerComponent指被渲染內存區域上的控件在屏幕每個像素點上需要使用的bits位,colorspace指被渲染內存區域的顏色格式。
由于整個繪圖區域的大小是在父視圖調用抽象類UIView的子視圖時設置的,所以需要在initWithFrame方法里面根據父視圖傳回的 CGRect的大小設置繪圖需要渲染的區域,然后在 drawRect:里調用 CGColorSpaceCreateDeviceRGB()設置渲染區域的RGB顏色格式。繪圖的渲染區設置好后,就需要設置畫筆的顏色和畫筆的粗細,CGContextSetStroke ColorWithColor方法可以設置畫筆的顏色,CGContextSetLineWidth可以設置畫筆的粗細,為了應用更符合客戶端的操作,本文在父視圖里設置了顏色按鈕和粗細按鈕,用戶可以按照自己的想法選擇顏色和畫筆的粗細,接下來就需要創建繪制的路徑,首先需要把畫筆移動到起始點CGContextMoveToPoint,然后移動路徑到另一點CGContext AddLineToPoint,一點到另一點的方式多種多樣,本文主要是通過正弦波路徑到達,即 CGContextMoveToPoint(context,self.progress,260+sin ((3.1415926/45)*self.progress)*50); self.progress+=0.5; floatt=self.progress; CGPointgoPoint=CGPointMake (t, 260 +sin ((3.1415926/45)*t)*50);CGContextAddLineToPoint(context, goPoint.x, goPoint.y);現在可以用前面設置好的畫筆CGContextStrokePath()繪制創建的正弦波路徑。到此終于完成了第一次緩沖。現在把第一次繪制的圖形CGBitmapContextCreateImage緩沖到二次緩沖區CGImageRef, 為了避免內存泄漏, 在這里 可以CGContextRelease()釋放第一次緩沖區的圖形上下文,然后把二次緩沖區的內容通過UIImage的類方法轉換為UIImage型,最后調用drawInRect:把最終緩沖好的內容顯示在屏幕上,此時就可以看見清晰的波形。如圖3所示,根據不同的畫筆顏色和畫筆粗細繪制出了光滑的波形,并且在繪制波形的過程中,可以點擊保存按鈕來保存繪制的波形。

圖3 雙緩沖重繪波形圖Fig.3 Waveform diagram of double buffering redraw
普通繪圖的波形如圖2所示,與雙緩沖繪制的波形如圖3所示相比較,發現普通繪圖的(0,0)點在屏幕的左上角,雙緩沖繪圖的(0,0)點在屏幕的左下角,所以就會出現兩個圖所示的波峰(波谷)相反的情況。在代碼調試的時候,波形從左到右逐步繪制,但是普通繪圖先在繪圖上下文中繪制波形,然后將繪制的波形呈現在屏幕上,這兩步之間有一定的時間差,所以普通繪圖看上去不僅屏幕閃爍,而且波形也不連續。雙緩沖技術巧妙的解決了普通繪圖產生的屏幕閃爍和波形不連續的現象。雙緩沖技術繪圖先在創建的內存區域實現兩次緩沖繪圖,最后把整個內存區域直接繪制到屏幕上,這樣人們的視覺就不會看到繪圖操作和更新屏幕操作之間由于時間差而引起的屏幕閃爍和波形斷斷續續的現象。
經驗證,利用雙緩沖技術繪圖巧妙的解決了普通繪圖在重繪時引起的屏幕閃爍和波形不連續的現象,使繪制出的波形畫面更清晰、更符合人們的視覺效果,為基于ios操作系統強大動畫功能的畫面質感提供了有力的保障。
[1]Mark D,Nutting J,LaMarche J.iphone 4與ipad開發基礎教程[M].北京:清華大學出版社,2012.
[2]XMobileApp.iPhone創意開發入門與實踐[M].北京:人民郵電出版社,2010.
[3]王志剛,王中元,朱蕾,等.iphone UIKit詳解[M].北京:電子工業出版社,2012.
[4]DevDiv移動開發社區.ios開發實戰體驗[M].北京:海洋出版社,2012.
[5]劉威.Objective-C編程之道ios設計模式解析 [M].北京:人民郵電出版社,2011.
[6]夏偉頻.ios編程[M].武漢:華中科技大學出版社,2013.