ImageView / RenderScript

Blurring Images – Part 1

There are a lot of cool effects that we can do in Android to manipulate images, and I cover some of these in my conference presentation for 2014 entitled Graphical Magic. One technique that I cover in the presentation is how to blur images and the example code uses RenderScript to perform the blur because there is no simple API built into Android that we can use. In this series we’ll look at both the RenderScript blurring technique, and also how we can also perform blurring in Java. We’ll also perform some benchmarking to understand how well each approach performs, and look at ways that we can obtain the best performance.

Let’s start by getting a really simple example working, using RenderScript! This is a concept which may strike fear in to the hearts of developers who have never used RenderScript, because RenderScript is really hard, isn’t it? Yes, it can be. However, there are some things that are really easy, and blurring images is one of them.

For those who are unfamiliar with RenderScript it was introduced in API11, and there is a compat library which provides RemderScript to API8 and later. It is essentially a native computation framework particularly geared towards for graphics. The RenderScript engine will determine the most appropriate processor (CPU or GPU core(s), and can split atomic operations across multiple processors) to perform a requested operation at runtime. The native syntax is based upon C99, bearing API similarities to OpenCL, CUDA, and GLSL.

If that sounds scary please stick around because it’s just about to get a whole lot easier. While it is a framework which permits us to create custom kernels to perform filtering and processing, there are some useful built in kernels that we can use, and one of these allows us to blur an image.

The following example code is developed for API17 and higher. I have decided against using the compat library because, at the time of writing, it is not supported within Android Studio. Furthermore the blur kernel which we’ll use was not introduced until API17, hence the minSdkVersion 17 requirement.

Let’s dive in to a simple example. Here’s a very simple RelativeLayout containing an ImageView with a TextView on top of it:



	
	

	

The effect that we want is to blur the area of the image within the ImageView which falls within the bounds of the TextView – effectively blurring the area behind the ImageView. The technique that we’re actually going to use is to take a copy of the area of the image which falls within the bounds of the TextView, blur it, and then set the blurred copy as a the background of the TextView.

part1

I have very deliberately designed this layout so that the image displays actual size starting from the top left of the display. This makes the position calculations which follow much simpler and this is a discussion of blurring techniques rather than image positioning mathematics. Try setting android:scaleType="center" on the ImageView and you’ll see the positioning go horribly wrong!

Here’s a method which takes three parameters, a Bitmap (which has been obtained for the ImageView); a View (this is our TextView, but the technique works for any View type, so we’ll support that in our method); and a radius which control how much blurring will be applied:

private void blur(Bitmap bkg, View view, float radius) {
	Bitmap overlay = Bitmap.createBitmap(
		view.getMeasuredWidth(), 
		view.getMeasuredHeight(), 
		Bitmap.Config.ARGB_8888);

	Canvas canvas = new Canvas(overlay);

	canvas.drawBitmap(bkg, -view.getLeft(), 
		-view.getTop(), null);

	RenderScript rs = RenderScript.create(this);

	Allocation overlayAlloc = Allocation.createFromBitmap(
		rs, overlay);

	ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(
		rs, overlayAlloc.getElement());

	blur.setInput(overlayAlloc);

	blur.setRadius(radius);

	blur.forEach(overlayAlloc);

	overlayAlloc.copyTo(overlay);

	view.setBackground(new BitmapDrawable(
		getResources(), overlay));

	rs.destroy();
}

The first thing that we need to do is create a new Bitmap object to hold the copy of part of our image which we intend to blur. The size of this will be the dimensions of our View (lines 2-5).

Now we wrap that blank bitmap within a Canvas that we can draw upon (line 7).

The next step is what I call the “cookie cutter” we copy the section of the image from the Bitmap which falls within the bounds of the View (lines 9-10).

The next thing that we must do is construct a RenderScript object which of the context within which we’ll perform the blur (line 12).

RenderScript is a native environment which operates within its own memory space, therefore we cannot simply pass references to bitmap data across, we need to marshal it between the Java and RenderScript memory spaces. This is done using an Allocation instance – which is how we create and reference an object within the RenderScript memory space. Creating the Allocation from our new Bitmap will copy the contents of the Bitmap in to the Allocation (lines 14-15).

Now we create a ScriptIntrinsicBlur instance which creates the appropriate blur script in RenderScript, and is the Java interface to that object which allows us to control it (lines 17-18).

The input for the script defines the source bitmap upon which the blur will be performed (line 20).

We now set the radius to control the strength of the blur (line 22).

The forEach method performs the blur, and the argument represents where the result should be output. We’ll write back to the source Allocation to reduce the number of object we need to create (line 24).

The blur is now complete, but we must copy the blurred image back to Java memory space (line 26).

Finally we wrap the blurred bitmap inside a BitmapDrawable and set this as the background of our View lines (28-29).

We’re done with our RenderScript context so we clean it up. Doing this will automatically delete our Allocation, as well (line 31).

That is our basic blur logic complete, but what isn’t yet clear is where and when we need to call this method. Unfortunately there is not a simple answer to that question, so we’ll look on to that in the next article.

Usually I like to publish working code with each article, but in this case we haven’t yet got to a point where we have a full end-to-end example working. I promise that it will be rectified in the next article!

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

3 Comments

  1. Hi Mark,
    I am getting “android.renderscript.RSIllegalArgumentException: Unsuported element type.” error line 17 in blur method. How can I fix it ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.