更新:比較新的版本的Android NDK都自帶基本的GNU工具鏈,因此不用安裝龐大的cygwin或者MSYS了,直接解壓NDK而後在Eclipse裏配置編譯器就能夠了。html
————————————————————————————————————java
Android NDK須要使用Linux下的make、gdb等開發工具,所以要安裝一個模擬的Linux環境。這裏選擇最經常使用的cygwin。MSYS應該也能夠,不過沒有親自試過,留給有求證精神並鄙視cygwin的龐大和緩慢的Coder去驗證!android
cygwin有本身的安裝器,至關於Linux發行版下的包管理器,用來管理軟件。打開後選擇從網絡安裝,選擇一個合適的鏡像,偷懶的話直接把Devel分類下的軟件所有選上,點擊下一步後這個包管理器會本身解決各類亂七八糟的依賴關係,給你下載安裝幾個G的軟件包。若是有潔癖或者網絡不給力,能夠本身慢慢選擇要裝哪些軟件,這樣裝的東西會少不少。順便吐槽一句,cygwin的這個圖形化包管理器體驗真是渣,快裝完的時候有個選項沒看清,手賤點了一下上一步,我再次點擊下一步的時候它就給我卸載又從新安裝配置了一遍,因而又多花了十幾分鍾。。。有沒有省事點的像aptitude這樣牛氣哄哄的工具?bash
裝完cygwin後還要簡單地配置一下。首先請下載最新版本的Android NDK並解壓,解壓後的路徑名不能包含空格!這是NDK本身的硬性規定,否則就等着出錯吧……這些準備好後打開cygwin terminal。這貨雖然不如Linux下各類功能強大的Terminal,但比Windows的cmd順眼多了。首先看看你的home目錄準備好了沒有,方法很簡單,輸入echo ~便可。若是輸出的目錄相似於/home/xxx,那就沒問題了;若是輸出是Windows用戶的home目錄,那就打開Windows環境變量設置,刪掉home變量(我覺得會對Windows系統有影響的,刪掉後發現沒有可見的變化……),而後在cygwin根目錄的home目錄下創建一個與你當前登陸Windows的用戶名同名的文件夾。重啓cygwin terminal後,再次輸入echo ~檢查,能夠看到cygwin已經將home目錄重定向到你新建的文件夾。網絡
接下來把/etc/defaults/etc/skel目錄下的.bash_profile、.bashrc、.inputrc這幾個文件複製到本身的home目錄下,而後在.bash_profile文件結尾添加相似的兩句:函數
NDK=/cygdrive/d/android-ndk/android-ndk-r9/ ##這裏是ndk的目錄,我是註釋,你看不見我!
export NDK工具
這幾句是給cygwin設置環境變量。/cygdrive/d/對應Windows的D盤,其餘盤依此類推。熟悉Linux環境的朋友用terminal能夠輕鬆完成這幾步,不熟悉也不要緊,默默地複製粘貼文件而後用記事本打開文件編輯吧,哈哈~開發工具
作完上面的幾步,NDK環境基本搞定,接下來能夠簡單測試一下。在terminal中執行cd $NDK/samples/hello-jni/,而後再執行../../ndk-build,若是看到這樣的測試
那麼恭喜你,你的環境搭建完成~ui
環境搭建好後我還惆悵了一陣子,Google就給了hello-jni這麼一個破例子,NDK到底怎麼使?繼續查了不少資料終於有了眉目。在hello-jni工程的jni目錄下,有Android.mk這樣一個文件,這其實就是一個Makefile文件。這個文件可謂麻雀雖小,五臟俱全,該有的都有了,能夠成爲很好的範本和copy對象。接下來在Eclipse下打開咱們本身的Android工程,已有的或者新建的都行。在Project Explorer下右鍵選中工程打開Properties,而後找到Builders。這裏列舉了一個Android工程在編譯時用到的一些工具。接下來咱們選中New,選擇Program類型,接下來就是具體的設置了。貼圖以示清白:
Name能夠隨便取,具體的Location就要按照本身的安裝路徑設置。最後的Arguments比較長,個人是這樣的--login -c "cd '${project_loc}' && $NDK/ndk-build NDK_DEBUG=1",其中NDK_DEBUG=1是設置編譯出來的so文件是Debug版本,不須要的能夠去掉這一句。
接下來在Android工程目錄下新建一個文件夾jni,這個文件夾主要是存放mk文件和C/CPP的代碼文件。因而別猶豫,趕忙把hello-jni給的Android.mk文件複製進去。
在Android工程下新建一個Java文件TestJni.java:
public class TestJni {
    public native void test();
   
    static {
        System.loadLibrary("testjni");
    }
}
而後把Android.mk文件的LOCAL_MODULE對應的值改爲testjni。LOCAL_SRC_FILES這個選項是指所用到的C/CPP代碼文件。Jni的C/CPP代碼有其固定格式,得先用javah工具生成頭文件,而後按照頭文件的函數聲明來寫具體代碼。關於javah命令的使用,能夠查閱jni的有關資料,這裏再也不贅述。這裏有篇很實用的文章,提到的錯誤基本人人都會犯一遍:《用javah 導出類的頭文件, 常見的錯誤及正確的使用方法》。
正確生成頭文件,而後按照頭文件中的聲明格式寫C/CPP文件,把頭文件和C/CPP文件添加到Android工程的jni目錄裏,最後點Run As Android Application,這些代碼就會被編譯成so文件,在Java代碼中經過native方法能夠方便調用。
  
YUV420SP,即所謂的NV21格式,是Android系統中攝像設備的預覽數據的默認格式。Android API中有個方法Camera.Parameters.setPreviewFormat(int pixel_format),用來設置預覽數據的格式。嘗試過設置其餘如jpeg、png等更省事的格式,但在運行中都會報錯,仔細查閱API文檔,有這麼一句:It is strongly recommended that either NV21
or YV12
is used, since they are supported by all camera devices.這才知道不一樣Android設備支持的格式可能不同,但NV21和YV12是全部設備都支持的格式。接下來將以默認的格式NV21爲例。
在ARGB顏色模式下的圖像操做都很是輕鬆,由於每一個像素的顏色都用一個32位的整型數表示,按空間順序排列,很是符合人的正常思惟。但NV21格式的圖像數據就有點繞了,把Y和UV放到了兩個平面上,UV的採樣頻率的總和還只有Y的一半,平均下來一個像素佔用12位。NV21格式的具體介紹能夠從下面的參考文章找到,這裏再也不贅述,總之這個格式折騰了我很久…
下面是一段在YUV420SP格式下截取矩形圖像區域的代碼,邏輯簡單明瞭,但細節處容易出錯。僅供參考:
void getTargetRect(byte[] src, byte[] dst, int srcW, int srcH, int startW,
            int startH, int dstW, int dstH) {
        if (src == null || dst == null || srcW < 1 || srcH < 1 || startW < 0
                || startH < 0 || dstW < 1 || dstH < 1 || startW + dstW > srcW
                || startH + dstH > srcH)
            return;
        int srcFrameSize = (srcW * srcH * 3) >> 1;
        int dstFrameSize = (dstW * dstH * 3) >> 1;
        for (int j = startH; j < startH + dstH; ++j) {
            int jOffset = j - startH;
            int srcUv = srcFrameSize + (j >> 1) * srcW;
            int dstUv = dstFrameSize + (jOffset >> 1) * dstW;
            for (int i = startW; i < startW + dstW; i += 2) {
                int iOffset = i - startW;
                dst[jOffset * dstW + iOffset] = src[j * srcW + i];
                dst[jOffset * dstW + iOffset] = src[j * srcW + i + 1];
                if ((jOffset & 1)==0) {
                    dst[dstUv + iOffset] = src[srcUv + i];
                    dst[dstUv + iOffset + 1] = src[srcUv + i + 1];
                }
            }
        }
    }
有關圖像旋轉,參考文章[2]已經給出實現代碼,須要注意的問題是旋轉的方向,旋轉的方向能夠經過改變循環係數的增減方向來改變。
有意義的參考文章: