package net.robinx.lib.blur.widget;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.view.View;
import android.view.Window;
/**
* Created by Robin on 2016/7/23 12:20.
*/
public class BlurDrawable extends ColorDrawable {
private int mDownSampleFactor;
private final View mBlurredBgView;
private int mBlurredViewWidth, mBlurredViewHeight;
private boolean mDownSampleFactorChanged;
private Bitmap mBitmapToBlur, mBlurredBitmap;
private Canvas mBlurringCanvas;
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
private float offsetX;
private float offsetY;
private int mBlurRadius = 1;
private static boolean enabled;
private int mOverlayColor = Color.TRANSPARENT;
private float cornerRadius = 0;
private final Path path = new Path();
/**
* will only initial once when class loaded
*/
static {
enabled = (Build.VERSION.SDK_INT >= 19);
}
private final RectF rectF = new RectF();
public BlurDrawable(View blurredBgView) {
this.mBlurredBgView = blurredBgView;
if (enabled) {
initializeRenderScript(blurredBgView.getContext());
}
setOverlayColor(mOverlayColor);
}
/**
* used for dialog/fragment/popWindow/dialog
*
* @param activity the blurredView attached
* @see #setDrawOffset
*/
public BlurDrawable(Activity activity) {
this(activity.getWindow().getDecorView());
}
/**
* Set for window
*
* @param blurredWindow another window,void draw self(may throw stackoverflow)
*/
public BlurDrawable(Window blurredWindow) {
this(blurredWindow.getDecorView());
}
@TargetApi(17) public void setBlurRadius(int radius) {
if (!enabled) {
return;
}
this.mBlurRadius = radius;
mBlurScript.setRadius(radius);
}
@TargetApi(17) public void setDownSampleFactor(int factor) {
if (!enabled) {
return;
}
if (mDownSampleFactor != factor) {
mDownSampleFactor = factor;
mDownSampleFactorChanged = true;
}
}
public void setCornerRadius(float radius) {
this.cornerRadius = radius;
}
/**
* set both for blur and non-blur
*/
public void setOverlayColor(int color) {
mOverlayColor = color;
setColor(color);
}
@TargetApi(17) private void initializeRenderScript(Context context) {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
setBlurRadius(mBlurRadius);
setDownSampleFactor(8);
}
private boolean prepare() {
final int width = mBlurredBgView.getWidth();
final int height = mBlurredBgView.getHeight();
if (mBlurringCanvas == null
|| mDownSampleFactorChanged
|| mBlurredViewWidth != width
|| mBlurredViewHeight != height) {
mDownSampleFactorChanged = false;
mBlurredViewWidth = width;
mBlurredViewHeight = height;
int scaledWidth = width / mDownSampleFactor;
int scaledHeight = height / mDownSampleFactor;
scaledWidth = scaledWidth - scaledWidth % 4 + 4;
scaledHeight = scaledHeight - scaledHeight % 4 + 4;
if (mBlurredBitmap == null
|| mBlurredBitmap.getWidth() != scaledWidth
|| mBlurredBitmap.getHeight() != scaledHeight) {
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (mBitmapToBlur == null) {
return false;
}
mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (mBlurredBitmap == null) {
return false;
}
}
mBlurringCanvas = new Canvas(mBitmapToBlur);
mBlurringCanvas.scale(1f / mDownSampleFactor, 1f / mDownSampleFactor);
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
}
return true;
}
@TargetApi(17) private void blur(Bitmap mBitmapToBlur, Bitmap mBlurredBitmap) {
if (!enabled) {
return;
}
mBlurInput.copyFrom(mBitmapToBlur);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(mBlurredBitmap);
/*this.mBlurredBitmap = StackBlur.blurRenderScript(mBlurredBgView.getContext(),mBitmapToBlur,16,false);*/
/*this.mBlurredBitmap = StackBlur.blurNatively(mBitmapToBlur,16,false);*/
}
/**
* force enable blur, however it will only works on API 17 or higher
* if your want to support more, use Support RenderScript Pack
*/
public void setEnabled(boolean enabled) {
BlurDrawable.enabled = enabled && (Build.VERSION.SDK_INT >= 17);
}
@TargetApi(17)
public void onDestroy() {
if (!enabled) {
return;
}
if (mRenderScript != null) {
mRenderScript.destroy();
}
}
@Override public void draw(Canvas canvas) {
if (cornerRadius != 0){
path.reset();
rectF.set(0, 0, canvas.getWidth(), canvas.getHeight());
path.addRoundRect(rectF,cornerRadius, cornerRadius, Path.Direction.CCW);
canvas.clipPath(path);
}
if (enabled) {
drawBlur(canvas);
}
//draw overlayColor
super.draw(canvas);
}
@TargetApi(17) private void drawBlur(Canvas canvas) {
if (prepare()) {
mBlurredBgView.draw(mBlurringCanvas);
blur(mBitmapToBlur, mBlurredBitmap);
canvas.save();
canvas.translate(mBlurredBgView.getX() - offsetX, mBlurredBgView.getY() - offsetY);
canvas.scale(mDownSampleFactor, mDownSampleFactor);
canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
canvas.restore();
}
}
/**
* set the offset between top view and blurred view
*/
@TargetApi(17)
public void setDrawOffset(float x, float y) {
this.offsetX = x;
this.offsetY = y;
}
public View getBlurredBgView() {
return mBlurredBgView;
}
}