顯示具有 Android RenderScript 標籤的文章。 顯示所有文章
顯示具有 Android RenderScript 標籤的文章。 顯示所有文章

2012年5月2日 星期三

Android 開發教學筆記 - 研究 Renderscript 之 Fountain 與 FountainFbo 的差異

fountainFbo


關於 Fountain 和 FountainFbo 的介紹,可以看 Renderscript>Graphics 官網得到更詳細的介紹,在此僅簡易筆記。


首先快速帶一下 Fountain 的特效,就是在螢幕上點擊、久按、拖拉著任何一點,這時螢幕會隨著點擊事件產生一個點畫在螢幕上,接著亂數決定這個點的顏色以及該如何移動並會隨著時間往下掉,這樣的效果就接近煙火。其中在 FountainRS 有定義最多顯示 50000 個點(PART_COUNT變數),所以拖曳時的效果又跟煙火不太一樣。而 FountainFbo 則是多顯示一塊小區域,重複繪出螢幕上的特效,並使用 Framebuffer Object。


先用 vimdiff 看一下 FountainRS.java 和 FountainFboRS.java 的差異:


$ cd ~/workspace
$ vimdiff Fountain/src/com/example/android/rs/fountain/FountainRS.java FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java


java_FountainRS_FountainFboRS


扣除 package name 這類東西外,看得出來主要差別在 public void init(RenderScriptGL rs, Resources res) 中所使用的流程。


先看一下共同項目:


ProgramFragmentFixedFunction,其定義 http://developer.android.com/reference/android/renderscript/ProgramFragmentFixedFunction.html


ProgramFragmentFixedFunction is a helper class that provides a way to make a simple fragment shader without writing any GLSL code. This class allows for display of constant color, interpolated color from the vertex shader, or combinations of the both blended with results of up to two texture lookups.


Mesh,其定義 http://developer.android.com/reference/android/renderscript/Mesh.html


This class is a container for geometric data displayed with Renderscript. Internally, a mesh is a collection of allocations that represent vertex data (positions, normals, texture coordinates) and index data such as triangles and lines.


Vertex data could either be interleaved within one allocation that is provided separately, as multiple allocation objects, or done as a combination of both. When a vertex channel name matches an input in the vertex program, Renderscript automatically connects the two together.


Parts of the mesh can be rendered with either explicit index sets or primitive types.


ScriptField_Point,其定義在 renderscript 裡頭(C99語法)


typedef struct __attribute__((packed, aligned(4))) Point {
        float2 delta;
        float2 position;
        uchar4 color;
} Point_t;


ScriptField_Point 是自訂結構,定義於在 *.rs,編譯後自動產生的,用途是紀錄每一個點的資訊;Mesh為一種資料結構,用來收集一批資料,最重要的是 Renderscript 有 rsgDrawMesh API,可以一次把 Mesh 收集的資料繪出,因此程式架構上,並非用 loop 繪每個 Point,有點像寫 C 時,用 memset 取代用 loop 對 array 的初始化,更多細節請參考 Renderscript>Graphics>Drawing with a mesh;ProgramFragmentFixedFunction 對我而言是比較生疏的部份,這跟 OpenGL Shading Language (GLSL) 有關,其中 Google Translate 對 Shader 翻譯為"著色",我把 GLSL 當作繪圖相關的基本需求,另外,從描述的意思是使用 ProgramFragmentFixedFunction 可以省去從頭到尾定義 GLSL 出來,何謂 GLSL 呢?可以參考 Renderscript>Sample>Balls>BallsRS.java,裡頭有用 ProgramVertex 引入 GLSL 語法,更多細節請參考 Renderscript>Graphics>Programs


不一樣的地方:


對 FountainRS.java 來說,使用 ProgramFragmentFixedFunction 建立 GLSL 環境後,傳給 RenderScriptGL rs 環境使用,但在 FountainFboRS.java 中,多定義兩個 ProgramFragment 變數,mProgramFragment 和 mTextureProgramFragment,並且在 Renederscript 裡頭接應,接用 vimdiff 看一下 fountain.rs 和 fountainfbo.rs 的差異:


$ cd ~/workspace
$ vimdiff Fountain/src/com/example/android/rs/fountain/fountain.rs FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs


rs_fountain_fountainfbo


可看出 fountain.rs 裡的東西基本上都出現在 fountainfbo.rs 了,所以了解 fountrain.rs 在 void root 做事後,可以細看 fountainfbo.rs 的變化,此部份在官網 Renderscript > Graphics > Framebuffer Object 有很多細部介紹,在此就不多提囉。


總結一下,在 fountainfbo 中採用 Framebuffer Object 的繪圖技巧,可以讓開發者先在某一處 buffer 繪圖(offscreen),繪完後在貼到螢幕上(onscreen)。


2012年3月31日 星期六

Android 開發教學筆記 - 使用 RenderScript 之 rsSendToClient


From: Compute Renderscript overview


由於 Renderscript 為 Async 架構,因此當 RS 做完事後,希望他送個訊息到 Java 那層時,就會用到 rsSendToClient/rsSendToClientBlocking 等函數來使用,只是翻了文件後,僅有一句話介紹、沒有文件,更別說教學 Orz 後來發現網路上也有人在 stackoverflow 詢問,但一樣沒結果。幸運地,我找到了 Android 系統程式碼!


程式碼:


frameworks/ex/carousel
frameworks/ex/carousel/java/com/android/ex/carousel/carousel.rs
frameworks/ex/carousel/java/com/android/ex/carousel/CarouselRS.java


用法:


HelloCompute.java:


public class HelloCompute extends Activity {

       // ...
       private RSMessageHandler mRsMessage = new RSMessageHandler() {
              public void run() {
                     switch(mID) {
                            default:
                                   Log.e("mID","value:"+mID+",data[0]:"+mData[0]+",data[1]:"+mData[1]+", data[2]:"+mData[2]);
                     }
              }
       };

       // ...
       public void createScript() {
              mRS = RenderScript.create(this);
              mRS.setMessageHandler(mRsMessage);
              mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);
              mScript.invoke_fireInRS();
       }
}


mono.rs:


#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.hellocompute)


#include "rs_graphics.rsh"


void fireInRS() {
        int data[3];
        data[0] = 1;
        data[1] = 2;
        data[2] = 3;
        rsSendToClient(1, data, sizeof(data));
}


2012年3月12日 星期一

Android 開發教學筆記 - 研究 Renderscript 以 Gravity 為例


From: http://code.google.com/p/renderscript-examples/wiki/Gravity


這是一個重力特效的範例。看過 Renderscript HelloWorld 的架構後,很快就能了解 Gravity,他們倆是一樣的架構,只是畫圖的部份不一樣。


這邊有四個檔案 Gravity.java、GravityRS.java、GravityView.java 和 gravity.rs,以 GravityRS.java 和 gravity.rs 最為重要,因此挑這兩個檔案研究一下:


gravity.rs:


(gTouchX, gTouchY) 座標資訊


用來決定重力中心位置,這會從螢幕上的 TouchEvent 後,慢慢傳遞到這兩個變數,所以在畫面上任一點點一下,則改變重力中心位置


Point 資料結構(delta、座標和顏色)


用來紀錄每一個畫在螢幕上的點資訊,包括座標、顏色,而 delta 則是用來計算與重力中心距離相關的數值,在畫面上則是用來決定靠近重力中心的速率,也就是座標變化量


void initParticles()


用來初始化 Point 位置,首先先得知有多少 Point,接著得知 Width 跟 Height 後,把所有 Point 座標用亂數建立出來,並設定顏色,而靠近重力中心的座標變化量就先定為 0


int root()


計算各 Point 與重力中心點 (gTouchX, gTouchY) 的位置,並決定該 Point 移動到重力中心點的座標變化量(越接近移動越快)。最後,則是透過 rsgDrawMesh(partMesh); 畫出各個 Point,並且 return 1 代表 1ms 更新畫面一次,也代表座標會重算一次


