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月27日 星期二

[MySQL] 資料庫、資料表、連線編碼 @ Ubuntu 10.04

最近接觸 MySQL,一直以來都不太熟,只記得幾年前曾透過 PhpMyAdmin 建過幾張表。最近則因為開發環境是 MySQL,加上資安設定,變成必須透過某台機器才能連線,於是我就"搞鋼"地透過 SSH Tunnel 和 MySQL command line 來使用(其實可以配合GUI來使用就好),隨後碰到資料庫、資料表及連線編碼問題。


首先是連線問題,要避開 mysql-client 的編碼問題,可採用以下指令強制使用 utf8 連線:


$ mysql -h 127.0.0.1 -u root -p --default-character-set=utf8


查詢當前環境的編碼資訊:


mysql> show variables like "char%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

其中 character_set_database、character_set_server 和 character_set_system 為 MySQL Server 端的設定,使用 mysql-client 連線時,設定 --default-character-set=utf8 影響的僅有 character_set_client 、character_set_connection 和 character_set_results,若 mysql-client 不設定使用 utf8 編碼時,在 Ubuntu 10.04 環境下,很有可能預設採用 latin1:


mysql> show variables like "char%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_results | latin1 |
+--------------------------+----------------------------+

接要要留意的還有 character_set_database 這個變數資訊,在 Ubuntu 10.04 server 環境預設也是 latin1,並且這個變數會跟你使用的資料庫有關:


mysql> CREATE DATABASE `MyDBDefault`;
mysql> use `MyDBDefault`;
mysql> show variables like "char%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_database | latin1 |
+--------------------------+----------------------------+

mysql> CREATE DATABASE `MyDB` DEFAULT CHARACTER SET utf8;
mysql> use `MyDB`;
mysql> show variables like "char%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_database | utf8 |
+--------------------------+----------------------------+

當資料庫 character_set_database 為 utf-8 時,這時在其裡頭建立的資料表預設就會是 utf8 了,如果建立資料庫沒指定並且系統環境也沒設定,那大概很有機會是 latin1 編碼。


總結一下,假使開發環境大家都默認是 utf8 編碼:


使用 mysql-client 操作時,多加 --default-character-set=utf8 使用,可避開 mysql-client 端環境變因(特別是不熟的機器):


$ mysql -h 127.0.0.1 -u root -p --default-character-set=utf8


建立資料庫時,可指定 DEFAULT CHARACTER SET utf8 來避開 server 端預設環境的問題:


mysql> CREATE DATABASE `MyDBUTF8` DEFAULT CHARACTER SET utf8;


建立資料表時,可指定 DEFAULT CHARSET=utf8 來避開資料庫預設非 utf8:


mysql> CREATE TABLE `MyTable` ( `MyID` INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY ) DEFAULT CHARSET=utf8;


其他部分:


如何透過指令查詢資料表、資料欄位的編碼 how-do-i-see-what-character-set-a-database-table-column-is-in-mysql


2012年3月25日 星期日

春思

2012-03-25


春天看起來好像來了,卻又快過了?記得老家常說清明過後氣溫會越來越熱,端午過後,焰日更盛。


最近周邊親朋好友紛紛轉換跑道,對於自己的未來似乎越來越明確,只是與人閒聊交談,除了活在不確定性的規劃中,還得應付著別人的觀感,想起來也滿累的。例如跑到新創公司,不被看好的甚多。這種現象早在兩年前我也就感受過,當時周邊有個 team (被迫?!)以 spinoff 的方向進行中,可是單位內幾乎沒人認同,永遠的數落。


最近職場上又被 Push 了一把,說的很不錯:「人生總有犧牲,但要記得自己的犧牲是為了獲取什麼。」不然工作或生活上總只剩得抱怨兩字,只記得自己苦了什麼。有人存錢存了一整年,只為那幾天的出國旅遊;有人打拼二三十年,為了買個房讓妻兒溫暖。想想,這些都是挺有意義的事,何必說別人省吃儉用過的很苦呢?


今年花了不少時間在思考,看到周邊有人追求 CP 值、追求名聲、追求金錢,我想,我漸漸地知道自己要的是什麼。規劃一塊自己認為有意義的嘗試,不用為了滿足別人的眼光而回話、做事,別忘了真正為你負責的還是自己啊,單純為自己的生活之道努力吧!


2012年3月21日 星期三

