AndroidRenderScript的简单使用

 

配置环境

Android官方好像说RenderScript已经停止维护了,但是相比较Vulkan我感觉RenderScript的学习成本更低,因为RS脚本更像C语言,使用起来简单方便,在图片处理的速度上也不亚于NDK,一些简单的图片处理需求使用RS脚本还是比较方便的

我使用的RS脚本是AndroidX库里面的,没有使用support库是因为打包以后的体积更大

根据官网的描述,我们首先在模块级的Gradle文件下添加以下配置

        android {
            compileSdkVersion(28)

            defaultConfig {
                minSdkVersion(9)
                targetSdkVersion(19)

                renderscriptTargetApi = 18
                renderscriptSupportModeEnabled = true
            }
        }
        

renderscriptTargetApi的有效值是11到最新发布的Api

renderscriptSupportModeEnabled 指定如果生成的字节码不支持当前设备自动回退到兼容版本

因为core内自带对AndroidX RS脚本的支持,所以无需导入依赖

使用

RS脚本的创建

首先在app\src\main 路径下创建rs文件夹,这个文件夹是专门用来放置.rs 文件的

我在这里创建了一个ImageSize.rs文件,其内容是

#pragma version(1)
#pragma rs java_package_name(org.phcbest.koutudemo)



int xx = 100;
int h = 0;
int w = 0;

int data[4];

typedef struct TestS{
    int num;
    int age;
}Test;

Test ttss;

uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) {
    uchar4 out = in;
    out.r = 255 - in.r;
    out.g = 255 - in.g;
    out.b = 255 - in.b;
    if (x == w-1 && y == h-1){
        data[0] = 123123;
        data[1] = 111113;
        data[2] = x+1;
        data[3] = y+1;
        ttss.num = 10;
        ttss.age = 100;
        rsSendToClient(11,&ttss,sizeof(ttss));
        rsSendToClient(10,data,sizeof(data));
        //rsSendToClient(xx);
    }
    return out;
}

这种语言和C99很像

这个demo中完成的内容是将输入的像素反色,并且使用rsSendToClient和Android层通信

脚本功能不难理解,我们需要理解的内容是上面两行pragma

  • #pragma version(1)
    

    这个用于声明此脚本中使用的 RenderScript 内核语言的版本。目前,1 是唯一有效的值。

  • #pragma rs java_package_name(org.phcbest.koutudemo)
    

    这个是用来指明生成的中间层文件的位置,前半部分使用固定格式,括号内填写应用的包名

在Android层中使用

private fun testGetImageSizeByRs(kato: Bitmap) {
        val rs = RenderScript.create(this)
        val imageSize = ScriptC_ImageSize(rs)
        val inputAllocation = Allocation.createFromBitmap(rs, kato)
        rs.messageHandler = object : RenderScript.RSMessageHandler() {
            override fun run() {
                when (mID) {
                    11 -> {
                        Log.i("TAG", "mid is : $mID")
                        Log.i("TAG", "mData is : $${mData[0]}")
                        Log.i("TAG", "mData is : $${mData[1]}")
                        Log.i("TAG", "mData is : $mLength")
                    }
                    10 -> {
                        Log.i("TAG", "mid is : $mID")
                        Log.i("TAG", "mData is : $${mData[0]}")
                        Log.i("TAG", "mData is : $${mData[1]}")
                        Log.i("TAG", "mData is : $${mData[2]}")
                        Log.i("TAG", "mData is : $${mData[3]}")
                        Log.i("TAG", "mData is : $mLength")
                    }
                    else -> {}
                }
            }
        }
        imageSize._h = kato.height
        imageSize._w = kato.width
        val outputType = Type.Builder(rs, Element.RGBA_8888(rs))
            .setX(kato.width)
            .setY(kato.height)
        val outputAllocation = Allocation.createTyped(rs, outputType.create())
        val outputBitmap = Bitmap.createBitmap(kato.width, kato.height, kato.config)
        imageSize.forEach_invert(inputAllocation, outputAllocation)
        outputAllocation.copyTo(outputBitmap)
    
    	inputAllocation.destroy()
        outputAllocation.destroy()
        rs.destroy()
    }

Android层面的代码量也很少,使用起来特别方便,我们来梳理一下流程

  • 创建使用上下文创建RS对象
  • ScriptC_ImageSize是AndroidStudio自动给我们生成的中间层类,只要编写完RS脚本之后编译就会自动生成,我们初始化该类
  • 初始化输入分配
  • 给rs设置RSMessageHandler,这个配置是和 rsSendToClient配套的,在rsSendToClient发送的消息在RSMessageHandler中接收
    • mID为rsSendToClient方法的第一个参数,是我们自定义的消息唯一ID
    • mData是一个Int数组,是传输的载荷,就算我们RS脚本中传递的是结构体,Android层收到的也只是Int数组
    • mLength是载荷的大小,RS层中使用sizeof(data)来获得载荷的大小,这一点和C99一样
  • 接下来就是设置一些RS里面的全局变量
  • 初始化输出类型,使用Type.Builder来创建
  • 初始化输出分配
  • 调用RS脚本,使用forEach_invert,invert是rs脚本中的方法,forEach_是固定的调用格式
  • 释放资源

这些就是简单的RS脚本使用,还有RS层和Android层通信的一些接口,还有一些需要注意的,RS脚本中使用rsDebug来打印日志,如果需要控制台显示日志需要在创建RenderScript对象的时候使用DEBUG模式,不然控制台不会打印信息,因为RS脚本是并行计算的,打印log会拖慢其图片处理的速度

val rs = RenderScript.create(this,RenderScript.ContextType.DEBUG)