robot
最新文章(10)
這並不是一個創新
Tossug 2/9 SVG 加 XBL 分享
SVG and XBL No Widget
OpenSource 與嵌入式系統
Internet 的工人智慧
GCC Spec Files
Android Native code 的繪圖方法
Android Native code 不用 NDK
基於隱私的一種 DRM
Build Android SDK on FreeBSD
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (120)
Python (93)
FreeBSD (71)
WEB (61)
雜記 (48)
URL (48)
hardware (46)
javascript (36)
Linux (31)
blog (30)
所有關鍵字
新增 URL
Android Native code 的繪圖方法
by thinker
2 Columns
關鍵字:
Android
大部分 $Android$ $應用$程式,應該都是用 Java Code 完成所有的工作,包括繪圖。但,有些情況下,你會希望繪圖有更快的反應;例如 game ,這時 native code 可能是一個選擇。 在 $Android$ 上,有一個 $graphic$ engine ,稱為 Skia 。 Skia 的功能約等於 Cairo ,功能上相似,但 Skia 的支援沒 Cairo 廣。其實, $Android$ 的 Java Code 都是透過 Skia 進行繪圖,而 Skia 主要的 class type 是 SkCanvas ,所的有繪圖功能都建構在這個 class 上。因此,如果我們能在 native code 取得 $Android$ 所建立的 SkCanvas ,能直接使用 Skia 對畫面做輸出。 在 $Android$ 的 UI 設計裡,每一個 UI component 都是一個 view ;例如: button 、 label 等等,全是 view 。當 $Android$ 要畫一個 view 時,會呼叫 view 的 onDraw() 畫出 component 的外觀。而 $Android$ 會將一個 android.graphics.Canvas type 的物件,當成參數給 onDraw() 。 onDraw() 就在這個 canvas 上輸出 component 的外觀,例如畫一個 button 。這個 canvas 其實就對映到一個 SkCanvas ,我們只要在這個 canvas 上作畫,就等於畫到畫面上的一個區域。 == Native code == $Android$ 是透過 JNI 呼叫 native code ,於是我們在 Java side 定義了一個 View ,使該 View 透過 JNI 把 canvas 傳送給我們的 native code ,以便在 native code 裡繪圖。 {{{#!cpp package com.example; import android.view.View; import android.graphics.Canvas; class myview extends View { public void onDraw(Canvas canvas) { nativedraw(canvas); } native void nativedraw(Canvas canvas); static { System.loadLibrary("mynative"); } } }}} 在個這 class 裡,我們將 canvas 透過 JNI 傳給 native code ,接下來我們定義 JNI 的 native function 。 {{{#!cpp #include <SkCanvas.h> extern "$C$" { #include <jni.h> void Java_com_example_myview_nativedraw(JNIEnv *env, jobject thiz, jobject obj) { jclass cls; jfieldID fid; SkCanvas *canvas; cls = env->GetObjectClass(obj); fid = env->GetFieldID(cls, "mNativeCanvas", "I"); canvas = (SkCanvas *)env->GetIntField(obj, fid); /* ..... draw on canvas .... */ } } }}} obj 這個參數就是 Java side 的 canvas ,而 SkCanvas 是放在 Java side canvas 的 mNativeCanvas 欄位,是一個整數。這個整數其實就是 SkCanvas 的位址,我們直接 casting 成 SkCanvas 的 pointer ,如此我們就取得 SkCanvas ,可以透過 Skia 介面直接作圖。 注意!! JNI 是定義在 $C$ 語言上,而 Skia 是 $C++$ 的 library 。因此,我們使用 $C++$ 寫 native code ,但在 JNI 的 function 定義時,必需用 extern "$C$" 包起來,告知 $C++$ compiler 這個 function 是使用 $C$ 的呼叫規則。 == 動畫 == 如果你要在 native code 裡作動畫,那麼就必需要能更新畫面。如果只是把 canvas 的位址存起來,不斷的對 canvas 做更新,你會發覺畫面並不會跟著更新。正確的作法是要呼叫 View 的 invalidate() function ,讓 $Android$ 進行更新的動作。 當 View.invalidate() 被呼叫之後, $Android$ framework 會呼叫 View 的 onDraw(),這時呼叫 Native code 進行畫圖,更新的結果才會顯示在畫面上。因此,進行動畫時,Java side 必需定期的呼叫 View.invalidate() ,以進行畫面的更新。 == SurfaceView == SurfaceView 也是 View ,但有獨立的 buffer ,稱為 surface 。由於這個 buffer 不需透過 $Android$ framework 作畫面更新,能直接對應到畫面上的區塊,提供更好的效能。(其實還是間接更新到畫面上,但少了 $Android$ framework 這一層,而且可透過 $hardware$ 加速。加速的功能視平台而定。) 當你需要更好的效能時, 繼承 SurfaceView 也是一個方式。透過 SurfaceView 時, canvas 不再是透過 onDraw() 取得了。你能透過呼叫 SurfaceView.getHolder() 取得 surface holder ,而 SurfaceHolder.lockCanvas() 會傳回 SkCanvas 。 Native code 可以在這個 SkCanvas 上作畫,然後呼叫 SurfaceHolder.unlockCanvasAndPost() ,將內容更新到畫面上。 每次更新畫面前,都要透過 SurfaceHolder.lockCanvas() 取得人一個新的 SkCanvas,在這個 canvas 上作畫才行。作完後都必需呼叫 SurfaceHolder.unlockCanvasAndPost() 進行更新。如果才能正確的更新畫面。 另外, SurfaceHolder.lockCanvas() 傳回的 SkCanvas 並不會保存上次繪圖的內容。請特別注意。 SurfaceHolder.lockCanvas() 可以傳入一個參數,指定要更新的部分。透過指定特定區域,我只需重繪該部分,而不需更新整個畫面,並改善更新的效能。 == 結語 == 會$研究$這個部分,是因為現在正在 porting MadButterfly 到 $Android$ 上。在此順手記下一些心得。
最後更新時間: 2009-11-18 17:53:14 CST |
引用
查詢:
COMMENTS: