原文:http://noark9.github.io/2013/12/28/cocoa-drawing-guide-study-part-1/git
cocoa drawing由AppKit提供而且也兼容其餘的模式:
Quartz,OpenGL,Core Image,Core Video,Quartz Composer,PDF Kit,QuickTime
基於Quartz,因此AppKit提供了Quartz相關的功能github
cocoa drawin基於Quartz能夠利用硬件資源進行渲染,而且使用的是打印機的模式。所以不一樣的繪圖順序也會獲得不一樣的結果。canvas
繪圖環境決定了繪圖最終的結果,canvas決定了繪圖內容在哪裏,繪圖設置控制繪圖的大小,顏色,質量,方向等。安全
繪圖上下文能夠說是繪製的位置,封裝了繪圖所須要的各類信息。
那麼繪圖的目標能夠是下面的東西多線程
NSGraphicsContext也管理了繪圖目標的狀態,這些狀態會影響如何繪圖,好比閒的寬度,顏色,填充色,繪圖狀態能夠保存在當前圖形上下文的棧上,全部的改變均可以經過回滾繪圖狀態來撤銷。
cocoa管理的一些屬性和Quartz不一樣,好比顏色使用NSColor,大部分的基於路徑的繪圖使用NSBezierPathapp
Coordinate System是cocoa支持使用Quartz應用的,使用浮點數,繪圖代碼繪製在用戶的協調空間,在渲染到具體設備的時候,轉換到設備的Coordinate Space。Coordinate Systemide
User Coordinate Space使用具體的值,每個單位是1/72英寸,但並不表明是72dpi。不使用像素或是dpi,你只須要考慮大小,而顯示由cocoa去關心。Device Coordinate Space使用設備的解析度單位,cocoa負責Device Coordinate Space和User Coordinate Space的轉換。函數
變形操做2d協調空間,變形使用一個變形的數學矩陣,獲取變形屬性如何修改協調,cocoa中變形是NSAffineTransform
類
那麼包括下面的東西:佈局
能夠用各類各樣的效果組合獲得有趣的結果,另外使用變形操做比你直接操做原數據要快字體
繪圖以前須要指定顏色和顏色空間,NSColor
和NSColorSpace
NSPoint
,點,有一個x,y座標NSSize
,size包含寬,高NSRect
,矩形包含一個叫origin
的NSPoint
和一個叫size
的NSSize
,origin
是左上角的點位置,size
是寬和高
能夠用NSBezierPath
畫一些基本形狀
貝塞爾路徑對象,保存着矢量的路徑信息,保證數據小而且分辨率獨立。你可使用簡單形狀建立路徑或是與其餘基本形狀組合更復雜的路徑。
由NSImage
提供圖片,NSImageRep是圖片的表示類,圖片能夠從文件加載或者在線(on the fly?),支持BMP,GIF,JPEG,JPEG2000,PNG,TIFF,從EPS,PDF,PICT數據獲得的圖片和CoreImage圖片
cocoa提供了先進的文件系統繪製文字,例如:
cocoa中基本上全部的繪圖都在view裏面完成,View對象表示一個窗口上可視的一部分。一個視圖用來顯示一些可視的內容,也能夠包括多個子視圖。
NSView
是全部視圖的基礎,cocoa有一些基礎的視圖,text view,split view等,cocoa控件也是基於NSView
的。
能夠基於基礎的視圖和控件建立自定義的視圖,cocoa經過drawRect:
消息告訴你視圖須要如何繪製,實現drawRect:
方法實現自定義繪圖。
1 2 3 4 5 |
- (void)drawRect:(NSRect)rect
{
// Custome draw code
}
|
drawRect:
調用時,cocoa將繪畫焦點鎖定到你的view,保存繪圖轉貼,調整當前的變形矩陣適應你的視圖的方向,調整從view中截取的矩形,咱們只須要完成繪製就能夠了。
打印和建立PDF,EPS的話,在這裏就不看了,實際用到的時候看吧。
在自定義視圖中實現drawRect:
方法可使用路徑,文字,圖形或者Cocoa,Quartz,OpenGL等東西
向View發送setNeedsDisplayInRect:
或者setNeedsDisplay:
消息,告訴View部分或者所有的內容已經失效而且須要更新,在下一個更新循環的時候cocoa會響應後發送一個drawRect:
消息給View進行更新。
使用Core Animation,設置Timer,或者使用NSAnimation或者NSViewAnimation類時在必定幀率產生的通知。接收到消息時,讓view中部分或者所有失效來進行強制更新。能夠參考Core Animation Programming Guide,以及後面會說道的NSTimer和使用Core Animation Objects。
NSView中的inLiveResize方法判斷是否正在發生改變大小的事件。爲了保證你的View可以如預期同樣,那麼儘可能少的進行繪製,能夠參考Drawing Performance Guidelines。
標識當前繪圖的上下文是設備,屏幕,仍是文件,Coordinate System,邊界等一些圖形屬性。基本上不須要人工建立一個圖形上下文。Cocoa應用中基本上全部的畫布都使用NSGraphicsContext。(OpenGL的話,使用NSOpenGLContext)
drawRect:
調用的以前,cocoa就會對當前的繪圖上下文先有必定的處理:
1 保存當前圖形狀態,保證能夠undo
1 添加一個適當的變形,保證方向跟如今View的方向是一致的
1 在可視範圍內截取區域,防止內容被其餘的視圖(Straying into other views)渲染
你的繪圖將會發送到Quartz Compositor和其餘在窗口中的視圖合併起來。
在你的drawRect:
結束後,cocoa回覆繪圖上下文爲下一個view繪製作準備。
能夠這樣獲取
NSGraphicsContext *context = [NSGraphicsContext currentContext];
使用saveGraphicsState
保存當前的圖形狀態
使用restoreGraphicsState
彈出當前的狀態並恢復到上一個保存的狀態
這兩個方法,調用須要成對
這兩個方法的類方法,做用於當前的圖形上下文
這兩個方法的實例方法,做用於特定的圖形上下文
映射view的Coordinate System和目標設備的Coordinate System用的。cocoa在調用drawRect:
以前會修改CTM,可使用一個NSAffineTransform
對象修改CTM的朝向,縮放,旋轉當前的Coordinate System。
截取區域描述了用於調用繪製函數的畫布區域,cocoa在調用drawRect:
以前會修改截取區域爲可視區域,可使用NSBezierPath
對象來進一步設置可視區域。
設置路徑的寬度,默認是1.0,可使用NSBezierPath
對象修改這個值。
線合併描述了線是如何合併的,默認是NSMiterLineJoinStyle
,可使用NSBezierPath
對象修改這個值。
描述一個路徑的開閉,默認是NSButtLineCapStyle
,可使用NSBezierPath
對象修改這個值。
描述線的斷開模式,也包括開始的模式,這個屬性沒有默認值,表示是實線,能夠修改NSBezierPath
對象的這個樣式。
定義線在何時合併成一個。只有Line join style使用了NSMiterLineJoinStyle
的時候起效。miter的長度已經被線的寬度除後,若是超過了miter limit的話,那麼就用斜面,默認是10.0,可使用NSBezierPath
對象修改這個值。
描述了其實是那一部分曲線被渲染了。數值越小,那麼曲線越平滑,也會有跟複雜的運算,這個值在不一樣的設備渲染時影響很是小。默認值是0.6,可使用NSBezierPath
對象修改這個值。
使用NSColor設置,渲染路徑的顏色。
使用NSColor設置,渲染路徑所包含區域的顏色(填充色)。
使用NSShadow描述所渲染內容的陰影。
描述顏色映射,cocoa不支持,須要使用Quartz。
使用NSFont設置
描述文字的字符空間。
描述文字如何進行渲染(這個屬性並非由Cocoa直接支持的)
描述渲染時圖形插值處理,使用NSGraphicsContext
類進行設置。
描述合成過程(cocoa中基於Quartz blend模式來進行支持,可是使用了不要同的使用方法和行爲),使用NSGraphicsContext
類進行設置,一些渲染方法可以提供額外的設置。
設置一個全局的alpha值,用於疊加在使用的顏色的alpha值上。cocoa不直接支持這個屬性,經過Quartz中的CGContextSetAlpha
函數設置。
設置抗鋸齒,使用NSGraphicsContext
設置。
打印原本是不考慮了,不過既然這裏單獨提到了一節,那我也就順便一塊兒看下了。
通常啊,cocoa的繪圖上下文,有兩種畫布,屏幕上的和打印的。在屏幕上的cocoa提供一個繪圖上下文做給有view更新繪製或者響應用戶打印文檔時使用。可是下面幾種狀況是須要手工建立圖形上下文的:
使用NSGraphicContext
的類方法,能夠建立使用屏幕做爲畫布的圖形上下文對象,這些方法沒法建立打印的畫布,cocoa把全部的打印都經過cocoa printing system(cocoa 打印系統)來處理這些任務併爲你提供繪圖上下文。所以,咱們須要使用NSPrintOperation
建立打印任務,而且你的view至少要提供最少的打印支持。
能夠用示例方法isDrawingToScreen
或者類方法currentContextDrawingToScreen
判斷畫布的類型。打印類型的畫布能夠用attributes
方法獲取畫布的更多信息。
NSGraphicsContext
在cocoa裏面是Quartz繪圖上下文CGContextRef
數據的一個轉化。轉化和使用很容易。
在drawRect:
方法中能夠用NSGraphicsContext
的方法直接修改大部分繪圖狀態屬性,可是還有一些屬性須要藉助其餘的一些對象。
保存和回覆當前繪圖狀態是消耗很是大的動做,儘可能在要立刻撤銷一些操做的時候,或者沒有修改的時候用,好比重置截取區域的時候。
cocoa提供了不少的顏色空間,NSColor默認支持RGB,CMYK,灰度,也能夠用ICC和ColorSync描述來支持自定義顏色空間。使用NSColor對象的setStroke
和setFill
方法來設置描邊和填充的顏色。文字的顏色不使用Fill或是Storke的,須要對文字專門進行設置。
路徑屬性包括了不少,上面提過一些,這裏說下設置路徑屬性有兩種,一種是全局的屬性經過NSBezierPath
類的類方法來設置,例如setDefaultLineWidth:
,另外一種是爲某個路徑對象設置,使用類方法設置,例如setLineWidth:
。
cocoa的字符串對象和core text系統都支持,使用屬性修改字符串的表現。NSAttributedString
對象經過選擇部分範圍進行設置屬性,NSString
直接設置整個字符串的屬性。
用NSFont
對象的set font family和size方法能夠設置圖形狀態中的全局字體設置,能夠在Quartz繪製字符串的時候使用。
嘛,這個麼,用官方的圖最好說明問題了
嗯,上面一堆圖,基本能說明問題了設置的名字基本都是NSCompositeXXXXXXXXXX
之類的
接下來是這些模式的一個數學模型(我仍是懶惰的截圖了),R是結果,S是源,D是目標,Sa是源的alpha值,Da是目標的alpha值,alpha值的範圍是0~1。
截取區域是用來肯定對view中的那一部分進行修改,調用drawRect:
以前cocoa會根據可視範圍設置截取區域,能夠用NSBezierPath
從新限制繪圖區域。簡單點的矩形能夠用NSRectClip
函數建立,NSRectClipList
建立一組矩形,NSBezierPath
用來建立更復雜的圖形。使用addClip
方法添加結果形狀到當前的截取區域。
根據不一樣的環繞規則,建立出來的截取區域也將不一樣,以下:
下面代碼建立上面圖形的截取區域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// If you plan to do more drawing later, it's a good idea // to save the graphics state before clipping. [NSGraphicsContext saveGraphicsState]; // Create the path and add the shapes NSBezierPath* clipPath = [NSBezierPath bezierPath]; [clipPath appendBezierPathWithRect:NSMakeRect(0.0, 0.0, 100.0, 100.0)]; [clipPath appendBezierPathWithOvalInRect:NSMakeRect(50.0, 50.0, 100.0, 100.0)]; // Add the path to the clip shape. [clipPath addClip]; // Draw the image. [NSGraphicsContext restoreGraphicsState]; |
可使用setClip
方法設置截取區域,可是這個方法將會替換原來的截取區域,
這個效果麼,你們應該都知道了,能夠用NSGraphicContext
中的setShouldAntialias:
方法設置。
若是在view和打印的畫布,能夠用cocoa提供的繪圖上下文。不過若是你要作其餘類型的繪圖,那麼就須要本身建立了。
若是須要在常規的更新週期以外繪製圖形,那麼須要單獨的建立一個圖形上下文。這樣能夠在另一個線程中,在一個屏幕外的窗口或是位圖上進行繪圖,以後再拷貝結果,使用NSGraphicsContext
類中的方法能夠爲特定的窗口或是位圖建立繪圖上下文。
使用graphicsContextWithWindow:
方法爲一個特定的窗口建立繪圖上下文,當時若是那個窗口有不少子視圖,那就很麻煩了,你須要一個一個的去設置,這個建議使用在建立屏幕外的緩衝區的時候用。
這裏說下,OSX的大部分窗口已經使用了雙緩衝機制,不須要淡騰的再去用屏幕外的窗口和位圖去刷新ui,費時費力。
在10.4之前, 須要經過截取view或是window,來實現位圖繪製。10.4之後能夠經過graphicsContextWithBitmapImageRep:
方法爲一個NSBitmapImageRep
對象設置繪圖上下文,繪圖將會直接渲染到位圖上。
不一樣與以前的那個PDF和PostScript不能那麼直接的建立,他們都要經過cocoa打印系統。
最快捷的方法是用NSView對象的dataWithPDFInsideRect:
和dataWithEPSInsideRect:
方法自動的用view中的繪圖代碼建立打印任務,輸出PDF和EPS。可使用NSPrintOperation
類手工建立打印任務,用runOperation
方法開始打印隊列。
打印過程當中,cocoa調用view上的一些方法來處理佈局和繪製,實現這些方法能夠支持PDF和EPS的打印。
AppKit爲全部窗口和線程的合併管理一個繪圖上下文。對應一個窗口,每一個線程都有一個繪圖上下文,也可使用另外一個線程來進行繪圖,可是這樣會有一些警告。
正常的窗口更新循環中,因此的繪圖請求都會發送到應用程序的主線程中。能夠經過調用setNeedsDisplay:
或者setNeedsDisplayInRect:
方法觸發重繪事件,這些方法不能在非主線程中調用。
若是必定要在非主線程中更新窗口那麼須要本身建立,配置那個窗口的繪圖上下文。
建立位圖是另外一種多線程繪圖的方式,由於位圖的畫,是本身做爲容器的,因此能夠安全的在非主線程中建立。