[PHP] CodeIgniter (PHP Framework) 筆記


圖片來源:http://codeigniter.com/


最近因工作上的關係,重溫 PHP & MySQL 的懷抱。說真的,我上一次寫 PHP & MySQL 已經是 2005 年的事情,但我離 PHP 還沒那麼遠,至少 2009 年還有寫過。最近工作上採用 CodeIgniter Framework,算是第一個我接觸的 PHP Framework 了。我記得在 2009 年時,我還很討厭 framework 這類的東西,但工作幾年後,我發現 framework 在某個角度來說是很有用的,例如工作交接、維護與傳承。


老實說用 framework 開發速度不見得快,但好處之一就是可以讓一群人依循某種共同的開發架構(MVC)撰寫程式,如此一來要接手的人比較不會太痛苦,至少痛苦的機率小了那麼一丁點。這邊就先簡短記錄一些筆記。


效能:


參考 Google Search 的第一筆資料 - PHP Framework 的效能比較。簡單的說,在一些常見的 framework 中,CodeIgniter 效能不算差的,但與原生 PHP 來比,效能還是差了一大截。


架構:


稱得上很標準的 MVC 吧?目錄結構有 application/controllers、application/models 和 application/views ,如其名的架構,其中寫 controller 時,大部分都是用一個  class 的架構,依照該 class 來實作:


class Welcome extends CI_Controller {
        public function index()
        {
                echo "Page at /welcome";
        }
        public function test()
        {
                echo "Page at /welcome/test";
        }
        public function update($x = NULL)
        {
                echo "Page at /welcome/update";
        }


所以,你的網頁上就有 http://domain/index.php/welcome、http://domain/index.php/welcome/test、http://domain/index.php/welcome/update 和 http://domain/index.php/welcome/update/x 四種網址位置(其中最後的 x 是變數)。


接著 Models 的部分,也是繼承某個 class 的寫法:


class MyModel extends CI_Model {
        function get_all_entries()
       {
                $query = $this->db->get('mytable');
                return $query->result();
       }
}


如此一來,在 controller 裡頭就可以用:


class Welcome extends CI_Controller {
        public function index()
        {
                $this->load->model('MyModel');
                $this->MyModel->get_all_entries();
        }
}


而 views 比較像傳統 PHP 用法,不用是個 class 而是像以前寫 PHP 那樣夾雜 HTML、PHP、CSS 和 Javascript 等,而這個 view 能接收的參數需要從 controller 傳進來:


class Welcome extends CI_Controller {
        public function index()
        {
                $x = array('123','456');
                $this->load->view( 'myview' , array( 'data' => $x ) );
        }


接著在 application/views/myview.php 中,就可以透過 global 變數 $data 取得資料。


 缺點:


錯誤訊息不見得會顯示


有道是越難除錯的錯誤,通常是最蠢的問題,對於 CodeIgniter 來說,像 controller 和 models 這種 class 架構來說,不小心漏寫{}時,系統不見得會顯示錯誤,此時只會看到 500 internal server error 這種訊息,這跟直接用 PHP 會顯示錯誤訊息來說,實在很不方便。除此之外,如果使用 MySQL 時,未安裝 php5-mysql 相關套件時,也一樣空白一片。


這些真的還滿糟糕的。


Session 預設為 client side session 架構


對 PHP 來說,原先有 $_SESSION 使用,但在 CodeIgniter 必須改用 $this->session->all_userdata() 這類的存取方式,但真正棘手的是它的實作方式,預設是把 session 資料進行加密儲存在 cookie 裡頭,除了安全問題外,本身 cookie 就跟瀏覽器的實作有關,所以不見得所有資料都可以正常透過 setcookie 方式儲存起來,例如 $session['中文字'] = true 在 2.1.0 版就會發生問題!更別說 cookie 資料被破解、使用限制等等的。


另一個作法是把 session 資訊儲存在 Databases 裡頭,如此也可能導致 DB 連線頻繁而產生效能危機。


更多資訊可以參考:Session 的瓶頸與解決方法,在此就是文中第三種方式。


