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

译文链接:
http://blog.jobbole.com/65353/

时间: 2024-11-10 00:47:47

Android模糊图像的相关文章

Android高级模糊技术

转自http://www.eoeandroid.com/forum.php?mod=viewthread&tid=330350 模糊五帖 Android模糊图像教程(1) http://www.eoeandroid.com/thread-330348-1-1.html Android模糊图像教程(2) http://www.eoeandroid.com/thread-330349-1-1.html Android模糊图像教程(3) http://www.eoeandroid.com/thread

HTC One (M8) 评测 - 不输于当前任何一部 Android 手机

class="post_content" itemprop="articleBody"> 尽管急于求变,但 HTC 没有改变 HTC One 对于产品的坚持.3 月底,HTC 在伦敦和纽约发布了 2014 年的旗舰机型-- HTC One (M8),作为 HTC 的扛鼎之作,升级版的 M8 究竟能否承载消费者的期待和 HTC 复兴的愿景?我们的评测或许能给你一个初步的判断-- 基本参数 5 英寸 1080P 屏幕 2.5 GHz 高通骁龙 801 处理器 2G

Android Studio 在 win7 下的安装和设置

首先完成android studio下载 http://developer.android.com/sdk/installing/studio.html 其次下载jdk1.7.0_01,并且完成安装: 下面开始进行安装和设置: 由于studio支持系统位数是64位,而我自己所用电脑是32位的,所以安装完成以后出现启动不了,解决方法如下: 用文本工具打开studio.bat 其中找到 SET BITS=IF EXIST "%JRE%\lib\amd64" SET BITS=64 把IF

Android 4.0设计规范10大改变

在拜读和翻译了Android design设计指导后,对比Android 4.0与Android2.3及之前版本的app设计指导,总结了Android 4.0设计的10大改变: 1. 导航栏 (详见模式PATTERNS>导航Navigation) 由之前的物理按键导航(返回.菜单.搜索.主页)变成了嵌入屏幕的虚拟按键(返回.主页.最近任务).   左侧为早期有4个物理按键的手机,右侧为新版只有3个虚拟按钮的手机 把菜单项和搜索项从导航栏去掉,把之前通过长按主页键才出现的最近任务直接展示在导航栏中

拉近与Android的差距:为iOS5设计消息通知

  通知中心作为iOS5的重大更新内容之一,可以将用户的iOS设备里的所有的通知集中放在一个地方,大大方便用户查看和管理.如果用户收到一封新邮 件.一条短信,或者是一个添加好友的请求,无论在任何界面下(包括游戏等全屏应用),用户都可以通过从屏幕顶部向下滑,将通知中心"拉下来",用户可以在 这里查看到所有的通知.所以苹果在iOS5的介绍中说到通知中心是你随时掌握最新生活资讯的绝佳途径. 可能出现通知的场景 一般在下面的场景中iOS应用程序会通知用户有有意思的事情发生了: 1. 收到一条消

Android界面与交互设计原则:以用户为中心

译者按: 在iOS HIG已经强大经典了N年之后,Android终于推出了一套比较系统的HIG(大概是为了配合Android 4.0 Ice Cream Sandwich).仔细比较两套HIG的"设计原则"部分,发现完全是截然不同的两种风格.iOS HIG走的是更专业型的路线,描述严谨且有不少的专业词汇(比如Metaphors.Consistency之类的).而Android则显得亲民许多,不仅描述方式简要易懂,配图鲜明直观,甚至还用了"me"作为了一系列要点的标题

串口-如何通过android截取收银机的打印数据,组装后上传到后台服务器,然后把数据传到打印机打印小票?

问题描述 如何通过android截取收银机的打印数据,组装后上传到后台服务器,然后把数据传到打印机打印小票? 如题.举个例子,收银机连接打印机实现收银.打印小票,现在想在中间接个硬件Android系统,截取打印数据上传到后台服务器,不对现有收银系统改造,不能影响正常收银. 注:该硬件两端通过串口/并口/usb连接收银机与打印机 解决方案 用小票打印机打印 解决方案二: 这种需要抓包的,安卓不可能.... 解决方案三: 这个你需要截取他的网络包就行了.

Android上webview界面切换动画效果

使用Android上的webview控件时需要跳转到下一个html时,要求当前界面缓缓的向左移动,下一个html界面缓缓的从右边出现.这与常规动画不同,一般方式将无法制作出动画.主要实现方法可以先保存上一个网页的快照,与将要跳转的页面结合起来,制作相关动画. 下面是主要代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 4

android webview定制contextmenu

问题描述 android webview定制contextmenu 10C 如何实现图种的webview的上下文菜单.需求是获取选中的内容,进行操作. 解决方案 http://www.2cto.com/kf/201310/248762.html 解决方案二: android的ContextMenuAndroid ContextMenuandroid ContextMenu 解决方案三: webview里的问题是可以实现你说的功能,不需要单独设置. 解决方案四: 你把属性设置对就不会错了 解决方案