GravityRS.java:


void init(RenderScriptGL rs, Resources res, int width, int height) 裡頭包括決定 Point 個數、用 Mesh 結構把 Point 集中起來(方便畫圖),剩下的則是相關的初始化動作。


Android 開發教學筆記 - 研究 Renderscript 以 HelloWorld 為例

ImportSample


此例是一個很簡單的 Renderscript 的 HelloWorld,程式跑起來後,螢幕上只會顯示一個 "Hello World!" 字串,並且隨著你點擊螢幕的位置,該字串就會在那個位置上顯示。


使用方式:


[File]->[Project]->[Android]->[Android Sample Project]->[Android 4.0]->[RenderScript > HelloWorld]


AddSampleProject


接著簡述架構,共有 4 個檔案需留意:


HelloWorld.java


HelloWorld.java 就像一般 Android app 中的角色,為一個 Activity,裡頭新增一個 HelloWorldView 後,再把它加到自身的 View 身上。除此之外,則是對 Android app 的程式流程資訊(onResume、onPause)傳給 HelloWorldView 處理。


HelloWorldView.java


HelloWorldView.java 是一個繼承於 RSSurfaceView 的物件,用來負責初始化 Renderscript 所需的環境資源,以及接收 View 上頭的事件資訊進行處理,例如 onTouchEvent 等,而此例則是接收 TouchEvent 的位置後,把資訊傳遞給 Renderscript 以決定畫字串的位置。


初始化 Renderscript 運行的環境:


RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
RenderScriptGL mRS = createRenderScriptGL(sc);
// Create an instance of the script that does the rendering
HelloWorldRS mRender = new HelloWorldRS();
HelloWorldRS mRender.init(mRS, getResources());


更新字串位置:


if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        mRender.onActionDown((int)ev.getX(), (int)ev.getY());
        return true;
}


HelloWorldRS.java


void init(RenderScriptGL rs, Resources res)


負責將 HelloWorldView 初始化 Renderscript 所需的執行環境資源接收起來,用全域變數紀錄。接著執行自身函數 void initRS()。


void initRS()


初始化 mScript 變數,將 Java 與 Renderscript 接起來。其中 mScript = ScriptC_helloworld 代表要使用 helloworld.rs 程式。初始化完後,再 bind 到 Renderscript 執行環境。


void onActionDown(int x, int y)


把座標資訊傳遞給 mScript(helloworld.rs) 使用。


HelloWorld.rs


HelloWorld.rs 的工作主要就只是依據指定的 (gTouchX, gTouchY) 座標把 "Hello World!" 字串印出。負責印出的函式是 int root(void),回傳的整數代表更新螢幕的時間(ms),如回傳 20ms 代表每 20ms 作一次 root 內容函式。


From: http://developer.android.com/guide/topics/renderscript/graphics.html
A root() function. This is the main worker function for your Renderscript and calls Renderscript graphics functions to render scenes. This function is called every time a frame refresh occurs, which is specified as its return value. A 0 (zero) specified for the return value says to only render the frame when a property of the scene that you are rendering changes. A non-zero positive integer specifies the refresh rate of the frame in milliseconds.


colorCode


因此,想要體會 20ms 執行 root 內容函式的話,那更新一下 helloworld.rs 片段程式碼,將 rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); 更新為 rsgFontColor(rsRand(1.0f), rsRand(1.0f), rsRand(1.0f), 1.0f); 如此一來,就可以看到字串顏色不斷得變化囉。(該程式碼只是字型顏色改成亂數產生而已)


最後一提,不曉得這是不是一個很傷硬體的範例?程式執行沒多久後,我那塊 PandaBoard 就會掛掉 :P 或許 root 裡頭改成回傳 200(ms) 也適合,因為 0.2 秒的反應速度對人來說還 ok 囉。


Android 開發教學筆記 - 學習 Renderscript 的足跡


From: Graphics Renderscript overview