 以上算是目前粗淺使用的經驗。


2012年3月15日 星期四

[Linux] 使用 Tarball 更新 OpenSSL @ Ubuntu 10.04

又到了一年一度機器被掃的時刻了,這次碰到的問題是 OpenSSL 版本太舊,有資安疑慮。


然而在 Ubuntu 上安裝時,直接用 ./config 後,安裝完的路徑卻不太對。網路上有看到別人用 ln -s 的方式強制切換位置,但安裝其他軟體時,一樣容易碰到 openssl header 跟 openssl library 版本不合的問題,摸索一下才找到正確的解法。


$ sudo apt-get remove openssl libssl-dev


$ tar -xvf openssl-w.x.yz.tar.gz
$ cd openssl-w.x.yz
$ ./config --prefix=/usr
$ sudo make install


如此一來才安全地搞定了。確認 OpenSSL 版本:


$ openssl version -a


只是更新完還有一堆東西要重編 Orz


2012年3月13日 星期二

[Python] 使用 PyPNG (Python PNG encoder/decoder) 產生 PNG 圖檔

sequential color random color


因研究關係,需要產生一些圖片像素有特殊規則的圖檔,找了一下剛好有 PyPNG 可以使用。


Python PNG encoder/decoder


$ tar -xvf pypng-0.0.12.tar.gz
$ cd pypng-0.0.12
$ sudo python setup.py install


程式碼:


import png
import random


rawRow = []
rawColumn = ()
width = 1024
height = 1024
for i in range(256):
        if len(rawRow) >= height:
                break
        if i == 0:
                continue
        for j in range(256):
                if len(rawRow) >= height:
                        break
                if j == 0 :
                        continue
                for k in range(256):
                        if len(rawRow) >= height:
                                break
                        if k == 0:
                                continue
                        if len(rawColumn) < width*3:
                                rawColumn = rawColumn + (i,j,k,)
                                #rawColumn = rawColumn + (random.randint(1,255),random.randint(1,255),random.randint(1,255),)
                        else:
                                rawRow.append( rawColumn )
                                rawColumn = (i,j,k,)


f = open("test"+str(width)+"x"+str(height)+".png","wb")
w = png.Writer(width,height)
w.write( f, rawRow )
f.close()


程式碼有點醜,但堪用 XD 所以還是好好珍惜光陰吧!


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月11日 星期日

走讀台灣 系列書籍


圖片來源:Pubu電子書城 - 走讀台灣 - 嘉義市


記得過年時剛好在 iPad 上嘗試使用 Pubu 電子書閱讀器,覺得這種用「圖片」方式瀏覽傳統書籍,在網路夠快的情境下,感覺其實沒有那麼糟。


最近想說想多了解一下自己居住的城市,所以就找了「走讀台灣」系列書籍,有豐富的圖片跟文字史料,我想,跟旅遊書籍差不多了吧!只是在網路上找了很久,才發現「走讀台灣」只在一些地方可以購買:



根據一位國中老師的分享,這系列似乎是課後補充教材?難怪很難購買,應該沒多人會想去買學生上課用的書吧 XD


最後,我是在 Pubu 電子書城購買 120 元的版本(DRM-free),這個版本就是國外行之有年的 Social-DRM 版本!簡單的說就是在電子書上的每一頁嵌入購買者的相關資訊,如 Email 或帳號資訊,所以,購買後若要下載還要等一會兒,我猜是系統正在動態產生這些圖檔(嵌有一小段個人資訊)。


說真的,最後產出的電子書只是「圖檔」湊起來的,說真的還是有點小落寞啦 :P 畢竟直接買實體書就會送電子書了,這時候這種圖檔式電子書就顯得遜色不少。而令我眼睛一亮的 EPUB 格式卻都只是圖檔而已,這下可好,螢幕小的閱讀裝置肯定很痛苦,完完全全地為 EPUB 格式產生出毫無用途的 EPUB 電子書而已。


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年3月4日 星期日

PayPal 使用心得


最近試用了一下 PayPal,覺得挺方便的,可以用在國際交易方面,例如購買商品等等。用法十分簡單,只要依自己習慣的貨幣存入帳戶,接著進行交易時,可以匯出對方貨幣,在按幾步後,就完成交易了!至於手續費的部分,可能還要查閱一下,以我這次用買家(付錢)的角度來看,看起來沒有任何手續費,但是,魔鬼都藏在細節裡,其實也存在一種手續費叫作「匯率」。


例如:


在 PayPal 存了美金 20 元,想要購買港幣 100 元的東西,使用 PayPal 需要支付 13.23 美金,過程中沒有任何手續費。


那匯率是多少?


1 美金 = 7.56 港幣


但問 Google 可以查到美金港幣的匯率是 1:7.76 左右,因此等於每一塊美金少了 0.2 港幣的價值,因此變相的手續費就在這裡。


目前不太清楚金額大小會不會影響匯率,如果匯率不會因金額大而變好的話,那變相的手續費還滿可觀的 Orz 


Android 開發筆記 - 實作 Mono 照片特效 @ Ubuntu 10.04

tw.study.rs_screenout


之前接觸 Renderscript - HelloCompute 時,第一次了解 Mono 特效是怎樣做的。這次則是要來筆記純用 Java 和 NDK 作的方式。


環境概述(以 Renderscript - HelloCompute 架構為例):


private Bitmap mBitmapIn;
private Bitmap 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);
}


protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        ...
        mBitmapIn = loadBitmap(R.drawable.data); // R.drawable.data 是一張圖片
        mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(), mBitmapIn.getConfig()); // 空資料,大小跟 mBitmapIn 一樣
        ...

        // doMonoByJava(); // use Java only
        // doMonoByNDK(); // use NDK
        // createScript();     // use Renderscript
        ...
}


純 Java 版:


private void doMonoByJava() {
        final float gMonoMult[] = {0.299f, 0.587f, 0.114f};
        int imageHeight = mBitmapIn.getHeight();
        int imageWidth = mBitmapIn.getWidth();

        /* only assign, 速度慢
        for( int i=0 ;i<imageWidth; ++i )
                for( int j=0;j<imageHeight; ++j)
                        mBitmapOut.setPixel(i, j, mBitmapIn.getPixel(i, j));
        */

        /* only assign, 速度快
        int[] pixels = new int[imageWidth*imageHeight];
        mBitmapIn.getPixels(pixels,0,imageWidth,0,0,imageWidth,imageHeight);
        mBitmapOut.setPixels(pixels, 0, imageWidth, 0, 0, imageWidth, imageHeight);
        */


        int[] pixels = new int[imageWidth*imageHeight];
        mBitmapIn.getPixels(pixels,0,imageWidth,0,0,imageWidth,imageHeight);
        for( int i=0 ; i<imageHeight*imageWidth ; ++i ) {
                int R = (pixels[i] >> 16) & 0xff; 
                int G = (pixels[i] >> 8) & 0xff;
                int B = pixels[i] & 0xff;

                int m = (int)((float)R*gMonoMult[0] + (float)G*gMonoMult[1] + (float)B*gMonoMult[2] );
                R = m;
                G = m;
                B = m;


                pixels[i] = 0xff000000 | (R << 16) | (G << 8) | B;
        }
        mBitmapOut.setPixels(pixels, 0, imageWidth, 0, 0, imageWidth, imageHeight);
}


使用 NDK 版:


HelloCompute.java:


package com.example.android.rs.hellocompute;

public class HelloCompute extends Activity {
        ...
        static {
                System.loadLibrary("my-jni");
        }
        public native void doByNDK( int [] pixels, int size);

        private void doMonoByNDK() {
                int imageHeight = mBitmapIn.getHeight();
                int imageWidth = mBitmapIn.getWidth();
                int size = imageWidth*imageHeight;
                int[] pixels = new int[size];
                mBitmapIn.getPixels(pixels,0,imageWidth,0,0,imageWidth,imageHeight);
                doByNDK(pixels,size);
                mBitmapOut.setPixels(pixels, 0, imageWidth, 0, 0, imageWidth, imageHeight);
        }
        ...
}


my-jni.c:


#include <jni.h>
void Java_com_example_android_rs_hellocompute_HelloCompute_doByNDK( JNIEnv* env, jobject thiz, jintArray pixels, jint size) {
        float gMonoMult[] = {0.299f, 0.587f, 0.114f};
        int i, R, G, B, m;


        jboolean isCopy = JNI_FALSE;
        jint *jpixels = (*env)->GetIntArrayElements(env, pixels, &isCopy);


        for(i=0 ; i<size ; ++i ) {
                R = (jpixels[i] >> 16) & 0xff; //bitwise shifting
                G = (jpixels[i] >> 8) & 0xff;
                B = jpixels[i] & 0xff;
                m = (int)((float)R*gMonoMult[0] + (float)G*gMonoMult[1] + (float)B*gMonoMult[2] );
                R = m;
                G = m;
                B = m;
                jpixels[i] = 0xff000000 | (R << 16) | (G << 8) | B;
        }
}


若要計算效率,可以再用這段程式碼:


Long timeCost = System.currentTimeMillis();

// do something ...

timeCost = System.currentTimeMillis() - timeCost;
System.out.println("Cost:"+timeCost);


2012年3月3日 星期六

[Android] 變更 CPU 狀態、使之 offline、降頻、超頻和穩頻 @ PandaBoard

之前被要求測試 Android 4.0 的 Renderscript 是否可以做平行處理,但是隨著工作量的增加,一直把這件事擺在後頭,今天,終於測試完了!最早之前,我想到的是修改 HelloCompute 範例中的 mono.rs 檔案,裡頭的 root 函式是一個工作單位,如果一台裝置是多 CPU 且系統支援平行處理的話,理論上一次會有多個工作單位在進行。因此想說擺一個 static 變數在 root 函式中,每次把數值加一並印出,依此觀察系統運作時,印出的數字是否不連續,如果不連續或有相同時,想要把這當作有平行的依據。今天則想到一個更簡單的妙招,就是在 root 函式中,用一個 loop 讓它不結束,如此一來,在雙核心的 CPU 下,就會看到兩次訊息,單核心就只會看到一個!理由很簡單,因為工作沒做完,就不會分配新的工作。


這在 Pandaboard 上測試很成功,我看到了兩次 root 函式印出的訊息,然而,我更想要驗證自己設計的方式是不是對的,那就必須找只有一個核心的裝置,甚至四核心的裝置測試,如果在一個 CPU 的裝置只看到一次,在四核心的裝置可以看到四次,那代表驗證成功了。結果把構想跟同事分享後,同事教了我一個密技!那就是手動去更改 CPU 的狀態!對 Pandaboard 來說,就是強制把一個 CPU 關掉(offline),慶幸的,驗證對了!


這個用法其實最常看到別人修改機子的 CPU 頻率,例如一台裝置他本身的 CPU 可以跑到 500 MHz,但為了省電、穩定、商業手段等理由,系統廠會降頻跑,例如用 300 MHz 運行等等,這些則可以透過系統設定檔進行操作的,也因此有人刷機改機追求好的效能。


此例只在雙核心的板子上,手動把一顆 CPU 關掉:


$ adb devices
List of devices attached
################ device
$ adb shell ls -R /sys | grep -i cpu
...
/sys/devices/system/cpu:
cpu0
cpu1
cpufreq
cpuidle
...
$ adb shell
root@android:/ # cd /sys/devices/system/cpu
root@android:/sys/devices/system/cpu # ls
cpu0
cpu1
cpufreq
cpuidle
kernel_max
offline
online
possible
present
root@android:/sys/devices/system/cpu # cat online
0-1
root@android:/sys/devices/system/cpu # cat cpu*/online
1
1
root@android:/sys/devices/system/cpu # echo "0" > cpu1/online
root@android:/sys/devices/system/cpu # cat cpu*/online
1
0
root@android:/sys/devices/system/cpu # cat online
0


在 Pandaboard 上,因為是雙核心架構,所以會看到 CPU0 和 CPU1 的資訊,接著分別印出 /sys/devices/system/cpu/cpu0/online 和 /sys/devices/system/cpu/cpu1/online 資訊,標記 1 代表都是運作中,同時印出 /sys/devices/system/cpu/online 會是 0-1 的資訊。接著使用 echo "0" > /sys/devices/system/cpu/cpu1/online ,代表令 CPU1 下線不運作,反之 echo "1" > /sys/devices/system/cpu/cpu1/online 則是讓他醒來運作。透過這個簡單的方式,就能讓控制裝置多核心的狀態啦!真是超方便的,但別忘了執行 adb shell 時,必須有 root 權限才行。


至於 CPU 調頻、穩頻,還沒親手測過,但原理很像,就順便筆記一下:


以 /sys/devices/system/cpu/cpu0 來說,裡頭 cpufreq 目錄,裡面又有 cpuinfo_cur_freq、cpuinfo_max_freq 和 cpuinfo_min_freq 等資訊,不難猜到吧?只要把 scaling_max_freq 跟 scaling_min_freq 設成一樣就是穩頻,把 scaling_max_freq 調高則是超頻吧,有空再來試試!穩頻應該很好用,可以在一樣的 CPU 狀態下收集測試數據。