在本系列的前面部分,我们已经看过使用RenderScript
来模糊一张图片,并通过一个性能分析技巧解决了此过程的瓶颈。我们发现使用RenderScipt
时,实际的模糊操作得非常快。但还是无法避免Java内存空间与RenderScript
内存空间之间位图编码(Marshalling)带来的开销。在这篇文章里,我们将不通过内存空间编码来实现模糊的功能,看看是否可以得到更好的效果。
首先必须承认,对于各种模糊算法的Java实现,我并没有做过详细的测试。所以很有可能一些其它算法比我挑选的更有效率。我挑选的算法来自StackOverflow上Yahel Bouaziz的回答,他很清楚地相关的模型,并且声称此算法特别快速。有评论说这个算法比其它的方法快得多。
我不会在这里重复这个方法,具体的实现可以参考这个资源
里了。我也不会对不同的模糊算法进行说明,解释Yahel的代码是如何工作的,或者与其它实现进行比较。这些不是实现模糊系列文章的重点。如果想了解更多的信息,这里有一个很好的Java image Processing指南,它使用AWT libraries实现。虽然不要直接在Android上使用,但却是学习不同模糊算法一个不错的起点。
我们使用一种不同的方法来对Yahel的方法进行封装,包括一个TimingLogger
。这样可以方便地评估实现效果:
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 |
; html-script: false ] private void blurJava(Bitmap bkg, View view, int radius) { TimingLogger tl = new TimingLogger(TAG, "blur"); Bitmap overlay = Bitmap.createBitmap( view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888); tl.addSplit("Bitmap.createBitmap()"); Canvas canvas = new Canvas(overlay); tl.addSplit("new Canvas()"); canvas.drawBitmap(bkg, -view.getLeft(), -view.getTop(), null); tl.addSplit("canvas.drawBitmap()"); Bitmap blurred = fastblur(overlay, radius); tl.addSplit("fastblur()"); view.setBackground(new BitmapDrawable( getResources(), blurred)); tl.addSplit("view.setBackground()"); tl.dumpToLog(); } |
运行后能够得到以下效果:
必须同意Yehel的说法,效果看起来好多了。所有关键的运行效果表现如何呢?
1 2 3 4 5 6 7 8 |
; html-script: false ] blur: begin blur: 0 ms, Bitmap.createBitmap() blur: 0 ms, new Canvas() blur: 1 ms, canvas.drawBitmap() blur: 125 ms, fastblur() blur: 0 ms, view.setBackground() blur: end, 126 ms |
使用之前运行RenderScript模糊图像的同一台设备进行测试,确保程序使用同一个基准测试。在节省了RenderScipt内存空间的编码开销后,单是模糊操作的时间就比包括信号编码RenderScript方法操作时间长了两到三倍。但这并不表明Yehel的方法不高效,这是由于没有直接在GPU上运行,而GPU恰好适合执行这种繁重的计算任务。
这个结果清晰地表明了当执行这类操作时,RenderScript非常有希望带来实现效果的显著提升。而当内存之间的解码开销很大时,由此得到的效果会让人相信这里的开销相当值得。在下一篇文章里,我们将深入讨论当运行动画时的帧速率,并探究是否可以让模糊背景的过程更加流畅。
本文的源码可以在这里找到。