接觸 Renderscript 一陣子了,盡管官方文件顯少,但最佳的學習方式就是大量的程式碼閱讀,久了就會熟習為啥有 A 還是 B 的架構。據說 Renderscript 一開始是要設計給圖形運算的,以高效能、跨平台為出發點,不知不覺就設計出可以平行運算的架構,所以學習 Renderscript 別忘了繪圖運算跟平行處理這兩個方向。


關於 Renderscript 相關程式碼:



我本身一開始是先接觸 HelloCompute 的,看懂之後又對 Carousel 的效果著迷,在沒有圖學背景,悄悄地啃食完了,深深覺得數學真的很奧妙。之後閒暇之餘,又看了 Fountain 、HelloWorld 、 Gravity 和 Balls 等範例,漸漸地就能夠把 Renderscript 雛形給建立好。


回憶起來,我覺得比較好的學習曲線:



  • 平行運算


    • HelloCompute> Balls


  • 繪圖運算


    • HelloWorld > Gravity > Fountain > Carousel 



從 HelloWorld 的架構下,就能夠搞懂怎樣初始化一些繪圖的架構,例如 int root() 回傳的數值可代表畫面更新的頻率等,而 HelloCompute 則是專門做平行運算的架構。之所以把 Balls 擺在平行運算是因為裡頭有一個在 Renderscript 中呼叫 rsForEach 啦,而 HelloCompute 是在 Java 端呼叫 mScript.forEach_root ,更簡單的範例請參考 Renderscript Part 2 吧,算是把 HelloCompute 從 mScript.forEach_root 快速改寫成 rsForEach 的方式。


除此之外,最近也有一篇 R. Jason Sams 寫的一篇 Levels in Renderscript ,也是不錯的效能測試的文章。


2012年3月8日 星期四

Android 開發教學筆記 - Renderscript Parallel Computing 之 Define a structure for your data

雖然網路上有許多 Renderscript 處理繪圖運算的範例,但它不只可以作繪圖加速還可以作平行運算,在 PandaBoard ES Rev B 板子上,發現雙核心 CPU 的架構下,似乎是沒問題的。然而,網路上場看得範例都是處理圖檔,讀檔的資料結構很簡單,就是最基本的 Bitmap 而已,於是花了不少功夫推敲,才找到如何定義自己的資料結構進行平行運算!


作法很簡單,先把你想要的資料結構定義在 Renderscript (myscript.rs) 檔案內,如 C 語言的 structure:


#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.MyParallelCompute)


#include "rs_graphics.rsh"


struct MyDataIn {
        int a;
        int b;
};


//struct MyDataOut {
//         int sum;
//};
//void root(const struct MyDataIn *in, struct MyDataOut* out) {


void root(const struct MyDataIn *in, int* out) {
        //out->sum = (int)a+b;
        *out = in->a + in->b;
        rsDebug("myscript a=", (int) in->a);
        rsDebug("myscript b=", (int) in->b);
}


如此一來,編譯後自動產生 ScriptField_MyDataIn.java (gen/com.example.android.rs.MyParallelCompute裡),之後在 Java 端撰寫程式時,使用這個 class 來包裝你的資料:


private void createScript() {
        mRS = RenderScript.create(this);

        int myDataCount = 5;


        ScriptField_MyDataIn in = new ScriptField_MyDataIn(mRS,myDataCount);
        //ScriptField_MyDataOut out = new ScriptField_MyDataOut(mRS,myDataCount,myDataCount);


        // initial data
        for( int i=0 ; i<myDataCount ; ++i) {
                in.set_a(i, i, false);
                in.set_b(i, i*2, false);
        }
        in.copyAll();


        //Allocation dataOut = Allocation.createTyped(mRS, out.getType());
        Allocation dataOut = Allocation.createSized(mRS, Element.I32(mRS), myDataCount);


        // call renderscript
        mScript = new ScriptC_myscript(mRS, getResources(), R.raw.myscript);
        mScript.forEach_root(in.getAllocation(),dataOut);


        //byte []x = new byte[ ScriptField_MyDataOut.Item.sizeof * myDataCount ];
        //dataOut.copyTo(x);


        int[] x = new int[myDataCount];
        dataOut.copyTo(x);


        // result data
        for( int i=0 ; i<myDataCount ; ++i) {
                //Log.e("MyParalle","(a,b,sum)=("+in.get_a(i)+"+"+in.get_b(i)+"="+out.get_sum(i)+")");
                Log.e("MyParalle","(a,b,sum)=("+in.get_a(i)+"+"+in.get_b(i)+"="+x[i]+")");
        }
}


