更新:比较新的版本的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]已经给出实现代码,须要注意的问题是旋转的方向,旋转的方向能够经过改变循环系数的增减方向来改变。
有意义的参考文章: