我的简书同步发布:RenderScript 让你的Android计算速度快的飞上天!

在上一篇文章Android自动手绘,圆你儿时画家梦! 中结尾提到,我将介绍提升轮廓提取速度相关内容,今天一起学习Android中的RenderScript。看完本文,你将学会如何使用并行计算技术,提高你的app中计算模块速度,尤其是提升图像处理中的复杂计算。

RenderScript介绍

根据Android官方网站的介绍:RenderScript是Android平台上用于运行计算密集任务的框架。RenderScript主要是面向数据并行计算,当然了,RenderScript中使用串行计算效率也很好。RenderScript是充分利用GPU,CPU的计算能力,由于不同的硬件对应的并行执行不同,RenderScript会编译2次,首先是我们的PC编译器编译到apk中,然后在apk安装的时候,再编译一次。这样的好处是,可以充分利用不同的硬件,我们编写的代码无需关心具体的硬件的不同,都能写出高性能的代码。 
RenderScript相关文档并不多,导致很难去学好RenderScript。但是其实用起来并不复杂,结合SDK中的两个例子和官方文档,基本可以入门了。

在使用RenderScript之前,请在eclipse的project.properties加上:

renderscript.target=18renderscript.support.mode=true
  • 1
  • 2
  • 1
  • 2

Hello RenderScript

概念说太多没啥用,先来一段简单代码。需求很简单,我们需要将一张图片中的每个像素的颜色取反色,即分别将255减去当前像素点的R、G、B,得到的新的RGB作为当前像素点的新颜色。如果不用RenderScript,实现起来也非常简单,通过两个for循环,遍历每个像素点,然后替换像素就好,如下:

