Android模糊图像

简介:
在Android中,我们可以实现很多很酷的处理图片的效果。在2014年某次会议的讲演《图像的魔力》中,我介绍了其中的一部分。其中的一项技术是如何模糊图像,示例代码是使用RenderScript实现的,因为在Android中没有内置的可使用的简单的API。在这个系列中,我们将着眼于RenderScript模糊技术和JAVA实现模糊功能。我们还将进行一些基准测试,以了解每种方案的运行情况,并探讨获取最佳性能的可行方法。
让我们先从实现一个简单的可以运行的例子开始,使用RenderScript!对于没有使用过RenderScript的开发者来说,这是一个让人心生恐惧的设想,因为RenderScript真的是很难,是不是?是的,的确是。不过它也有一些事情真的很简单,而模糊图像就是其中之一。
对于不熟悉RenderScript的人来说,他是在API11中引入的,并且有一个compat库,提供RenderScript给API8及以后的版本。它本质上是一个面向图形的本地计算框架。RenderScript引擎在运行期会选择最合适的处理器(CPU或者GPU核心,多核处理器间可以分解原子操作)来执行请求的操作。本地语法基于C99,与OpenCL, CUDA, and GLSL的API相似。
如果这听起来很可怕,请稍等一下,因为我们正要简化整个过程。因为它是一个框架,允许我们创建自定义的内核来实现过滤与处理,它内置了不少可以使用的内核,其中的一个允许我们模糊图像。
后面的实例代码基于API17及更高版本开发,我已经决定不使用compat库,因为在本文写作的时候,Android Studio还不支持。此外,我们使用的模糊核心在API17后才引入,所以有最小SDK版本为17的需求。
让我们深入到一个简单的例子,这里有一个非常简单的RelativeLayout,包含了一个ImageView,上层还有个TextView:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent"
  6.     android:gravity="center"
  7.     android:orientation="vertical" >
  8.      
  9.     <ImageView
  10.         android:id="@+id/image"
  11.         android:src="@drawable/broadstairs"
  12.         android:layout_width="match_parent"
  13.         android:layout_height="match_parent"
  14.         android:scaleType="matrix"
  15.         android:layout_centerInParent="true"/>

  16.     <TextView
  17.         android:id="@+id/text"
  18.         android:layout_width="wrap_content"
  19.         android:layout_height="wrap_content"
  20.         android:text="@string/hello"
  21.         android:layout_centerHorizontal="true"
  22.         android:textColor="<a href="http://www.jobbole.com/members/android/" rel="nofollow">@android</a>:color/white"
  23.         android:layout_marginTop="300dp"
  24.         android:textStyle="bold"
  25.         android:textSize="48sp"/>
  26. </RelativeLayout>
复制代码



我们想要做的效果是模糊ImageView内位于TextView显示区域的图像,有效的模糊ImageView后的区域。我们最终使用的技术是拿到位于TextView区域的图像副本进行模糊,然后再将模糊后的副本设定为TextView的背景。
我刻意的设计了这种布局,使图像显示实际大小,并从显示屏的左上方开始。这样让随后的位置计算简单些,而且这里讨论的是模糊技术而不是图像定位的数学算法。尝试设定ImageView的属性android:scaleType=”center”,就会发现定位出现错乱。
此处有一个方法,它有3个参数,一个位图(已在ImageView里获得),一个视图(这是我们的TextView,但是该技术适用于任意的视图类型,所以我们将在方法中进行支持),以及一个半径用来控制模糊的程度。
  1. private void blur(Bitmap bkg, View view, float radius) {
  2.     Bitmap overlay = Bitmap.createBitmap(
  3.         view.getMeasuredWidth(),
  4.         view.getMeasuredHeight(),
  5.         Bitmap.Config.ARGB_8888);  
  6.     Canvas canvas = new Canvas(overlay);
  7.     canvas.drawBitmap(bkg, -view.getLeft(),
  8.         -view.getTop(), null);   
  9.     RenderScript rs = RenderScript.create(this);  
  10.     Allocation overlayAlloc = Allocation.createFromBitmap(
  11.         rs, overlay);  
  12.     ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(
  13.         rs, overlayAlloc.getElement());   
  14.     blur.setInput(overlayAlloc);  
  15.     blur.setRadius(radius);  
  16.     blur.forEach(overlayAlloc);  
  17.     overlayAlloc.copyTo(overlay);  
  18.     view.setBackground(new BitmapDrawable(
  19.         getResources(), overlay));   
  20.     rs.destroy();  
  21. }
