我的简书同步发布: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=18
renderscript.support.mode=true

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);}
}

这是使用普通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;}

看不懂?不要急!我们一行一行解释。仔细看会发现其实大部分跟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代码如何调用,使之并行计算。

    @Overrideprotected 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();}

运行出来的结果:

我们继续解释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

RenderScript 让你的Android计算速度快的飞上天!相关推荐

  1. 阿里安全开源隐私计算新技术:计算速度快20倍,通信成本低2倍,已登安全顶会...

    博雯 发自 凹非寺 量子位 | 公众号 QbitAI 如何在不查看图片的前提下,几秒之内就识别一张图片? 这个看似在找茬的问题,却是隐私计算领域会真实碰到的问题. 著名的"百万富翁问题&qu ...

  2. android java 时间格式化_(Java / Android)计算两个日期之间的日期,并以特定格式显示结果...

    我试图在2个日期之间计算日期如下: >获取当前日期 >获取过去或未来的日期 >计算不同的差异. 1和否2 >以下列格式显示日期 >如果结果是在过去(2天前)或将来(在2天 ...

  3. android计算dpi代码_android计算pad或手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法...

    手机分辨率基础知识(DPI,DIP计算) 1.术语和概念 术语 说明 备注 Screen size(屏幕尺寸) 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸 摩托 ...

  4. Android计算标准BMI值

    今天做了关于计算标准BMI值的小作业,可以计算出你的体重是正常,偏瘦或偏胖 MainAcitivity.java 1 package com.example.bmi; 2 3 import java. ...

  5. android计算手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法

    手机分辨率基础知识(DPI,DIP计算) 1.术语和概念 术语 说明 备注 Screen size(屏幕尺寸) 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸 摩托 ...

  6. android计算bmi的程序,简单的基于android的BMI计算应用

    写这样的一个程序,纯粹是为了入门,为了练手,为何选择BMI,因为正好手边的书上就是拿这个做例子...何况写个BMI总比写个计算器来的简单...好这就开始了. BMI(即身体质量指数,称简体质指数又称体 ...

  7. android 计算到期日期,在Android上计算日期

    我正在为Android构建一个应用程序,我需要存储一天并计算到那一天到来的天数. 我将这一天存储在共享的首选项上.首先,我初始化日历. Calendar next = Calendar.getInst ...

  8. 判断生日日期的Android代码,android计算生日的方法

    前阵子遇到一个需求:计算生日.假设生日是2016年2月1号,那么我需要得到一个"1岁28天"的字符串.我没想到java或者android API里有现成的方法可以计算,ios小哥表 ...

  9. android 计算运动速度,android – 计算参考真北的加速度

    加速计传感器返回设备的加速度.这是3维空间中的向量.该向量在设备坐标系中返回.你想要的是这个向量在世界坐标中的坐标,这很简单 R = rotation matrix obtained by calli ...

最新文章

  1. java dateformat 线程安全_SimpleDateFormat线程安全问题深入解析
  2. Anu Has a Function CodeForces - 1300C(二进制位运算)
  3. static_cast, dynamic_cast, const_cast探讨【转】
  4. android jni ——Field Method -- Accessing Field
  5. python iterator iterable_Python中Iterator和Iterable的区别
  6. 最重要的 Java EE 最佳实践
  7. 下标要求数组或指针类型_算法一看就懂之「 数组与链表 」
  8. vs 未能找到符号_意大利甲级赛事分析:尤文图斯vs卡利亚里
  9. java 判断端口是否开放telnet
  10. python3-day2(基本回顾)
  11. 主从复制面试之作用和原理
  12. 如何获得鼠标选中的值
  13. 黑马JAVA P108 语法知识:枚举、枚举作用
  14. 关于能力模型的思考总结
  15. android,解决手动创建的桌面快捷方式无法跳转到制定的activity的问题,提示未安装应用程序
  16. leetcode 买卖股票的最佳时机含手续费(Java)
  17. 使用USB充电的5号电池
  18. 用python计算100以内所有奇数的和_用python脚本来计算100以内奇数或者偶数之和
  19. Mysql常用类型和字段属性
  20. html 无缝拼接,用jQuery写一个无缝衔接轮播图,超精简又详细

热门文章

  1. Vue、ElementUI
  2. 【CSS】课程网站 Banner 制作 ① ( Banner 栏测量 | Banner 盒子模型代码 | 代码示例 )
  3. PLSQL连接登录失败
  4. 5行代码提升时间序列预测,都有用!
  5. 微信公众号接入H5支付
  6. ant design vue table分页
  7. Typora安装包64位---百度网盘下载
  8. 【华为】Smart-Link基础知识
  9. DNS中的A记录和CNAME记录的区别
  10. 如何解决浏览器的兼容性