Design and Implementation of Mobile Device-oriented Vector Drawing Platformhtml5
引用本論文: 張雲貴. 面向移動設備的矢量繪圖平臺設計與實現[D]. 北京:北京理工大學軟件學院, 2013.android
本論文的類似度爲0%,是源創論文。歡迎評閱討論,請勿抄襲,如需更多資料請在博客留言。ios
若是在研究或論文中使用到,歡迎回復或私信你的學校、姓名、研究領域,並在論文中添加引用或致謝。感謝你對開放成果的尊重和鼓勵。c++
本章根據移動設備的特色,設計了跨平臺繪圖內核及設備接口。爲了在iOS和Android上實現矢量繪圖平臺,本章設計了適配器的實現方式。後面兩章將基於本章內容具體闡述在iOS和Android上適配器的實現方式。算法
相對於Web應用,本地應用一般開發成本高、難以實現跨平臺。iOS和Android的主要編程語言分別是ObjC(即Objective-C)和Java,相互移植很困難。本文提出了基於虛函數多態行爲的跨平臺擴展機制。該機制將主要功能在跨平臺內核中使用C++實現,而在不一樣的移動平臺上實現適配器模塊,使其易於擴展。編程
對於iOS,界面封裝層採用ObjC和C++實現(實現文件的後綴名爲.mm)。ObjC類能夠調用C++類和全局函數,但不能從C++類繼承。國內外的類似軟件一般使用ObjC實現全部繪圖功能,不能跨平臺。json
本文針對iOS設計瞭如圖3‑1(a)所示的擴展機制:與設備平臺相關的類從繪圖內核的C++接口派生,以虛函數多態方式實現定製。其優勢是跨平臺內核能夠調用與設備平臺相關的C++類,C++類再調用ObjC類。創新點是能夠在跨平臺內核中使用C++實現易於擴充的功能,使用ObjC對內核進行擴展。canvas
對於Android,界面封裝層採用Java實現,在內核層與界面封裝層之間提供JNI封裝層,實現C++到Java的銜接轉化。一般的實現方案是使用Java實現全部繪圖功能,不多從C++類繼承和擴展。本文設計瞭如圖3‑1(b)所示的調用和回調機制:界面封裝層經過JNI調用內核,內核經過SWIG所生成的Director類回調到JNI的Java類,界面封裝層再從Java類派生,以虛函數多態方式實現定製。創新點是可在跨平臺內核中使用C++實現易於擴充的繪圖功能,經過SWIG在Android上實現定製。
基於多態的回調擴展機制的創新在於實現了「一次編寫,多處應用」,解決了目前在移動平臺之間代碼移植困難的問題。在跨平臺內核中不斷擴充功能不影響設備平臺適配器模塊,從而加強了擴展性,減小了移植工做。
跨平臺內核採用C++實現。C++適合iOS、Android、Windows Phone、Windows Desktop等操做系統。本文試驗總結了下列跨平臺編碼策略:
(1)使用標準C/C++函數庫(libc、libstdc++)進行數學計算、字符串操做、動態內存管理,不使用C++異常類型(exception)以免跨編程語言的異常未捕獲問題。
(2)避免使用各個平臺的保留關鍵字與已有名稱做爲變量和函數名。例如,在iOS上不使用id做爲變量名。爲了在iOS上避免宏定義、枚舉定義及變量的同名衝突,不使用命名空間來區分名稱,而是加上命名前綴,對枚舉值的名稱以k開頭。
(3)不使用複雜的多線程管理函數和鎖類型。將多線程管理放在設備相關的模塊中,少用全局變量和共享數據。使用輕量級的原子計數加減函數[1]進行訪問保護。
(4)在iOS和Android上,使用泛型編程和標準模板庫(STL)的容器類,不使用專用的容器等數據結構類型。
(5)由於iOS和Android不支持寬字符(wchar_t)類型,字符串默認採用UTF-8編碼,因此在跨平臺內核中採用UTF-8編碼、char*類型。
目前,主流的移動操做系統是iOS和Android。本文經過對這兩個平臺的特性分析得出下列與繪圖相關的結論:
(1)使用多點觸摸手勢的交互方式。單指和雙指手勢具備廣泛適用性,設備平臺已提供這些手勢的識別器,不須要從新開發手勢識別器。
(2)界面主要採用二維圖形引擎顯示。圖形引擎內部可使用OpenGL ES加速繪製,使用層(矩形紋理)進行顯示內容的緩存和動畫合成。官方推薦採用離屏渲染技術加速繪圖,在層上緩存渲染內容。
(3)交互式動態繪圖的刷新方式主要有兩種:標記視圖的無效區域,依靠消息機制延後刷新內容;在後臺線程中預渲染,完成後直接提交到視圖的層中。
(4)與繪圖相關的設備平臺差別,主要是使用了不一樣的編程語言、本地圖形庫、界面框架和消息處理方式。視圖和層採用樹狀層次結構顯示內容和傳遞消息事件。
根據iOS和Android的異同分析,本文提出了一種如圖3‑2所示的內核跨平臺的交互式繪圖模型。
本繪圖模型包含六大元素,具體描述以下。
(1)應用程序。集成繪圖平臺,定製界面佈局和效果,應用繪圖數據。
(2)視圖適配器。使用界面框架實現佔位視圖,響應繪製和觸摸消息,將顯示請求和識別出的手勢委託內核視圖處理。容許內核以回調方式刷新內容。
(3)畫布適配器。將畫布接口的顯示原語映射到特定圖形庫的繪製函數。
(4)內核視圖。管理圖形,將顯示和手勢請求轉發給交互命令和圖形實體。
(5)交互命令。將手勢請求轉換爲繪圖步驟,實現交互邏輯。對顯示請求,以動態圖形回顯交互效果。擴充交互命令可實現更多繪圖功能。
(6)圖形實體。在內核中管理和顯示圖形數據,擴充類型實現更多繪圖功能。
本繪圖模型的關鍵設計點:抽象出畫布接口和視圖接口,採用適配器模式解決平臺差別問題,基於委託模式實現圖形顯示和觸摸交互功能的跨平臺。
爲了下降模塊耦合性、在不一樣的設備平臺上提升可複用性,本論文的矢量繪圖平臺(TouchVG)按圖3‑3所示的分層架構設計。整體上分爲兩大層:跨平臺繪圖內核層和設備平臺相關的界面封裝層。在跨平臺繪圖內核層中實現設備平臺無關的功能。在界面封裝層中提供設備平臺特有的通用功能,供頂層的各類應用程序使用。
對於iOS,界面封裝層採用ObjC和C++實現。對於Android,界面封裝層採用Java實現。在繪圖內核層與界面封裝層之間提供JNI封裝層,容許Android的Java代碼與內核的C++代碼相互調用。實現相互調用的關鍵技術是使用SWIG進行編程語言的轉換和集成。在Windows桌面應用中,可使用C#和WPF開發界面封裝層及程序(也可使用MFC和GDI+實現)。SWIG也應用在C++與C#之間的銜接轉換上。
TouchVG在各個操做系統上有相應的繪圖平臺,這些平臺有相同的內核。例如,圖3‑4所示的iOS和Android繪圖平臺。這些繪圖平臺都是本地應用形式,能實現更大程度的融合和性能優化。在繪圖內核中實現主要的繪圖功能,經過在不一樣平臺上編譯同一代碼的方式適應多種平臺、避免重複開發、提升可複用性。
爲iOS提供的繪圖平臺以靜態庫的形式供各類繪圖應用程序使用,如圖3‑4(a)所示。應用程序無需使用繪圖內核的接口,只需使用界面封裝層所提供的簡易接口,從而下降了應用開發的工做量。界面封裝層和應用程序都基於iOS平臺的SDK開發,能夠實現統一的界面風格,下降實現難度。
爲Android應用程序提供的繪圖平臺包含一個JAR包(內核JNI和界面封裝層的Java類)和一個繪圖內核的本地動態庫,如圖3‑4(b)所示。應用程序經過使用界面封裝層所提供的簡易接口下降了集成難度。界面封裝層和應用程序都基於Android 平臺的SDK開發,能夠最大程度地利用平臺SDK的特性,實現緊密融合。
TouchVG平臺採用如圖3‑5所示的MVC架構模式設計動態行爲。
(1)在繪圖視圖中響應重繪消息,使用當前的繪圖上下文初始化畫布適配器,將此畫布適配器以抽象畫布接口對象傳入內核視圖,請求繪圖。
(2)內核視圖將靜態繪圖和動態繪圖的請求分別轉發給圖形實體和當前繪圖命令。圖形實體和繪圖命令調用畫布接口對象繪圖。在繪圖時畫布適配器將被回調。
(3)在繪圖視圖中響應觸摸消息,識別出手勢後委託內核視圖處理,由內核視圖轉發給當前繪圖命令進行交互式繪圖。
(4)繪圖命令根據觸摸位置改變臨時圖形,調用抽象視圖接口的重繪函數觸發重繪消息,在下次重繪時顯示該臨時圖形,從而實現動態繪圖。在一次觸摸結束後改變圖形實體,調用視圖接口的從新生成函數,這樣就會顯示新的圖形實體內容。
爲各類iOS繪圖程序提供的TouchVG繪圖平臺以靜態庫的形式出現。爲Android程序提供的TouchVG繪圖平臺包含JAR包和繪圖內核的本地動態庫。TouchVG繪圖平臺包含的模塊如圖3‑6所示。其中,refine表示有某個類實現了指定的接口。
TouchVG有下列六個核心模塊,除了GraphView模塊外都是跨平臺的模塊。
(1)GraphView:設備平臺相關的適配器模塊,包含視圖適配器和畫布適配器。
(2)CoreView:內核視圖模塊,爲上層適配器提供訪問接口,管理圖形數據,將顯示和手勢請求轉發給圖形實體或交互命令。
(3)Command:交互命令模塊,實現選擇命令及多個繪圖命令。這些命令用於繪製和修改各類圖形,爲上層界面提供動做消息接口以便接受觸摸手勢或鼠標消息數據。交互命令模塊在內部將動做消息分發給相應的命令對象,實現交互式繪圖。
(4)Shape:圖形實體模塊,主要功能是常見圖形的存儲、渲染和交互計算。
(5)Graphics:圖形接口模塊,實現顯示座標系變換、放縮平移計算、多種曲線形狀的顯示輸出。在圖形接口庫中不該用任何圖形庫,只是提供了適合多種平臺的畫布接口。由界面封裝層的畫布適配器類使用某一種圖形庫來實現該畫布接口。
(6)Geometry:數學幾何模塊,實現點、矢量、變換矩陣、矩形框、基於Bezier的曲線、路徑、剪裁、最短距離等幾何計算功能及方程組求解等數學算法。
其餘模塊或類:
(1)JsonStorage:用JSON實現的一種序列化類,實現了MgStorage接口。
(2)Test:跨平臺的單元測試模塊,供設備平臺的TestCanvas等程序使用。
TouchVG平臺的代碼目錄結構如圖3‑7所示,各個文件夾節點的含義見表3‑1。
主目錄 |
文件夾名 |
含義 |
core |
callback |
畫布接口和視圖接口 |
view |
供視圖適配器調用的內核視圖分發接口 |
|
test |
畫布接口的單元測試類 |
|
command |
交互命令模塊 |
|
shape |
圖形實體模塊 |
|
json |
JSON序列化器模塊 |
|
graph |
圖形接口模塊 |
|
geom |
數學幾何模塊 |
|
ios |
view |
視圖適配器和畫布適配器 |
tests |
iOS繪圖測試程序 |
|
lib |
iOS繪圖平臺的靜態庫工程 |
|
android |
mk |
編譯腳本 |
demo/jni |
本地動態庫的配置文件和自動生成的源文件 |
|
touchvg/jni |
SWIG生成的JNI類文件 |
|
touchvg/view |
視圖適配器和畫布適配器 |
|
vgdemo |
Android繪圖測試類、測試程序 |
本節描述與設備平臺顯示相關的實現策略和跨平臺繪圖內核相關的實現方式,爲在iOS和Android上實現了畫布適配器和視圖適配器設計接口。
內核跨平臺的矢量圖形顯示方式如表3‑2所示。在設備平臺上實現可自定義繪圖的視圖類(例如MyDeviceView),響應視圖的重繪消息,將繪圖上下文信息傳入畫布適配器(例如CanvasAdapter)進行初始化,而後將此畫布適配器傳入內核視圖(GiCoreView)請求繪圖,內核視圖再分發給相應的對象進行顯示。
在內核中調用抽象畫布接口(GiCanvas)的繪圖函數時,畫布適配器的對應實現函數將自動使用特定的圖形庫完成繪製。由於設備相關的視圖類與顯示哪些內容無關,因此在跨平臺內核中擴充圖形結構後不影響視圖類,實現了跨平臺繪圖。
交互式繪圖在上述靜態圖形顯示的基礎上,根據多點觸摸動做的位置信息,動態改變和顯示圖形。如表3‑3所示,其執行步驟以下:
(1)在繪圖視圖類中識別出多點觸摸手勢動做,委託內核分發手勢動做。在繪圖視圖類(例如MyDeviceView)中利用系統內置的手勢識別器從多點觸摸信息中識別出某種觸摸手勢,而後轉換手勢動做參數並委託內核處理(第8~10行)。
(2)內核視圖(GiCoreView)將手勢動做分發給當前的繪圖命令(第21~27行),由繪圖命令根據觸摸位置來改變臨時圖形的形狀。
(3)繪圖命令改變臨時圖形後,調用視圖接口(如表3‑4所示的GiView)的redraw函數,視圖適配器自動設置重繪區域和觸發重繪消息(第2八、13~14行)。
(4)視圖類在重繪響應函數中委託內核顯示動態圖形(第1~四、18~19行),內核的繪圖命令將調用抽象畫布接口(GiCanvas)繪製動態圖形(例如拖曳效果)。
函數名稱 |
接口函數定義 |
對應的GiCoreView函數 |
從新構建顯示 |
void regenAll() |
void drawAll(GiCanvas& canvas) |
追加顯示新圖形 |
void regenAppend() |
bool drawAppend(GiCanvas& canvas) |
更新顯示 |
void redraw() |
void dynDraw(GiCanvas& canvas) |
根據iOS和Android的手勢識別特色(例如Android須要自行實現放縮和旋轉手勢的識別算法),設計瞭如表3‑5所示的繪圖命令的手勢原語。其中,click、doubleClick、longPress是單指手勢,touchBegan、touchMoved、touchEnded是單指拖動手勢(使用最多,故分解爲三個),twoFingersMove對應於雙指捏合、旋轉和拖動手勢(三種手勢合併爲一個手勢原語是爲了不在設備平臺識別具體手勢類型)。
手勢原語 |
接口函數定義 |
點擊 |
bool click(const MgMotion* sender) |
雙擊 |
bool doubleClick(const MgMotion* sender) |
長按 |
bool longPress(const MgMotion* sender) |
開始拖動 |
bool touchBegan(const MgMotion* sender) |
正在拖動 |
bool touchMoved(const MgMotion* sender) |
拖動結束 |
bool touchEnded(const MgMotion* sender) |
鼠標掠過 |
bool mouseHover(const MgMotion* sender) |
雙指觸摸 |
bool twoFingersMove(const MgMotion* sender, int state, const Point2d& pt1, const Point2d& pt2) |
MgMotion包含當前觸點、起始觸點、觸摸動做狀態和視圖GiView對象等數據,經過MgMotion的view成員變量將設備平臺的視圖適配器對象傳遞到內核。
TouchVG平臺參考HTML5 Canvas標準[2]設計了跨平臺的畫布接口,定義了表3‑6中所列出顯示原語,適合多數圖形庫。
(1)基於路徑的矢量圖形顯示。
畫布接口支持子路徑,包含beginPath、moveTo、lineTo、bezierTo、quadTo、closePath及drawPath等7種顯示原語。設計要點:a、避免使用數組、點等複合數據類型,沒有折線和多邊形等須要可變數量座標的繪圖函數,主要使用浮點數和整數等簡單類型,以避免在JNI中生成複雜的參數類型。b、座標使用單精度浮點數而不是整數,與iPhone 四、iPad 3等高像素密度(PPI)的顯示屏相適應。
由於矩形、線段、圓、橢圓是較簡單且常用的圖形,大多數圖形庫都提供了這些圖形的繪製函數,因此在畫布接口中提供了drawRect、drawLine、drawEllipse繪圖函數。其餘矢量圖形能夠用這些路徑顯示原語表示。例如使用三次貝塞爾曲線表示任意角度的圓弧、四段貝塞爾曲線表示橢圓、將B樣條曲線和三次參數樣條曲線分解爲連續的三次貝塞爾曲線。
顯示原語 |
接口函數定義 |
設置畫筆 |
void setPen(int argb, float width, int style, float phase) |
設置畫刷 |
void setBrush(int argb, int style) |
清除區域 |
void clearRect(float x, float y, float w, float h) |
顯示矩形 |
void drawRect(float x, float y, float w, float h, bool stroke, bool fill) |
顯示橢圓 |
void drawEllipse(float x, float y, float w, float h, bool stroke, bool fill) |
顯示線段 |
void drawLine(float x1, float y1, float x2, float y2) |
開始新的路徑 |
void beginPath() |
添加子路徑 |
void moveTo(float x, float y) |
添加線段 |
void lineTo(float x, float y) |
添加貝塞爾曲線 |
void bezierTo(float x1, float y1, float x2, float y2, float x, float y) |
添加拋物線 |
void quadTo(float cpx, float cpy, float x, float y) |
閉合當前路徑 |
void closePath() |
繪製路徑 |
void drawPath(bool stroke, bool fill) |
保存剪裁區域 |
void saveClip() |
恢復剪裁區域 |
void restoreClip() |
矩形做爲路徑 |
bool clipRect(float x, float y, float w, float h) |
區域做爲路徑 |
bool clipPath() |
顯示控制點 |
void drawHandle(float x, float y, int type) |
顯示圖像 |
void drawBitmap(const char* name, float xc, float yc, float w, float h, float angle) |
單行文字 |
float drawTextAt(const char* text, float x, float y, float h, int align) |
(2)設置畫筆和畫刷。
包含setPen和setBrush設置函數。設計要點:a、顏色值使用整數(int argb),在Android上可直接應用該顏色值,在iOS等平臺上按字節順序提取各個顏色份量並轉換爲平臺特有的顏色類型。b、在跨平臺內核的GiGraphics類中保存線寬等繪圖參數,自動判斷和調用畫布接口的setPen和setBrush函數,避免在各個設備平臺的畫布適配器中重複實現該功能。
(3)圖形剪裁。
使用圖形庫的剪裁功能,包含saveClip、restoreClip、clipRect、clipPath函數。
(4)顯示圖像。
爲了簡化實現、隔離平臺的差別性,圖像在設備平臺中管理,在畫布接口中只使用名稱來標識圖像對象。drawHandle(x, y, type)顯示原始大小的控制點圖標等點狀圖像,drawBitmap(name, xc, yc, w, h, angle)在一個矩形框內顯示圖像。對於drawBitmap,使用實際寬高表達圖像放縮信息、角度表達圖像旋轉信息、圖像的中心位置表達位移信息,這三者共同表達圖像的矩陣變換信息,以任意角度和大小矢量化顯示圖像。
在跨平臺內核中使用命令模式和模板方法模式設計交互命令體系結構,如圖3‑8所示,每一個交互式繪圖命令都對應於一個命令類,支持表3‑5所示的手勢原語接口。每一個命令類對應有一個惟一標識的命令名稱,全部命令對象都在MgCmdManager命令管理器類中緩存。
內核中提供經常使用的基本繪圖命令,外界模塊能夠實現更多的交互命令類,將命令名稱和類工廠函數登記到命令管理器,在實際使用該命令時才建立命令對象。
在設備平臺相關的模塊中能夠不直接訪問交互命令,經過傳遞命令名稱啓動相應的交互命令(例如view.commandName=」circle」),由GiCoreView類將顯示請求和手勢動做轉發給當前命令。
TouchVG平臺採用二維變換矩陣實現矢量圖形的仿射變換(例如無級放縮顯示、平移顯示、旋轉變形),使用三種座標系:模型座標系、世界座標系和顯示座標系。其中,模型座標系和顯示座標系都參照世界座標系使用一個變換矩陣表示,分別記爲M和D。模型座標系到顯示座標系的變換矩陣爲M×D-1。顯示座標系的變換矩陣計算涉及下列變量:
(1)視圖中心點的世界座標(xc,yc),單位爲毫米。
(2)視圖顯示比例(scale)。爲1.0時表示按世界座標系等比顯示。
(3)視圖的寬度(width)和高度(height),單位爲點,與設備相關。
(4)視圖顯示時每英寸的點數(dpi),與設備相關。
按以下式(3-1)計算顯示座標系相對於世界座標系的變換矩陣:
本章提出了一種適合多種移動平臺的基於虛函數多態行爲的跨平臺擴展機制,將主要功能在跨平臺內核中使用C++實現,在不一樣的移動平臺上開發適配器模塊,易於擴展和實現。在Android上使用SWIG實現C++與Java的調用和擴展。
經過對移動平臺的差別分析,總結了iOS和Android在屏幕顯示和觸摸交互方式等方面的特色。提出了一種跨平臺繪圖模型,設計了跨平臺交互式繪圖內核,關鍵點是抽象出畫布接口和視圖接口,採用適配器模式解決平臺差別問題,基於委託模式實現圖形渲染和觸摸操做功能,具有跨平臺特性。
TouchVG平臺採用分層和MVC架構,主體功能在跨平臺內核中實現。爲移動平臺上的適配器實現工做描述了關鍵實現方式、畫布接口和視圖接口的設計意圖。
[1] 採用條件編譯方式實現原子計數加減函數,在Android上使用 __sync_add_and_fetch和 __sync_sub_and_fetch函數,在iOS上使用OSAtomicIncrement32和OSAtomicDecrement32函數。
[2] Canvas 2D Context:http://dev.w3.org/html5/2dcontext/ 。