Inkpad繪圖原理淺析

Inkpad是一款很是優秀的iPad矢量繪圖軟件,保管你一看見就忘不了。個人感受是」一覽衆山小」、」相見甚晚」,以致於我寫的TouchVG就是」小巫見大巫」。必須好好學習這款軟件的代碼,破解其高性能繪圖奧祕。git

若是你閱讀本文以爲哪裏寫得不明白,能夠提出來交流,若是本文能幫助你一點點就OK了,我也是在學習,本意不是想寫漂亮的文章。github

1、觸摸交互繪圖

交互式繪圖固然得先看觸摸響應機制,先看一張序列圖:canvas

觸摸繪圖響應序列圖

WDCanvas是表明繪圖畫布的主視圖,直接響應原始觸摸事件(touchesBegan、touchesMoved等),沒有使用雙擊、旋轉、長按等很流行的手勢識別器,奇怪吧?
我猜測Inkpad不使用手勢識別器有兩個緣由:(1)手勢識別器採用延遲識別技術進行手勢二義性判斷,有幾百毫秒的延遲,會影響繪圖快速響應的感受;(2)Inkpad是獨立的程序,全部界面都是本身的,不須要與各類帶有手勢識別器的界面組件共存(例如在滾動視圖、電子書頁面中繪圖)。     緩存


在WDCanvas中經過」[[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self]」向當前命令傳遞觸摸事件,這固然用的是單實例模式命令模式了。  
多線程

 
在WDCanvas的touchesBegan函數中不當即向交互命令WDTool發送觸摸開始事件,而是延遲到touchesMoved纔去發送。我猜測做者本想區分普通輕擊(Tap)和拖動(Pan),但在實現時並不完全:在下面的touchesEnded函數片斷中,沒有移動也是要發送touchesBegan的。我認爲應該在touchesMoved中檢查移動距離來判斷是否算是已移動,而不是簡單的直接切換到已移動狀態。框架

if (!controlGesture_ && [self canSendTouchToActiveTool]) {
    if (!moved_) {
        [[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self];
    }
    [[WDToolManager sharedInstance].activeTool touchesEnded:touches withEvent:event inCanvas:self];
}

 

在開始觸摸、當前不是鋼筆命令(WDPenTool)時,設置WDCanvas的activePath爲空(self.drawingController.activePath = nil)。當前路徑(activePath)用於記憶鋼筆命令的當前圖形路徑,將activePath定義在WDDrawingController中而不是定義在WDPenTool中,是方便於在多個類中檢查使用,不然要到特定的命令類獲取數據太麻煩了。異步

交互命令類的觸摸響應函數使用了以下所示的模板方法模式,具體的命令類重寫beginWithEvent、moveWithEvent、endWithEvent函數實現具體的繪圖邏輯。函數

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //.....
    WDEvent *genericEvent = [self genericEventForTouch:primaryTouch_ inCanvas:canvas];
    [self beginWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //......
    [self moveWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self endWithEvent:genericEvent inCanvas:canvas];
}
    
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self touchesEnded:touches withEvent:event inCanvas:canvas];
}

下面就要說說Inkpad的一大亮點:在動態拖曳繪圖中使用OpenGL ES高速繪圖了!工具

在繪圖命令(例如可繪製多種圖形的WDShapeTool)中,設置WDCanvas的shapeUnderConstruction屬性(待構建的圖形,WDPath對象),將當前的臨時圖形(每次都構造一個WDPath對象)傳遞給WDCanvas。WDCanvas的setShapeUnderConstruction函數將調用WDSelectionView成員的當即繪製函數,WDSelectionView使用OpenGL ES 1.1繪製各類動態圖形。用了OpenGL ES,拖動上千圖形也回顯毫無延遲感受,這是Inkpad的亮點。性能

(我以爲光把這段OpenGL ES渲染矢量圖形、文字的類提取出來都足夠實現一個很炫的動畫顯示軟件了吧)

Inkpad在使用OpenGL ES 1.1繪製動態圖形時,採用的優化顯示方法有:(1)單點線寬,(2)忽略虛線樣式,(3)不填充,(4)緩存圖形輪廓路徑。Inkpad對顯示性能是下了狠工的。

 

觸摸操做完成後就要向文檔提交靜態圖形了:(1)繪圖命令在endWithEvent中向當前層(WDLayer)提交新的圖形對象([canvas.drawing addObject:path]);(2)WDDrawingController 觸發WDCanvas視圖的重繪消息;(3)在WDCanvas的drawRect:函數中,遍歷各個圖形,調用各個對象的renderInContext函數顯示全部圖形,其實質是將圖形的path顯示到當前CGContext上(看似普通)。

與動態圖形的繪製相比,提交靜態圖形並無採用OpenGL ES,而是使用最簡單的CGContext顯示方式,並且是重畫所有圖形,沒有使用CALayer等高級渲染方式。這就產生一個疑問,大量圖形會不會太慢?我還沒去作性能分析實驗,初步估計Inkpad採用的顯示優化方法是:(1)緩存CGPath輪廓路徑和填充路徑等對象;(2)避免幾何計算和對象重構。

Inkpad已經這麼好了,就須要從新評估TouchVG最近作的一些顯示優化實驗,看看這些計算是否有必要:(1)在GCD中異步繪製,使用前臺圖形列表和後臺圖形列表避免多線程衝突;(2)在CALayer上提早繪製,在主視圖中只貼圖(使用renderInContext,利用GPU貼圖);(3)每一層圖形一個CALayer,避免重繪全部圖形;(4)是否有必要使用OpenGL ES 2.0繪製靜態圖形?

 

2、相關核心類

Inkpad繪圖核心類

一、WDCanvas:繪圖主視圖,包含標尺視圖、選擇集渲染視圖、刪除提示線視圖,包含交互命令類要用的臨時圖形對象。

二、WDCanvasController:繪圖界面操做類,負責多種UI控件的管理和操做分發。

三、WDDrawingController:負責各類圖形的增刪改查邏輯。WDCanvasController是外殼功能,WDDrawingController是內核功能。

四、WDDrawing:圖形文檔類,容納全部繪圖內容。

五、WDLayer:一個層,包含多個圖形元素WDElement。

六、WDStylable:可描邊、填充的圖形的基類。

七、WDAbstractPath:具備矢量路徑的圖形的基類。

八、WDPath:可指定線端箭頭形狀的矢量路徑的圖形類。

九、WDCompoundPath:複合路徑的圖形類。

 

十、WDTool:命令基類。

十一、WDShapeTool:添加幾何形狀的命令,可繪製矩形、橢圓、星形、多邊形、直線段、螺旋線。這些不一樣圖形的繪圖工具是在WDToolManager中構造的。

十二、WDPenTool:貝塞爾曲線繪圖工具。

1三、WDFreehandTool:自由畫光滑曲線工具。

 

對於藍色部分的Core.Model類,主要基於矢量路徑設計了幾何圖形模型類,好像不包含圖形的特徵數據(即後續編輯保持形狀特徵)吧。

我以爲咱們能夠基於Inkpad對圖形種類和交互命令工具進行擴充,作點行業相關的軟件來,若是你感興趣就加入討論吧。

這兩個UML圖是用EA畫的,InkpadUml.xmi能夠導入到其餘UML建模工具。

 

寫了半天有點累了,先寫到這吧。指望目標是把Inkpad吃透,結合TouchVG和TouchVGAnimation創造出一個新框架或軟件:)

相關文章
相關標籤/搜索