package com.github.miao1007.animewallpaper.ui.widget.blur; 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.support.annotation.ColorInt; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.view.View; import android.view.Window; /** * Created by leon on 2/8/16. * port Blur from <a href="https://github.com/500px/500px-android-blur">500px-android-blur</a> * * Real time Blur * API 17 and above: use blur * * API 17 lower: * @see #setColor(int) */ 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 static boolean enabled; private int mOverlayColor = Color.argb(200, 0xec, 0xec, 0xec); 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(@NonNull View mBlurredBgView) { this.mBlurredBgView = mBlurredBgView; if (enabled) { initializeRenderScript(mBlurredBgView.getContext()); } setOverlayColor(mOverlayColor); } /** * used for dialog/fragment/popWindow/dialog * * @param activity the blurredView attached * @see #setDrawOffset */ public BlurDrawable(Activity activity) { this(activity.getWindow()); } /** * Set for window * * @param blurredWindow another window,void draw self(may throw stackoverflow) */ public BlurDrawable(Window blurredWindow) { this(blurredWindow.getDecorView()); } @TargetApi(17) private void setBlurRadius(@IntRange(from = 0, to = 25) int radius) { if (!enabled) { return; } mBlurScript.setRadius(radius); } @TargetApi(17) public void setDownSampleFactor(@IntRange(from = 0) 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(@ColorInt 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)); //设置blur半径, iOS中默认为12px setBlurRadius(12); //图片缩放等级,缩放越大越节约性能,理论要在100px^2以内 setDownSampleFactor(8); } /** * 相当于一个单例的初始化 */ private boolean prepare() { //assume a 1080 x 1920 RecyclerView 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; // The following manipulation is to avoid some RenderScript artifacts at the edge. // 136 x 244 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; } } //创建了一个136 x 244的画板 //当画板调用draw是,将画到mBitmapToBlur上 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; } /** * 渲染任务,可以在16ms完成,可以调用多核 * 将mBitmapToBlur渲染为mBlurredBitmap输出 */ @TargetApi(17) private void blur(Bitmap mBitmapToBlur, Bitmap mBlurredBitmap) { if (!enabled) { return; } //类似于c中的alloc,这里是栈内存,这样就把bitmap放入了c的栈中 mBlurInput.copyFrom(mBitmapToBlur); //滤镜加入输入源 mBlurScript.setInput(mBlurInput); //滤镜进行渲染并输出到output,类似于DSP mBlurScript.forEach(mBlurOutput); //将栈内存复制到bitmap mBlurOutput.copyTo(mBlurredBitmap); } /** * 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()) { //在1920x1080中,只画一个大小为 136 x 244 的RecyclerView,这个View绘制了两次 //类似于开发者选项中的多显示输出 //将bitmaptoblur进行赋值 mBlurredBgView.draw(mBlurringCanvas); //进行模糊渲染,生成mBlurredBitmap blur(mBitmapToBlur, mBlurredBitmap); // canvas.save(); //这里的是dx,正好与坐标是相反的 canvas.translate(mBlurredBgView.getX() - offsetX, mBlurredBgView.getY() - offsetY); //实际输出的只有 136 x 244的像素,缩放后就和当前view一样大了 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 getmBlurredBgView() { return mBlurredBgView; } }