
Writing Effective Blur on Android
- Transfer
- Tutorial

Today we will try to understand the blur methods available for Android developers. After reading a certain number of articles and posts on StackOverflow, we can say that there are a lot of opinions and ways to accomplish this task. I will try to pile it all up.
And so, why?
Increasingly, you can notice the blur effect in applications appearing on the Google Play Store. Take at least the wonderful Muzei application from + RomanNurik or the same Yahoo Weather. Looking at these applications, you can see that with skillful handling with blur, you can achieve very impressive results.
The Blurring Images series of articles prompted me to write this article , so the first part of the article will be very similar. In fact, I will try to dig a little deeper.
Here's something we’ll try to achieve:

Let's get started
First, I want to show what we are working with. I created 1 activity within which is located
ViewPager
. ViewPager
leafing through fragments. Each fragment is a separate implementation of blur. Here's what mine looks like
main_layout.xml
:
And this is what the fragment layout (fragment_layout.xml) looks like:
As you can see, there is nothing military - an ordinary full-screen picture with text in the middle. You can also notice an additional LinearLayout - I will use it to display all kinds of service information.
Our goal is to blur the background of the text, thereby emphasizing it. Here is a general principle of how we will do this:
- From the picture, cut out the section that is located directly behind the TextView
- Wash out
- The resulting result is set as the background for the TextView
Renderscript
Probably the most popular answer today to the question "how to quickly blur an image in Android" is Renderscript. This is a very powerful tool for working with images. Despite its apparent complexity, many of its parts are very easy to use. Fortunately, blur is one of those parts.
public class RSBlurFragment extends Fragment {
private ImageView image;
private TextView text;
private TextView statusText;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
image = (ImageView) view.findViewById(R.id.picture);
text = (TextView) view.findViewById(R.id.text);
statusText = addStatusText((ViewGroup) view.findViewById(R.id.controls));
applyBlur();
return view;
}
private void applyBlur() {
image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
image.getViewTreeObserver().removeOnPreDrawListener(this);
image.buildDrawingCache();
Bitmap bmp = image.getDrawingCache();
blur(bmp, text);
return true;
}
});
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void blur(Bitmap bkg, View view) {
long startMs = System.currentTimeMillis();
float radius = 20;
Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()),
(int) (view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-view.getLeft(), -view.getTop());
canvas.drawBitmap(bkg, 0, 0, null);
RenderScript rs = RenderScript.create(getActivity());
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();
statusText.setText(System.currentTimeMillis() - startMs + "ms");
}
@Override
public String toString() {
return "RenderScript";
}
private TextView addStatusText(ViewGroup container) {
TextView result = new TextView(getActivity());
result.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
result.setTextColor(0xFFFFFFFF);
container.addView(result);
return result;
}
}
Let's see what happens here:
- When creating a fragment - a layout is created, a TextView is added to my “service panel” (I will use it to display the speed of the algorithm) and blurring starts
- Inside
applyBlur()
I registeronPreDrawListener
. I do this because at the time of calling this function, my UI elements are not ready yet, therefore there is nothing much to blur. Therefore, I need to wait for the moment when my layout will be measured and ready to draw. This callback will be called immediately before the first frame is drawn. - Inside, the
onPreDraw()
first thing I usually do is change the return value to true. The fact is that the IDE generatesfalse
by default, which means that the rendering of the first frame will be skipped. In this case, I am interested in the first frame, so we set it to true. - Next, we remove our callback - we are no longer interested in onPreDraw events
- Now I need to get the Bitmap out of my ImageView. I make her create a drawing cache and pick it up
- Well, actually, blur. Consider this process in more detail.
I want to note right away that this code has a number of disadvantages, which should be remembered:
- This code does not “overwrite” with layout changes. In a good way, you need to register onGlobalLayoutListener and restart the algorithm when this event is received
- Blurring is done in the main thread. Do not try to do this in your applications - this kind of operation must be "unloaded" in a separate thread so as not to block the UI. AsyncTask or something similar will do the job.
Back to
blur()
:- An empty Bitmap is created, the size corresponding to our TextView - here we copy a piece of our background
- Create a Canvas so that you can draw in this Bitmap
- We shift the coordinate system to the position where the TextView is located
- Draw a piece of background
- At this point, we have a Bitmap that contains a piece of background located directly behind the TextView
- Create a Renderscript Object
- Copy our Bitmap to the structure that Renderscript works with.
- Create a blur script (ScriptIntrinsicBlur)
- Set the blur parameters (in my case, radius 20) and run the script
- Copy the result back to our Bitmap.
- Great, we have a blurry Bitmap - set it as the background for our TextView
Here's what happened:

As you can see, the result is pretty good looking and took 57ms from us . Considering that drawing of one frame should not take more than 16ms (~ 60fps), we can assume that the frame rate in our case drops to 17fps for the period while the blurring is performed. I would say unacceptable. It is therefore necessary to load the blur into a separate stream.
I also want to note that it
ScriptIntrinsicBlur
is available in API> 16. Of course, you can use the renderscript support library, which will reduce the required level of API. But, as you might guess, the renderscript didn’t converge, so let's look at one of the alternatives.
Fastblur
In fact, blurring is nothing more than a manipulation of pixels, so what prevents us from manipulating these same pixels ourselves? Fortunately, on the Internet, a fairly large number of implementations of all kinds of blurring algorithms is available, so our task is to choose the most optimal one.
On the all-knowing StackOverflow ( or rather, here ), I came across a good implementation of the blur algorithm.
Let's see what came of it. I won’t give all the code, because only the blur () function will be different:
private void blur(Bitmap bkg, View view) {
long startMs = System.currentTimeMillis();
float radius = 20;
Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()),
(int) (view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-view.getLeft(), -view.getTop());
canvas.drawBitmap(bkg, 0, 0, null);
overlay = FastBlur.doBlur(overlay, (int)radius, true);
view.setBackground(new BitmapDrawable(getResources(), overlay));
statusText.setText(System.currentTimeMillis() - startMs + "ms");
}
And here is the result:

As you can see, the quality is difficult to notice the difference with renderscript. But the performance is poor - 147ms ! And this is far from the slowest algorithm! I'm afraid to even try to blur Gauss.
Optimize
Let's think for a second what blur is all about. At its core, blurring is very closely connected with the “loss” of pixels (I would like to ask experts on mathematics and graphics not to swear much, because I describe it more based on my understanding of the problem than on specific facts :)). What else can easily help us “lose” pixels? Reducing the picture!
What if we reduce the image first, blur it, and then enlarge it back?
Let's try it!

And so, we have 13ms renderscript and 2ms FastBlur. Pretty good considering that the quality of the blur remains comparable in quality to the previous results.
Let's take a look at the code. I will describe only the FastBlur option, as the code for Renderscript will be similar (a link to the full version of the code is available at the end of the article):
private void blur(Bitmap bkg, View view) {
long startMs = System.currentTimeMillis();
float scaleFactor = 1;
float radius = 20;
if (downScale.isChecked()) {
scaleFactor = 8;
radius = 2;
}
Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()/scaleFactor),
(int) (view.getMeasuredHeight()/scaleFactor), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-view.getLeft()/scaleFactor, -view.getTop()/scaleFactor);
canvas.scale(1 / scaleFactor, 1 / scaleFactor);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(bkg, 0, 0, paint);
overlay = FastBlur.doBlur(overlay, (int)radius, true);
view.setBackground(new BitmapDrawable(getResources(), overlay));
statusText.setText(System.currentTimeMillis() - startMs + "ms");
}
scaleFactor
determines how much we will reduce the picture. In my case, we will reduce it by 8 times. Moreover, given that we lose the lion's share of pixels when reducing / increasing the image, we can safely reduce the blur radius of the main algorithm. By scientific poke I reduced to 2x- Create a Bitmap. This time it will be less - in this case, 8 times.
- Note that when rendering, I set the flag
FILTER_BITMAP_FLAG
, which will allow smoothing when resizing the picture - As before, we apply blur, but now to a much smaller picture and with a smaller radius, which allows us to speed up the algorithm
- We put this small picture as a background for TextView - it will be automatically enlarged.
Interestingly enough, Renderscript worked slower than FastBlur. This happened due to the fact that we saved time on copying Bitmap to Allocation and back.
As a result, we got a pretty quick method of blurring images on Android.
Useful links:
Sources for this
article on GitHub. Ancestor of the article.
Post on SO on blurring algorithms.