Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ImageView blur animation

I'm looking to add a blur animation to an ImageView, but with a set duration. So, for example I want an image to blur out over time.

I already have the method to blur the image, but what I need is to make it go from blur to none blur over, say, 2 seconds.

Can anybody help me out?

EDIT: This is the method I currently have to blur the image.

public Bitmap blur(Bitmap sentBitmap, int radius) {

    // Stack Blur Algorithm by Mario Klingemann <[email protected]>

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][3];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            // Preserve alpha channel: ( 0xff000000 & pix[yi] )
            pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];

            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}
like image 556
Fifer Sheep Avatar asked Jul 18 '14 13:07

Fifer Sheep


People also ask

How do I blur an image in XML?

There are two way to achieve. 1) You can use FrameLayout to which you can set blur background. 2) You can use latest Blur library which i have !

How do you blur the background of a picture on android?

You simply open the camera > tap menu > select “portrait” option > take your picture > tap the thumbnail to select the image you just captured > Google automatically adds the blur background effect to the image.


2 Answers

Blur effects are always difficult on Android. Essentially you have to decide between looks and performance. The better the blur looks the longer it takes, and if the blurring itself is not instantaneous then you cannot really animate the blur.

You original blurring algorithm produces really good results, but because of that it is also very slow making a blur animation impossible. Just to demonstrate what it would take to effectively blur this image I have created a simple blur animation by scaling the bitmap down:

public class BlurAnimation extends Animation {

    private final ImageView imageView;
    private final Bitmap bitmap;
    private final float startValue;
    private final float stopValue;
    private final float difValue;

    private BlurAnimation(ImageView imageView, Bitmap bitmap, int startValue, int stopValue) {
        this.imageView = imageView;
        this.bitmap = bitmap;
        this.startValue = startValue;
        this.stopValue = stopValue;
        this.difValue = stopValue - startValue;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);

        int current = (int)(this.difValue * interpolatedTime + this.startValue + 0.5f);
        Bitmap blurred = quickBlur(this.bitmap, current);
        this.imageView.setImageBitmap(blurred);
    }

    public Bitmap quickBlur(Bitmap bitmap, int factor) {
        if(factor <= 0) {
            return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        }
        return Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / factor, bitmap.getHeight() / factor, true);
    }
}

This works quite well (even though there is still some lag), but the results cannot be compared with your blur algorithm, it just looks terrible:

enter image description here

So you see, it is very difficult to combine performance and good looks when it comes to blurring an image, but there are some options first and foremost RenderScript. RenderScript is very fast and has a built in gaussian blur filter. I have never used it, but from what I hear it might be the solution to your problem.

You can also try to load already scaled down versions of an image, this would produce the same effect as in the gif above, but would be even faster. The drawback is that using this in an Animation is again problematic, but if you just need a blurred image and you don't really care about quality then you should go for this option.

You can find more information about RenderScript and other fast blur options in this answer

like image 154
Xaver Kapeller Avatar answered Oct 12 '22 08:10

Xaver Kapeller


I won't prolong my answer too much with code because honestly I don't have it, but I'll try to point out the key things you must consider and a few useful links.

first, the approach:

  • blur as fast as possible
  • cache the blurred result
  • cross-fade

blur as fast as possible:

your algorithm looks really nice and I believe it does give a good effect, but the reality is that it's running in Java VM in a single thread. You will for absolutely sure have much better performance using RenderScript. That's because RenderScript auto-scales the rendering process to multi-processor and to the GPU.

the base code for it is below, taken directly from android-developers.blogspot.com:

RenderScript rs = RenderScript.create(theActivity);
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(rs));;
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
theIntrinsic.setRadius(25.f);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);

cache the blurred results:

That's because Blur takes time. There will also be a lot of memory implications on that and you must watch it carefully. You certainly won't be able to do it very well in a scrolling list that is recycling views and stuff. A tip maybe is to use Picasso library (LINK) for that and also handle your threading. Something like this:

Picasso
   .with(context)
   .load(/* asset/res/file/url */)
   .transform(new BlurTransformation(value))
   .into(target);

// The transform method is automatically called by Picasso in a background thread
public class BlurTransformation implements com.squareup.picasso.Transformation {
    private final float radius;

    // radius is corner radii in dp
    public BlurTransformation(final float radius) {
        this.radius = radius;
    }

    @Override
    public Bitmap transform(final Bitmap source) {
       // do the transformation and return it here
    }

    @Override
    public String key() {
        return "blur"+Float.toString(radius);
    }
}

// you can also use this to get it instantly
Picasso... all the code... .get();

cross-fade:

here is where you'll balance between performance and great looks. The reality is that a real animation is impossible without actually render every frame for every step, but with a cross-fade between a few key-frames you can achieve great results.

How many key-frames you'll use will depend on the performance you want, the amount of free memory on the device, etc.

If you're fine with just 2 key-frames (1 not blurred and 1 totally blurred) you can probably apply a TransitionDrawable to it. If you want a finer looking effect, you'll have to create a series of cross-fade transitions or probably create your own custom drawable.

for just 2 key-frames you can see an example on the Yahoo Weather app, if you would like to see an example with a few key-frames you want to check Muzei live wallpaper.

very useful links*

On the following link you'll see a nice discussion by Muzei creator (Roman Nurik, which is also one of the engineers at Google working for the Android) on how he achieved the key-frames, why that's the only way to do it and why, although more complex code, it creates at the end a much prettier result: (short version) https://plus.google.com/+RomanNurik/posts/2sTQ1X2Cb2Z (complete version) https://medium.com/@romannurik/serendipitous-ideas-3a1721a6f716

This link is the source code for Muzei live-wallpaper where you can check how he calculates the key-frames and animates the wallpaper: https://github.com/romannurik/muzei

Good luck and happy coding!

like image 26
Budius Avatar answered Oct 12 '22 08:10

Budius