此例很簡單,使用平行運算把輸入的資料(struct MyDataIn)進行相加後輸出(int)。紅色是把資料轉成 RS 的輸入,綠色則是用來儲存運算結果,而藍色則是把綠色取得的資料,轉成在 Java 常用的結構。


幾個小筆記:



  • 用 forEach_root 進行平行運算,輸入的資料個數要跟輸出個數一樣才能使用,在此資料個數就是 myDataCount,而從 Java 傳遞給 forEach_root 的參數輸入跟輸出必須是 Allocation 型態

  • 想要自訂結構,請在 *.rs 先定義(如 struct MyDataIn),接著編譯後,則可以再 Java 中使用(如 ScriptField_MyDataIn)

  • 在 Java 中使用 ScriptField_* 進行資料初始化後,想要把它變成有效的 Allocation 時,記得先做 copyAll(),不然則是每次設值時,最後一個 copyNow 參數設定成 true,詳情起看 ScriptField_*.java 查閱實作

  • 運算後的資料也是 Allocation,必須把它轉成自己的資料結構,由於 Allocation 目前只支援輸出(copyTo)成 int [], short [], float [], byte[] 和 Bitmap 等,所以建議先以這些結構來設計,不然輸出還要花心力去做轉換,希望之後的架構可以支援直接輸出成自訂結構

  • Allocation 初始化有不少 Element.XXX 函式可以使用,例如 rs 用 void root( uchar4 *in ) 的話,那就用 Element.U8_4(mRS) 來對應


2012年2月23日 星期四

Android 開發教學筆記 - 研究 Renderscript 以 Carousel 為例 @ PandaBoard


這個 Carousel 例子是一個類似翻頁動作的特效,仔細看一下,是不是覺得自己在一個圓心中間,被很多照片包圍住呢?這個例子是 10 張照片,等於你走進了一個 10 面牆的房間,隨著你觸動移動事件,等於房間每面牆近似以你為中心旋轉。猜測原理後,就可以去驗證實做了。


six
此圖為正六邊形。左下角用來推邊長 len,右上角用來推各點 P1, P2, ... 的座標,θ就是兩頂點的夾角,此外,定義右下角是第一象限,左下是第二。


首先,讓我驗證這是個正 n 邊形的效果,主要是看到這段程式碼:


@ initBitmaps(): // Calculate the length of the polygon
len = RADIUS * 2 * sin(M_PI/NUM_ITEMS);


這是計算正 n 邊形的邊長公式,其中 n = NUM_ITEMS,此例就是 10 張照片


接著,開始計算正 n 邊形各頂點的三維座標資訊:


此例是把 y 軸當作 0 來看代 (x,y,z) = ( sinθ * RADIUS, 0, -cosθ * RADIUS)


@ initBitmaps(): // Calculate the vertices of rectangles
vertices[i*3] = sin(angle * M_PI / 180) * RADIUS;
vertices[i*3 + 1] = 0;
vertices[i*3 + 2] = -cos(angle * M_PI / 180) * RADIUS;


畫出正 n 邊形:


@ displayCarousel(): // Draw the rectangles
for (int i = 0; i < 10; i++) {
        ...
        rsgDrawQuadTexCoords(...);
        ...
}


rsgDrawQuadTexCoords 則是輸入長方形四個座標點,但是每一個頂點傳入的參數還有多了兩個,該數值通常定義為 (u, v),其中 u = 0 代表左邊,反之右邊,而 v =0 代表上面,反之下面,因此此例依序透過 (u, v) = (0, 1), (0, 0), (1, 0), (1, 1)來描述頂點位置。其中 (0, 1) 和 (0, 0) 使用的頂點座標位置分別是 ( r*sinθ , -(len/2), -r*cosθ ) 和 ( r*sinθ , len/2, -r*cosθ ),還記得最初定義得座標系嗎? y 軸是縱軸,此例第一張照片左上角座標為 ( r*sinθ , len/2, -r*cosθ ),左下角為 ( r*sinθ , -(len/2), -r*cosθ ),至於第一張圖的右上角跟右下角得座標,那就是用正 n 邊形第二個頂點來計算,以此規則算出 10 張照片的座標位置


至於觸動時的旋轉效果,則是透過 rotate 的功能實作的:


@ displayCarousel():
// Load vertex matrix as model matrix
rs_matrix4x4 matrix;
rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -400.0f); // camera position
rsMatrixRotate(&matrix, rot, 0.0f, 1.0f, 0.0f); // camera rotation
rsgProgramVertexLoadModelMatrix(&matrix);


先把 camera(觀看照片的位置?) 位置移到 (0,0,-400) 位置,由於此正 n 邊形半徑為 828,因此距離照片 428 各單位。接著讓 camera 的位置對 (0,1,0) 向量旋轉,就達成這個效果囉


其他資訊:


rsMatrixLoadPerspective 對應 OpenGL:gluPerspective,以 void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); 來說,分別為視角、寬高(長)比、前景和背景,查 wiki 可知人的視角大約 120 度,魚眼是 180 度,在此例是 30 度視角為例,接著寬高(長)比沒啥問題,而 near 和 far 的參數,若有玩單眼相機的話,應該不會太陌生,在網路上找資料的結果,通常前景是用一個大於 0 的數值,如 0.1f 等。


rsMatrixLoadTranslate 對應 OpenGL: glTranslate,以 void glTranslated(GLdouble x, GLdouble y, GLdouble z); 來說,就是切換到 (x,y,z) 座標


rsMatrixRotate 對應 OpenGL: glRotate,以 void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); 來說,代表以 (x,y,z) 向量為軸心,旋轉 angle 弧度。此外,(0,1,0) 和 (0,100,0) 都是一樣的向量,網路資料是說用 (0,1,0) 所造成的運算量較低。


而 rsMatrixLoadTranslate 和 rsMatrixRotate 定義可在 Renderscript Reference 查詢。


2012年2月15日 星期三

Android 開發教學筆記 - 初探 Renderscript 以 HelloCompute 為例 @ PandaBoard

tw.study.rs_screenout


最近把玩 Renderscript(RS),這東西在 2011 年初被提了出來,但最近才仔細看文件,也隨著 Android 4 可以看到完整的系統實現原始碼等,目前 RS 可用在 Android 3.x 和 Android 4.x 系統上。RS 的本意著重在三個層面,依序是 "Portability" 、"Performance" 和 "Usability"。我把他想成建立一個框架,讓你寫程式可以不用擔心硬體狀況、又不用擔心 Java VM 拖速度並且開發上可以簡單快速,當然,連帶的缺點就是必須學習這個框架的用法等。除此之外,RS 用的語法是 C 語言(C99 standard),流程看似與 JNI/NDK 很接近,但最特別的是整個架構的設計,並透過 llvm 技術,讓你的程式不只跑在 CPU 上頭,還可以跑在 GPU 或 DSP 上,目標就是提供跨硬體設備的機制,包含不同架構的 CPU 等,這就不見得單純用 JNI/NDK 可以作到的事。最後,關於 RS 的使用時機?可以用在大量運算(平行運算?)上,大部分的是用2D/3D影像處理當作例子,對於遊戲開發應該有不少幫助。


此練習仿造 Android 4.0 之 HelloCompute 範例,此範例是將一張 JPEG 圖片進行灰階影像特效。由於程式碼精簡,所以就拿來當作第一個 RS 的練習,可熟悉 RS 流程。


主要流程:


建立 Android Project -> 建立 RenderScript -> 設定使用 RenderScript 的時機。


建立 Android Project:


Project Name: StudyRSCompute
Build Target: Android 4.0
Package Name: tw.study.rs 


tw.study.rs


建立 RenderScript (MyRSCompute.rs):


src/tw.sutdy.rs -> New -> File -> MyRSCompute.rs


#pragma version(1)
#pragma rs java_package_name(tw.study.rs)


const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};


void root(const uchar4 *v_in, uchar4 *v_out) {
    float4 f4 = rsUnpackColor8888(*v_in);
    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);
}


按下 build 後,可以在 res->raw 看到 myrscompute.bc,另外,在 gen->tw.study.rs 也能看到 ScriptC_MyRSCompute.java 和 MyRSCompute.d 的產生,並且在 R.java 中可以看到 raw 裡頭有 myrscompute 的定義。


建立測試圖檔:


接著找一張圖,此例用 http://zh.wikipedia.org/wiki/Android 裡的圖片 http://upload.wikimedia.org/wikipedia/commons/thumb/a/ad/Galaxy_Nexus_smartphone.jpg/371px-Galaxy_Nexus_smartphone.jpg,更名為 android.jpg


在 res 中,建立 drawable 目錄,並將 android.png 擺入且 build 後,在 gen->tw.study.rs->R.java 中可以看到圖檔的定義


建立圖檔 layout (res->layout->main.xml):


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <ImageView
        android:id="@+id/displayin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


    <ImageView
        android:id="@+id/displayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


</LinearLayout>


撰寫呼叫 RS 的時機 (StudyRSComputeActivity.java):


package tw.study.rs;


import android.app.Activity;
import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
import android.widget.ImageView;


public class StudyRSComputeActivity extends Activity {
    private Bitmap mBitmapIn;
    private Bitmap mBitmapOut;


    private RenderScript mRS;
    private Allocation mInAllocation;
    private Allocation mOutAllocation;
    private ScriptC_MyRSCompute mScript;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mBitmapIn = loadBitmap(R.drawable.android);
        mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(), mBitmapIn.getConfig());


        ImageView in = (ImageView) findViewById(R.id.displayin);
        in.setImageBitmap(mBitmapIn);


        ImageView out = (ImageView) findViewById(R.id.displayout);
        out.setImageBitmap(mBitmapOut);


        createScript();
    }

    private void createScript() {
        mRS = RenderScript.create(this);


        mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
        Allocation.MipmapControl.MIPMAP_NONE,
        Allocation.USAGE_SCRIPT);
        mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());


        mScript = new ScriptC_MyRSCompute(mRS, getResources(), R.raw.myrscompute);


        mScript.forEach_root(mInAllocation, mOutAllocation);
        mOutAllocation.copyTo(mBitmapOut);
    }

    private Bitmap loadBitmap(int resource) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return BitmapFactory.decodeResource(getResources(), resource, options);
    }
}


 整個 Package PackageExplore:


PackageExplore_tw_study_rs


猜想的架構:


在 MyRSCompute.rs 中,只有一個 root 函數,該函數接收單位是一個像素資訊(4個浮點數),並且把收到的像素 v_in 作灰階,經向量 dot 處理後,將結果存在 v_out;在 StudyRSComputeActivity 中,在 onCreate 時,把預備好的圖檔讀進來,並轉成 Bitmap 結構,隨即透過 createScript 呼叫 RS,在 createScript 中,先建立 Renderscript 執行環境,包括用 Allocation 進行 Java 層變數的初始化,這些變數之後才可以傳給 RS 用,接著在 Java 層用 forEach_root 的方式呼叫 RS 進行處理(平行處理?),最後再把處理完的資料在存回 Bitmap 變數,在 ImageView 顯示出來。


其他心得:


學會處理圖片灰階特效,也才發現圖片特效有平行處理的可行性架構。RS 有提供很多數學函數,請參考 RS Runtime API Reference