复制代码

我们要做的第一件事是创建一个新的位图对象,用来保存想要模糊的图像区域的副本。它的大小将会是我们视图的尺寸(2-5行)。
现在我们将空的位图包装到画布中,我们可以在上面绘图(7行)。
接下来的步骤我称为”缓存剪切”,我们拷贝位图中的处于视图显示区域内的那部分图像(9-10行)。
接下来我们必须做的事情是构建一个RenderScript对象上下文,在其中我们执行模糊操作(12行)。
RenderScript是一个本地环境,它在自己的内存空间内运行,所以我们不能简单的传递位图引用,我们需要在JAVA和RenderScript内存区域进行序列化。这使用了一个分配实例来完成,这是在RenderScript内存区域中创建和引用对象的方式,为我们的位图创建一个分配会将位图的内容拷贝的分配区域中(14-15行)。
现在我们创建一个 ScriptIntrinsicBlur实例,它创建合适的RenderScript模糊脚本,并且是脚本对象的Java接口,它让我们能够控制它。(17-18行)。
脚本的输入定义了我们需要执行模糊的位图源(20行)。
现在我们设置半径来控制模糊强度(22行)。
forEach方法执行模糊操作,参数代表了结果的输出位置。我们将它写回源分配地址以减少创建的对象数量(24行)。
现在模糊完成了,但是我们必须把模糊后的图像拷贝回JAVA内存区域(26行)。
最后将模糊后的位图包装到BitmapDrawable内,并设置为视图的背景(28-29行)。
我们已经使用完RenderScript上下文,清理掉。这样做也将自动删除我们的分配(31行)。
这就是我们完整的基本模糊逻辑,但目前还不清楚什么地方和什么时候我们需要调用此方法。不幸的是关于这个问题不能简单的进行回答,所以我们将在接下来的文章中讨论。
平时我喜欢每篇文章都发布相关工作代码,但是在本例中,我们还没达到有一个可运行的完整的端到端的示例的程度。我保证在接下来的文章中进行调整。
via:
原文链接: stylingandroid    翻译: 伯乐在线 - fhdis
译文链接: http://blog.jobbole.com/65353/




我们介绍了使用RenderScript使另一个视图范围内的图片部分模糊。但是实际上,我们并没有深入地调用这个方法来研究图像模糊行为。原因是我们需要在性能方面进行仔细考虑,这篇文章我们会进行更进一步地的探索。

调用这个方法最直白的方法是父布局的onDraw()。有经验的开发者读到这里可能会开始摇头,我们应该保持onDraw方法的实现尽可能有效。以前的文章中的代码包括创建对象、位图操作和切换到renderScript上下文。其中,OnDraw会降低帧速率。你可以不相信我的做法,但是可以通过测量并证明它是有效的。在后面的系列中,我们就会这样做。
如果布局是静态的(即我们的布局不包含任何动画),在布局时就不会改变待模糊的位置和范围。只有在布局改变时执行该操作才有意义,但前提是布局的所有视图大小和位置根据布局变化测量和计算过。这里有一个非常实用的技巧,可以注册一个OnGlobalLayoutListener监听函数。当布局发生改变的时候会调用onGlobalLayout()方法。当我们收到布局已经改变的通知时,注册的OnPreDrawListener监听函数的onPreDraw()方法会被调用每当执行onDraw方法。我们要做的第一件事情就是取消注册onPreDraw()方法,这样只有在布局改变的时候才会被调用,而不是每次onDraw方法触发时都调用。下面可以执行模糊方法,从这个方法的返回值很重要,使用它可以让我们放弃onDraw操作,重复之前的布局。这对在回调函数中修改布局非常有帮助,但是这里不需要这么做。所以返回true,继续绘制。
我们的Activity代码如下:
  1. public class MainActivity extends Activity {
  2.     private ImageView mImage;
  3.     private TextView mText;
  4.   
  5.     private OnPreDrawListener mPreDrawListener =
  6.         new OnPreDrawListener() {
  7.   
  8.         @Override
  9.         public boolean onPreDraw() {
  10.             ViewTreeObserver observer =  mText.getViewTreeObserver();
  11.             if(observer != null) {
  12.                 observer.removeOnPreDrawListener(this);
  13.             }
  14.             Drawable drawable = mImage.getDrawable();
  15.             if (drawable != null &&
  16.                 drawable instanceof BitmapDrawable) {
  17.                 Bitmap bitmap =
  18.                     ((BitmapDrawable) drawable).getBitmap();
  19.                 if (bitmap != null) {
  20.                     blur(bitmap, mText, 25);
  21.                 }
  22.             }
  23.             return true;
  24.         }
  25.     };
  26.   
  27.     private OnGlobalLayoutListener mLayoutListener =
  28.         new OnGlobalLayoutListener() {
  29.   
  30.         @Override
  31.         public void onGlobalLayout() {
  32.             ViewTreeObserver observer =   mText.getViewTreeObserver();
  33.             if(observer != null) {
  34.                 observer.addOnPreDrawListener(
  35.                     mPreDrawListener);
  36.             }
  37.         }
  38.     };
  39.   
  40.     /**
  41.      * Called when the activity is first created.
  42.      */
  43.     @Override
  44.     public void onCreate(Bundle savedInstanceState) {
  45.         super.onCreate(savedInstanceState);
  46.         setContentView(R.layout.main);
  47.         mImage = (ImageView) findViewById(R.id.image);
  48.         mText = (TextView)findViewById(R.id.text);
  49.         if (mImage != null && mText != null) {
  50.             ViewTreeObserver observer =
  51.                 mText.getViewTreeObserver();
  52.             if (observer != null) {
  53.                 observer.addOnGlobalLayoutListener(
  54.                         mLayoutListener);
  55.             }
  56.         }
  57.     }
  58.   
  59.     private void blur(Bitmap bkg, View view, float radius) {
  60.     ....
  61.     }
  62. }
