RenderScript was introduced with Honeycomb, aka API level 11. It is an API designed for performance intensive computations by executing native code on the device. These segments run either on the CPU or the GPU - this is not for the developer to decide, it is determined in runtime. I won’t go into more detail since there are a lot of comprehensive articles out there, for more info check out the documentation at the end of the post. This short tutorial is about how to write RenderScript code for simple image manipulation tasks.
Image manipulation problems can be highly parallelized, because (at least in this example) the same chunk of code is executed independently on each and every pixel. The runtime takes care of parallelizing and scheduling the most efficient way for the current device.
A lot of useful built-in scripts (called intrinsics) are available in out of the box, but some use cases require a different solution. The following section will show you how to create a script for dimming a bitmap by a specific percentage. It is more like a guideline on how to implement these sort of functions - the basics are given, writing your own requires only an algorithm.
Setting up the environment
Using RenderScript requires some extra effort:
- Add the following lines to the defaultConfig section of the build.gradle script:
- Create a rs directory in the src folder for the RenderScript files.
And you’re ready to roll!
Setting up the RenderScript code
The next step is writing the RenderScript code. The following lines dim every pixel of a bitmap by a given percentage. The processing takes about 110 milliseconds on a Nexus 5, using an image of 1920 x 1080 pixel resolution.
Let’s take a look at the RenderScript-specific elements!
- The #pragma version(1) defines the version of the RS kernel script (currently only a value of 1 is supported).
- The #pragma rs java_package_name declares the package name of the Java classes that use the script.
- The #pragma rs_fp_relaxed enables NEON optimizations on supported SoC-s.
- The attribute((kernel)) between the return value and the name of the dim function indicates that the method is a kernel function, invokable from Java code.
- The parameter and the return value are both of uchar4 types, representing the color of a pixel.
- The input parameter is automatically filled in.
- Global variables (like dimmingValue in the example) automatically generate setters.
The rest is pretty much self-explanatory. In the example the color of each pixel is dimmed by the amount; any other per-pixel based effect can be achieved by changing these calculations.
Using the script from java code
The reflected class will be available after completing the RenderScript file and building the project. The steps include initializing the RenderScript and the script objects, creating the necessary Allocations (these serve as inputs for the kernel), setting the required options, running and destroying the script. And of course since we’re talking about a potentially long-running operation, put everything into an async wrapper.
Creating a basic script is easy, but RenderScript is much more capable than this simple example. There are a lot of great resources available to learn more about RenderScript, here are my favourites.
- Introducing Renderscript and Renderscript Part 2 on the official Android dev blog
- RenderScript on the official documentation
- High Performance Applications with RenderScript by Jason Sams and Tim Murray at Google I/O 2014
- RenderScript Basic Tutorial for Android OS by Intel
The sample Activity is available at this Gist; I used RxJava as an async wrapper.