对于不需要看废话的请直接Show me the code
对于模糊处理,一般的解决方案有四种:
- Java实现的算法处理;
- NDK实现的算法处理;
- RenderScript处理;
- openGL处理;
这四种方案的性能考虑的话,一般来讲应该是: 1 < 3 < 2 < 4 (2、3的性能非常接近),不过当属RenderScript方式最为简便(很显然嘛,平台无关性)。
RenderScript是Android自SDK17提供的一套平台无关计算密集脚本,它使用C99语法脚本,可以实现高性能的计算,而其包含大量内联函数,其中就包括了我们今天用到的:ScriptIntrinsicBlur,这是一个高斯模糊处理内联函数,一般系统用来进行阴影的计算处理。
上面有提到RenderScript是自SDK17引入的,但是Android 团队为我们提供了RenderScript support library,而起在Gradle-base的项目中的引入也极其简单,仅需要在module build.gradle设置以下11、12两行即可:
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "org.ligboy.backsound"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
renderscriptTargetApi 20
renderscriptSupportModeEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
接下来简单说说 ScriptIntrinsicBlur,使用它虽然很方便,但是也是有弊端的,模糊radius不能超过25.0f,不过我们可以通过缩小原图抽样的方式变相的进行扩大半径。
Glide是一个优秀的图片加载缓存处理等一系列功能的框架,它优雅而且使用渐变,扩展也很容易,Glide就提供了自定义Transformation的方式进行扩展图片的处理
到了这里就是通过自定义Transformation的方式,详细的这里就不赘述了,感兴趣可以查看Glide官方文档:Transformation。需要特别提到的是,每个Transformation都有一个String getId()的回调,该ID用于在对处理后的图片进行缓存时的文件命名,这样当下次相同图片使用相同Transformation时,则无需任何实际处理,直接读取缓存文件,所以我们实现时的ID需要同时关联所有的调整参数,参数改变的ID也应该相应改变。下面就是我自定义的BlurTransformation
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.support.annotation.FloatRange;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.request.target.Target;
/**
* Georgia Blur Transformation
* <p>
* @author Ligboy.Liu ligboy@gmail.com.
*/
public class BlurTransformation extends BitmapTransformation {
private static final String ID = "org.ligboy.glide.BlurTransformation";
public static final float DEFAULT_RADIUS = 25.0f;
public static final float MAX_RADIUS = 25.0f;
private static final float DEFAULT_SAMPLING = 1.0f;
private Context mContext;
private float mSampling = DEFAULT_SAMPLING;
private float mRadius;
private int mColor;
public static class Builder {
private Context mContext;
private float mRadius = DEFAULT_RADIUS;
private int mColor = Color.TRANSPARENT;
public Builder(Context mContext) {
this.mContext = mContext;
}
public float getRadius() {
return mRadius;
}
public Builder setRadius(float radius) {
mRadius = radius;
return this;
}
public int getColor() {
return mColor;
}
public Builder setColor(int color) {
mColor = color;
return this;
}
public BlurTransformation build() {
return new BlurTransformation(mContext, mRadius, mColor);
}
}
/**
*
* @param context Context
* @param radius The blur's radius.
* @param color The color filter for blurring.
*/
public BlurTransformation(Context context, @FloatRange(from = 0.0f) float radius, int color) {
super(context);
mContext = context;
if (radius > MAX_RADIUS) {
mSampling = radius / 25.0f;
mRadius = MAX_RADIUS;
} else {
mRadius = radius;
}
mColor = color;
}
/**
*
* @param context Context
* @param radius The blur's radius.
*/
public BlurTransformation(Context context, @FloatRange(from = 0.0f) float radius) {
this(context, radius, Color.TRANSPARENT);
}
public BlurTransformation(Context context) {
this(context, DEFAULT_RADIUS);
}
/**
* Transforms the given {@link Bitmap} based on the given dimensions and returns the transformed
* result.
* <p/>
* <p>
* The provided Bitmap, toTransform, should not be recycled or returned to the pool. Glide will automatically
* recycle and/or reuse toTransform if the transformation returns a different Bitmap. Similarly implementations
* should never recycle or return Bitmaps that are returned as the result of this method. Recycling or returning
* the provided and/or the returned Bitmap to the pool will lead to a variety of runtime exceptions and drawing
* errors. See #408 for an example. If the implementation obtains and discards intermediate Bitmaps, they may
* safely be returned to the BitmapPool and/or recycled.
* </p>
* <p/>
* <p>
* outWidth and outHeight will never be {@link Target#SIZE_ORIGINAL}, this
* class converts them to be the size of the Bitmap we're going to transform before calling this method.
* </p>
*
* @param pool A {@link BitmapPool} that can be used to obtain and
* return intermediate {@link Bitmap}s used in this transformation. For every
* {@link Bitmap} obtained from the pool during this transformation, a
* {@link Bitmap} must also be returned.
* @param toTransform The {@link Bitmap} to transform.
* @param outWidth The ideal width of the transformed bitmap (the transformed width does not need to match exactly).
* @param outHeight The ideal height of the transformed bitmap (the transformed heightdoes not need to match
*/
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
boolean needScaled = mSampling == DEFAULT_SAMPLING;
int originWidth = toTransform.getWidth();
int originHeight = toTransform.getHeight();
int width, height;
if (needScaled) {
width = originWidth;
height = originHeight;
} else {
width = (int) (originWidth / mSampling);
height = (int) (originHeight / mSampling);
}
//find a re-use bitmap
Bitmap bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
if (mSampling != DEFAULT_SAMPLING) {
canvas.scale(1 / mSampling, 1 / mSampling);
}
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
PorterDuffColorFilter filter =
new PorterDuffColorFilter(mColor, PorterDuff.Mode.SRC_ATOP);
paint.setColorFilter(filter);
canvas.drawBitmap(toTransform, 0, 0, paint);
// TIPS: Glide will take care of returning our original Bitmap to the BitmapPool for us,
// we needn't to recycle it.
// toTransform.recycle(); <--- Just for tips. by Ligboy
RenderScript rs = RenderScript.create(mContext);
Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
blur.setInput(input);
blur.setRadius(mRadius);
blur.forEach(output);
output.copyTo(bitmap);
rs.destroy();
if (needScaled) {
return bitmap;
} else {
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, originWidth, originHeight, true);
bitmap.recycle();
return scaled;
}
}
/**
* A method to get a unique identifier for this particular transformation that can be used as part of a cache key.
* The fully qualified class name for this class is appropriate if written out, but getClass().getName() is not
* because the name may be changed by proguard.
* <p/>
* <p>
* If this transformation does not affect the data that will be stored in cache, returning an empty string here
* is acceptable.
* </p>
*
* @return A string that uniquely identifies this transformation.
*/
@Override
public String getId() {
StringBuilder sb = new StringBuilder(ID);
sb
.append('-').append(mRadius)
.append('-').append(mColor);
return sb.toString();
}
}
使用起来也是非常简单:
Glide.with(MainActivity.this).load(imageFile.getUrl())
.transform(new BlurTransformation(MainActivity.this, 100))
.crossFade()
.into(mBackgroundImageView);