复制代码
在父布局覆盖onDraw方法的优点是,可以在布局层次上任意附加OnPreDrawListener方法。因此需要自定义一个布局,这个布局是标准布局的子类,这样就可以覆盖onDraw方法。使用predrawlistener意味着可以非常容易在任意布局中添加。
最后,模糊图片实现如下:

在下一篇文章中,我会更深入地回答为什么要避免在onDraw中执行模糊操作,并且还会介绍有用的性能测量工具。
这篇文章的代码在这里

via:
原文链接: stylingandroid   翻译: 伯乐在线- fhdis

目录
相关文章
|
3月前
|
开发工具 Android开发 开发者
Android UI设计: 解释Android的Nine-Patch图像是什么,它用于什么目的?
Android UI设计: 解释Android的Nine-Patch图像是什么,它用于什么目的?
31 4
|
4月前
|
XML 前端开发 Java
Android App开发图像加工中卡片视图CardView和给图像添加装饰的讲解以及实战(附源码 简单易懂)
Android App开发图像加工中卡片视图CardView和给图像添加装饰的讲解以及实战(附源码 简单易懂)
51 0
|
4月前
|
Android开发
Android Studio入门之图像显示解析及实战(附源码 超详细必看)(包括图像视图、图像按钮、同时展示文本与图像)
Android Studio入门之图像显示解析及实战(附源码 超详细必看)(包括图像视图、图像按钮、同时展示文本与图像)
71 1
|
4月前
|
XML Java Android开发
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
40 0
|
4月前
|
XML 算法 Java
Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)
Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)
111 0
|
4月前
|
XML 安全 Java
Android Studio App开发之绘制简单的动画图像(附源码,简单易懂)
Android Studio App开发之绘制简单的动画图像(附源码,简单易懂)
52 1
|
XML 缓存 API
Android 垃圾分类APP(四)垃圾分类之图像输入
Android 垃圾分类APP(四)垃圾分类之图像输入
243 0
Android 垃圾分类APP(四)垃圾分类之图像输入
|
Java API Android开发
❤️Android 12 高斯模糊-RenderEffect❤️
Android 12 高斯模糊 新功能:更易用的模糊、彩色滤镜等特效 。 新的 API 让你能更轻松地将常见图形效果应用到视图和渲染结构上。 使用 RenderEffect 将模糊、色彩滤镜等效果应用于 RenderNode 或 View。 使用新的 Window.setBackgroundBlurRadius() API 为窗口背景创建雾面玻璃效果, 使用 blurBehindRadius 来模糊窗口后面的所有内容。 咱们一个一个玩。
1278 0
❤️Android 12 高斯模糊-RenderEffect❤️
|
API Android开发
【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )
【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )
138 0
【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )
|
Android开发
【Android 内存优化】Bitmap 图像尺寸缩小 ( 考虑像素密度、针对从不同像素密度资源中解码对应的 Bitmap 对象 | inDensity | inTargetDensity )
【Android 内存优化】Bitmap 图像尺寸缩小 ( 考虑像素密度、针对从不同像素密度资源中解码对应的 Bitmap 对象 | inDensity | inTargetDensity )
243 0