不知道从下午的某个时候开始,微信朋友圈一片雾霾。点开微信朋友圈被一大波的朦胧照片刷屏,点开之后想看高清照只能随机发红包给对方才能观看。虽然这股雾霾没几个小时就散了,但着实让朋友圈又沸腾了一次。比如像下面这样的:

这种效果我们一般称为高斯模糊或毛玻璃(磨砂)效果,那么如何实现这样的效果呢?Github上搜索了一下发现已经有多种实现方法,一般来说,解决方案有如下四种:

  • 1、Java算法实现;
  • 2、RenderScript;
  • 3、NDK实现的算法处理;
  • 4、openGL处理;

从性能方面考虑的话,java算法实现最差,openGL实现最优。RenderScript和NDK实现应该是比较接近的。但是考虑到易用性,使用RenderScript还是比较方便的。

Github上有就个项目将几种blur实现做对比测试: https://github.com/patrickfav/BlurTestAndroid

测试发现使用ScriptIntrinsicBlur是最高效的。但是ScriptIntrinsicBlur从API 17才引入也就是说Android4.2以下的版本都无法使用,不过可喜的是Android 团队为我们提供了RenderScript support library,所以在低版本上也可以使用ScriptIntrinsicBlur来实现blur效果。

首先在build.gradle加入renderscriptTargetApi 19renderscriptSupportModeEnabled true以引入RenderScript support library。

    defaultConfig {
        applicationId "com.liuzhichao.blurdemo"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        renderscriptTargetApi 19
        renderscriptSupportModeEnabled  true
    }

然后使用ScriptIntrinsicBlur实现blur功能:

import android.content.Context;
import android.graphics.Bitmap;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;

/**
 * Created by LiuZhichao on 1/26/16.
 */
public class RenderScriptGaussianBlur {

    private RenderScript rs;
    public RenderScriptGaussianBlur(Context context){
    this.rs = RenderScript.create(context);
    }

    public Bitmap blur(int radius, Bitmap bitmapOriginal) {
        final Allocation input = Allocation.createFromBitmap(rs, bitmapOriginal);
        final Allocation output = Allocation.createTyped(rs, input.getType());
        final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setRadius(radius);
        script.setInput(input);
        script.forEach(output);
        output.copyTo(bitmapOriginal);
        return  bitmapOriginal;
    }
}

界面上根据seekbar的值改变blur的程度。radius的取值范围是(0,25]。

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;
    private SeekBar mSeekBar;
    private RenderScriptGaussianBlur mRenderScriptGaussianBlur;
    private Bitmap mBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.imageview);
        mSeekBar = (SeekBar) findViewById(R.id.seekBar);
        mRenderScriptGaussianBlur = new RenderScriptGaussianBlur(this);

        mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);

        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int radius = seekBar.getProgress();
                if (radius <= 0) {radius = 1;} // 0 < radius <= 25
                mImageView.setImageBitmap(mRenderScriptGaussianBlur.blur(radius,mBitmap));
            }
        });
    }
}

这是blur后的效果图:
android_blur

不要想太多,这是原图,
android_blur
如果你不想重复造轮子,推荐几个库,可以直接使用:
Blurry:https://github.com/wasabeef/Blurry
500px-android-blur:https://github.com/500px/500px-android-blur
EtsyBlur:https://github.com/Manabu-GT/EtsyBlur