从 iOS 7 开始 Apple 从 拟物化 过渡到了 扁平化 的设计风格,同时也搭配使用了 毛玻璃风格 当作背景效果,不得不说十分惊艳,很有当时pc上 Widows Vista 和 OS X Yosemite 的味道,在那以后,Google 也从 Android L(5.0)开始使用了 原质化设计(Material Design) 设计语言,与 Microsoft 的 Metro 那种纯扁平化风格看似很相像,但实则由于引用了 Z轴 的概念,使其有了阴影和立体感,传达了 响应式交互 的设计理念。说到这里有一些跑题,由于笔者对设计美学很感兴趣,因此对这些平台都稍微了解一些皮毛。今天就来研究一下如何在 Android 上实现高斯模糊效果。html
Java : FastBlur.java ,应用很是普遍的 StackBlur 模糊算法实现代码,效率最低java
C++ :两种实现,1:标准高斯模糊算法 2:均值模糊,效率中等android
Android : RenderScript ,用来在 Android 上编写高性能代码的一种语言(使用C99标准,运行时机器再次优化编译, 能够均衡的运行在多个CPU 和 GPU上,有一个半径限制小于25的限制),效率最高git
由于效果的实现是基于 Java 的,因此有必要先来了解一下方法如何使用。github
public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap)
能够看出,使用方法很是简单,传入待虚化的bitmap、虚化程度(通常为8)、是否重用flag,最后返回模糊后的bitmap。算法
但若是直接把一张大图传入进行虚化,很容易就会产生OOM内存溢出,那就意味着我只能虚化小图,这样才能防止内存溢出。可是我并不想换其余图,那么,咱们就应该把这张图缩小。网络
平时咱们对图片缩小,必然会带来很明显的清晰度的损失,但高斯模糊自己的目的就是要实现模糊的效果,所以实际上的效果差异不大,几乎能够忽略。框架
同时因为图片缩小后再进行模糊处理,须要处理的像素点和半径都变小,从而使得模糊处理速度加快。maven
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) {}
咱们能够利用Bitmap的 createScaledBitmap()
方法来进行bitmap的缩放。其中前三个参数很明显,其中宽高咱们能够选择为原图尺寸的1/5;第四个filter是指缩放的效果,filter为true则会获得一个边缘平滑的bitmap,反之,则会获得边缘锯齿、pixelrelated的bitmap。这里咱们要对缩放的图片进行虚化,因此无所谓边缘效果,filter=false。ide
因此,咱们要使用
int scaleRatio = 5;// 缩放比例 此处表明1/5 int blurRadius = 8;// 虚化程度 Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, originBitmap.getWidth() / scaleRatio, originBitmap.getHeight() / scaleRatio, false); Bitmap blurBitmap = FastBlur.doBlur(scaledBitmap, blurRadius, true); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setImageBitmap(blurBitmap);
能够获得以下效果:
从图中能够看出,首先能够肯定思路是对的;而后,能够看出毛玻璃效果还不是特别的明显。为了获得如iOS那样的虚化效果,咱们有两种方法:
增大scaleRatio缩放比,使用更小的bitmap去虚化能够获得更好的模糊效果,并且有利于占用内存的减少;
增大blurRadius,能够获得更高程度的虚化,不过会致使CPU更加intensive
这里笔者经过增大缩放比来实验。
scaleRatio = 10
scaleRatio = 20
经过上面对比图咱们能够找出最适合本身的虚化效果。
RenderScript 主要 在Android中的对图形进行处理,RenderScript 采用C99语法进行编写,主要优点在于性能较高。在 API 11 的时候被加入到 Android 中。同时,Google提供了
android.support.v8.renderscript
兼容包,可以实现更低版本的兼容。
RenderScript 提供了一个用于实现高斯模糊的封装类 ScriptIntrinsicBlur ,由于在 API 17 后才正式适配到 Android ,因此在不使用兼容包的状况下只能兼容到4.2的设备。可是,咱们有兼容包啊向下兼容不是梦。
方法很简单,只需在build.gradle中加入:
defaultConfig { .... // 就是这么简单 renderscriptTargetApi 19 renderscriptSupportModeEnabled true }
另外因为一些厂商会深度定制Android系统,因此一些必要的依赖文件会被他们直接去掉,这致使一些型号的设备上调用RenderScriptd
的部分方法时会报错。遇到这种兼容问题的话,须要加上这些可能丢失的文件。
其实也简单,打开android_sdk/build-tools/选择19以上版本/renderscript/lib/packaged
咱们能够看见3个包含.so文件的文件夹。
直接复制这三个文件加到项目工程的 jniLibs 包下,没有的话去建一个。
若是首次建立 jniLibs 文件夹,还须要在 build.gradle 的 android{}
中加入:
sourceSets { main { jniLibs.srcDirs = ['jniLibs'] } }
针对使用的混淆的同窗,须要在混淆中加入:
-keep class android.support.v8.renderscript.** { *; }
将核心实现方法 ScriptIntrinsicBlur 封装成工具类。
import android.support.v8.renderscript.*; // 须要导入v8包,不然没法向下兼容 public class BlurBitmapUtil { /*** * 图片缩放比例 (例如 1/10) */ private static int scaleRatio = 10; /** * 对图片进行高斯模糊 * * @param context 上下文对象 * @param image 须要模糊的图片 * @param blurRadius 模糊半径,因为性能限制,这个值的取值区间为(0至25f) * @return 模糊处理后的图片 */ public static Bitmap blurBitmap(Context context, Bitmap image, @FloatRange(from = 1, to = 25) float blurRadius) { // 计算图片缩小后的长宽 int width = Math.round(image.getWidth() / scaleRatio); int height = Math.round(image.getHeight() / scaleRatio); // 建立一张缩小后的图片作为渲染的图片 Bitmap bitmap = Bitmap.createScaledBitmap(image, width, height, false); // 建立RenderScript内核对象 RenderScript rs = RenderScript.create(context); // 建立一个模糊效果的RenderScript的工具对象,第二个参数Element至关于一种像素处理的算法,高斯模糊的话用这个就好 ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 因为RenderScript并无使用VM来分配内存,因此须要使用Allocation类来建立和分配内存空间 // 建立Allocation对象的时候其实内存是空的,须要使用copyTo()将数据填充进去 Allocation input = Allocation.createFromBitmap(rs, bitmap); // 建立相同类型的Allocation对象用来输出 Type type = input.getType(); Allocation output = Allocation.createTyped(rs, type); // 设置渲染的模糊程度, 25f是最大模糊度 blurScript.setRadius(blurRadius); // 设置blurScript对象的输入内存 blurScript.setInput(input); // 将输出数据保存到输出内存中 blurScript.forEach(output); // 将数据填充到bitmap中 output.copyTo(bitmap); // 销毁它们释放内存 input.destroy(); output.destroy(); blurScript.destroy(); rs.destroy(); type.destroy(); return bitmap; }
使用 RenderScript 增长虚化程度的方法和 FastBlur 同样,有两种方法:
增大scaleRatio缩放比,使用更小的bitmap去虚化能够获得更好的模糊效果,并且有利于占用内存的减少;
增大blurRadius,能够获得更高程度的虚化,不过会致使虚化时间变长
但由于 RenderScript 的自然优点(低级语言, 运行时机器再次优化编译, 能够均衡的运行在多个CPU 和 GPU上),因此这里笔者经过增大虚化程度来实验,缩放比例为 1/10,实际运用时能够根据需求在对虚化程度和缩放比例上采起一个合适的数值。
blurRadius = 5
blurRadius = 15
blurRadius = 25
经过上面对比图咱们能够找出最适合本身的虚化效果。
目前来看,为什么 Google 设置这个25的限制, 缘由应该有两个 :
半径大于25的话耗时就成为了一个瓶颈;
若是想实现大于25的模糊效果,能够经过缩小原图,模糊,再放大来达到一样的效果
以上就是如何用 FastBlur 和 RenderScript 在 Android 上实现和 iOS 同样的高斯模糊效果的简单介绍,虽然在性能上毋庸置疑是 RenderScript 上最好,可是在一些使用场景上 FastBlur 耗时会更短,因此咱们各取所需,根据实际需求去选择使用。
上面说的2种解决方案都是从性能和效率出发的产物,但若是个人需求就是从网络上加载一张图片(好比头像),而后再高斯模糊化当背景,走一遍转换成bitma再将其转换成高斯模糊的流程或许会有一点点麻烦,这里我再提供一种简单快捷的解决方案 —— 基于Glid加载框架去实现一键 加载网络图片→高斯模糊化→展现。
首先在build.gradle中加入图片框架须要的库和图片工具库:
defaultConfig { .... compile 'com.yutianran.maven:super-adapter:1.0.0' compile 'jp.wasabeef:glide-transformations:2.0.2' }
Glide.with(this).load(url).bitmapTransform(new BlurTransformation(this,25)).into(imageView);
须要的参数很分别是
上下文对象
图片url
上下文对象,虚化数值
imageView控件
效果如上,能够看出 glide-transformations 库的虚化效果也是十分不错的,但对图片自己作的缩放应该不是不少,因此在加载速度上会弱于 FastBlur 和 RenderScript ,但做为轻量级图片而言足够了。
相关代码已上传至Github:BlurView,欢迎Star,Fork。