int width = mInBitmap.getWidth();int height = mInBitmap.getHeight();for (int x = 0; x < width; x++) {    for (int y = 0; y < height; y++) {        int color = mInBitmap.getPixel(x, y);        int r=  255-(Color.red(color) ;        int g=  255-(Color.green(color) ;        int b=  255-(Color.blue(color) ;        int c = Color.rgb(gray, gray, gray);        mOutBitmap.setPixel(x, y, c);     }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这是使用普通Java代码实现,如果对一张较大的图执行这段代码,其耗时可想而知!再去看看RenderScript是如何实现相同的功能的: 
首先,在代码目录下(即包目录下)创建rs文件,取名可以任意,我们新建一个hello.rs文件:

#pragma version(1)#pragma rs java_package_name(com.hc.renderscript)uchar4 __attribute__((kernel)) invert(uchar4 in){  uchar4 out = in;  out.r =255- in.r;  out.g = 255-in.g;  out.b = 255-in.b;  return out;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

看不懂?不要急!我们一行一行解释。仔细看会发现其实大部分跟C语言很像,首先#pragma是给编译器看的,#pragma version(1)是指版本号,目前只能选择1,没有更高的版本了。#pragma rs java_package_name(com.hc.renderscript)是告诉编译器,包的名称。因为每个rs文件都会自动生成对应的Java代码,比如,我们新建的hello.rs文件,会自动生成ScriptC_hello类,因此,我们需要在rs声明包的名称。接下来比较重要的关键字__attribute__((kernel)),它跟函数放在一起,用于声明这个函数是个RenderScript核心函数,而不是一个可调用的函数。什么意思呢?其实可以这样理解,就是这个函数不是个普通函数,是用于并行计算的函数。我们不能显式调用,它是RenderScript内部调用的函数。这时你可能会想,既然我们不能显式调用,那该怎么调用呢?别急,接下来为你揭晓。

我们继续看到invert函数,这个函数有个uchar4类型,不用想,肯定表示占用4个字节,每个字节表示的取值范围0~255。但是接下来的事情就很奇怪了,uchar4 in中直接可以用in.rin.gin.b分别取出rgb颜色。我猜想uchar4是个结构体类型,本来想去官网查看一下,找了很久没找到。找到的童鞋麻烦告诉我一下,我可以重新编辑这篇文章。但是就算没找到,我们也可以理解的通,其实,如果从本质上来说,它并不复杂,r表示第一个字节,g表示第二个字节,b表示第三个字节。甚至我们可以可以猜得到,还有in.a表示透明度,然后我测试了一下,发现真的编译通过。另外,从RenderScript Basics Tutorial这篇文章可以知道,还可以通过xyzw分别取出对应的第1、2、3、4个字节。也就是说,in.xin.r都是一个意思.好了这里不再继续纠结uchar4.

RenderScript的核心我们编写完成了,从上面rs文件的invert函数我们知道,这个函数只对具体一个像素点操作,可是我们的图片有width*height个像素点,我们需要这些像素点并行执行inver函数才能得到我们想要的结果。

我们再看看Java代码如何调用,使之并行计算。

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mSrcImageView = (ImageView) findViewById(R.id.src);        mDstImageView = (ImageView) findViewById(R.id.dst);        mInBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);        mOutBitmap = Bitmap.createBitmap(mInBitmap.getWidth(),mInBitmap.getHeight(), mInBitmap.getConfig());        mSrcImageView.setImageBitmap(mInBitmap);        RenderScript rs = RenderScript.create(this);        mScript = new ScriptC_hello(rs);        aIn = Allocation.createFromBitmap(rs, mInBitmap);        aOut = Allocation.createFromBitmap(rs, mInBitmap);        mScript.forEach_invert(aIn, aOut);        aOut.copyTo(mOutBitmap);        mDstImageView.setImageBitmap(mOutBitmap);        rs.destroy();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行出来的结果: 
关于Renderscript的理解_第1张图片 
我们继续解释Java代码:先看到第13行,创建的是一个RenderScript对象。接下来是将我们编写的rs文件对应的自动生成的Java类(即ScriptC_hello类)初始化。到目前为止,这些都很好理解。紧接着是创建了两个Allocation对象,这个对象是干嘛用的呢?从名称可以看出,它是用于分配内存的,createFromBitmap根据Bitmap分配内存。为什么需要创建2个Allocation对象呢?这主要是在执行rs文件里面的并行函数时一个Allocation类型 aIn用于参数传入,一个Allocation类型 aOut用于计算结果输出。这两个Allocation的Element类型必须相同,在函数调用时RenderScript会检查,如果不想同会抛异常。这里提到了ElementElemtent是指Allocation里的一项。比如我们要处理的是Bitmap,则Element表示的类型是像素。做并行计算时,aIn对应的一个元素(Element)的计算结果会放入aOut对应的位置上。定位到代码:mScript.forEach_invert(aIn, aOut);我们的rs文件里面并没有写forEach_invert函数,但是却在ScriptC_hello 类里面生成了这个函数。请注意,我们编写了invert函数,正因为我们的invert函数加了__attribute__((kernel))关键字,所以,会生成forEach_invert函数,这个函数传入的参数aIn和aOut我们都清楚了,RenderScript会自动将aIn里的每个元素(Element)并行的去执行invert函数.得到的结果放入aOut里。最后调用AllocationcopyTo函数把计算的结果转入到Bitmap中。

另外,值得注意的是,__attribute__((kernel))修饰的函数,其形参该怎么写,为啥我们这里是uchar4而不是uchar3或者是uint32之类的呢?我们该怎么确定好这个参数呢?其实,这主要是跟我们的需求有关,你可以根据需求改动。比如我们的aIn里的元素是像素,而一个像素有RGBA占4个字节,因此我们写成uchar4作为形参。还有就是,后面还可以加形参uint32 x,uint32 y,uint32 z。这些是可选项,可以加也可以不加,不影响函数的调用,但是必须是uint32类型。

还有个可选函数init(),在rs文件里的这个函数会指初始化时调用,并且只会调用一次。

有时候我们希望返回的结果不止一个对象该怎么办?我们可以选择使用全局变量,在rs文件中声明全局变量,在rs文件的函数中把数据写入到rs文件的全局变量中。再从Java代码中读取rs的全局变量即可!那么在Java代码中该怎么读取和设置rs中的全局变量呢?答案是,rs文件对应生成的Java类会自动生成全局变量的get和set方法。比如,在hello.rs文件中定义了全局变量int myVar.自动生成的ScriptC_hello类中会自动生成函数:set_myVar(int v)get_myVar().这样就可以访问rs文件中的全局变量了。

最后

回到最开始说的,提升上一篇文章的轮廓提取速度。如果没有看过上一篇文章的请跳过,或者是前去: Android自动手绘,圆你儿时画家梦! 查看。我们去看看Sobel算法,主要分为2步,首先将彩色图转为灰度图,在CommenUtils类的toGrayscale函数中。然后再是调用Sobelsuanf ,在SobelUtils类的Sobel函数。先看看toGrayscale函数:这个函数是直接调用系统的函数,我们不去管。在Sobel函数中,有两个地方使用了两个for循环,显然可以通过RenderScript进行并行计算,提升速度。篇幅原因,具体的实现这里就不提了。

源码地址:RenderScript

更多相关文章

  1. Android中使用xml保存数据(xml文件序列化)及使用pull解析xml文件
  2. Android 文件读写操作方法总结
  3. 对android里布局文件当中的TextView对象设置事件监听,但是不响应
  4. Android解析xml文件
  5. android创建隐藏文件或者文件夹,并对其读写操作
  6. [基础知识]全面认识AndroidManifest.xml文件(二)
  7. android下修改hosts文件不起作用(无效)的解决办法
  8. Android JSON解析示例代码
  9. android 模拟器手机如何添加文件到sd卡

随机推荐

  1. android 多个activity 共用一套广播时,写
  2. Android获取sdcard信息
  3. Android 优化
  4. Android 创建与解析XML(三)—— Sax方式(源
  5. android crash自动化分析
  6. android 监听网络连接状态的改变
  7. Android软键盘的弹出与隐藏
  8. Android ListView 局部刷新
  9. android如何往SDCard中存取图片
  10. build.gradle(module)