首页 > 其他分享 >Android图片进行高斯模糊处理/毛玻璃效果

Android图片进行高斯模糊处理/毛玻璃效果

时间:2023-10-26 19:05:49浏览次数:37  
标签:高斯 seekBar Bitmap SeekBar 线程 Override Android public 毛玻璃

android中实现毛玻璃效果的方法比较多, 有用java实现图片处理算法的, 也有把算法用c/c++实现并用jni调用的, 而实现毛玻璃的开源库在github上也有不少.
其实google的官方sdk中也为我们提供了这样的工具, 本着能用官方尽量不自己实现,能自己实现尽量不用第三方的原则, 官方的实现方式当然是要尝试一下的.
同时, 本例中的拖拽进度和图片的处理以及回显是通过RxJava放在不同的进程中处理的, 如果不熟悉Rx框架可以补一下, RxJava用于异步操作以及事件流的处理非常好用.
上图:

Android图片进行高斯模糊处理/毛玻璃效果_毛玻璃

使用官方api实现高斯模糊处理我们需要用到android.renderscript.ScriptIntrinsicBlur类以及方法, 而这个类只能在api17(android4.2)及以上的版本中才能使用, 在android4.0版本仍在大量使用的今天,这样当然不能满足我们的需求, 而google也为这个类提供了向下兼容的方法: 在Module的build文件中增加以下属性:

android{
    ...
    defaultConfig{
         ...
        renderscriptTargetApi 19
        renderscriptSupportModeEnabled true
    }
}

这样在AndroidStudio中就加入的V8包中的RenderScript的支持, 如果是使用Eclipse开发环境则需要手动把v8的jar包以及so文件加入到lib中, 如今google已经放弃了对eclipseADT的支持, 也没必要抱着Eclipse不放了.

这时我们只需要将RenderScript以及ScriptIntrinsicBlur和其他类的引用改为v8中的即可完成低版本的兼容, 如:android.support.v8.renderscript.ScriptIntrinsicBlur

现在就可用使用官方的api来进行图片的处理了, 下面记录的api的使用方法以及每个注释:

/**
     * 处理bitmap为高斯模糊图片
     * @param context 上下文
     * @param image   图片源
     * @param radius  模糊程度 0到25之间
     * @param scale   图片缩放比例, 该值越小越节省内存,模糊程度越敏感,0到1之间
     * @return 模糊的图片
     */
    public static Bitmap blurBitmap(Context context, Bitmap image, float radius, float scale) {
        // 计算图片缩小后的长宽
        int width = Math.round(image.getWidth() * scale);
        int height = Math.round(image.getHeight() * scale);
        // 将缩小后的图片做为预渲染的图片。
        Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
        // 创建一张渲染后的输出图片。
        Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
        // 创建RenderScript内核对象
        RenderScript rs = RenderScript.create(context);
        // 创建一个模糊效果的RenderScript的工具对象
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
        // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
        Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
        Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
        // 设置渲染的模糊程度, 25f是最大模糊度
        blurScript.setRadius(radius);
        // 设置blurScript对象的输入内存
        blurScript.setInput(tmpIn);
        // 将输出数据保存到输出内存中
        blurScript.forEach(tmpOut);
        // 将数据填充到Allocation中
        tmpOut.copyTo(outputBitmap);
        return outputBitmap;
    }

将上面这个方法加入到项目中即可完美得处理图片的高斯模糊效果了, 而官方api的使用方法到这里也基本介绍结束. 接下来会对开篇的拓展实现进行记录.

使用RxJava对SeekBar事件流进行缓存

我们知道图片的处理是比较耗时的, 将大量的图片处理放在UI线程中必然会造成界面的卡顿, 如果我们使用常规的方法来实现开篇的效果也肯定会遇到这个问题:

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                try {
                    Bitmap bitmap1 = blurBitmap(MainActivity.this, bitmap, 25f*i/100, 0.4f);
                    img_result.setImageBitmap(bitmap1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}
        });

seekBar的onProgressChanged方法是在UI线程中回调的, 而我们的blurBitmap方法传入了原始bitmap并进行高斯模糊处理并返回新的bitmap并更新UI也是在UI线程处理的, 我们拖动seekBar时会有大量的onProgressChanged回调涌入UI线程, 这时候seekBar的卡顿会非常的严重.

既然onProgressChanged是按顺序触发的事件, 那么使用RxJava来把这些事件当做事件流来处理再适合不过了. 首先我们要将seekBar的事件包装成一个Observable, 我们模仿RxBinding对OnSeekBarChangeListener进行封装:

public class SeekBarOnChangeSubscribe implements Observable.OnSubscribe<Float> {
    SeekBar seekBar;
    public SeekBarOnChangeSubscribe(SeekBar seekBar) {
        this.seekBar = seekBar;
    }
    @Override
    public void call(final Subscriber<? super Float> subscriber) {
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onNext(i * 1f);
                }
            }
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}
        });

        //unSubscribe监听,并将seekBar的监听置空
        subscriber.add(new MainThreadSubscription() {
            @Override
            protected void onUnsubscribe() {
                seekBar.setOnSeekBarChangeListener(null);
            }
        });
    }
}

创建一个工具类让我们的封装看起来更加有builder的感觉

public class MyBinder{
        public static Observable<Float> bind(SeekBar seekBar){
             return Observable.create(new SeekBarOnChangeSubscribe(seekBar));
        }
    }

调整我们的监听:

MyBinder.bind(seekBar)
                .map(new Func1<Float, Bitmap>() {
                    @Override
                    public Bitmap call(Float aFloat) {
                        Bitmap bitmap1 = null;
                        float radius = 25f * aFloat / 100;
                        bitmap1 = blurBitmap(MainActivity.this, bitmap, radius, 0.4);
                        return bitmap1;
                    }
                })
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap newImage) {
                        img_result.setImageBitmap(newImage);
                    }
                });

现在我们已经成功的把回调监听改为了RxJava模式的响应试监听 但是这样仍然不满足我们的需求, 因为他本质上和之前是一样的, 阻塞UI线程并调用blurBitmap方法. 我们需要给map和subscribe方法分别指定.observeOn(Schedulers.computation())和.observeOn(AndroidSchedulers.mainThread())的调度线程.

MyBinder.bind(seekBar)
                .observeOn(Schedulers.computation())
                .map(new Func1<Float, Bitmap>() {
                    @Override
                    public Bitmap call(Float aFloat) {
                        Bitmap bitmap1 = null;
                        float radius = 25f * aFloat / 100;
                        bitmap1 = blurBitmap(MainActivity.this, bitmap, radius, 0.4);
                        return bitmap1;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap newImage) {
                        img_result.setImageBitmap(newImage);
                    }
                });

这时候看起来像那么回事了, 跑一下看看如何? 当然, 你会遇到MissingBackpressureException! 为何呢, 我们将图片处理放入子线程并在ui线程更新了UI啊. 问题不是出在这里, 其实当我们在子程序中处理图片时这个子线程依然是处于阻塞状态的, 而UI线程中产生的事件仍在不断的涌入子线程, 这时候RxJava并不知道该如何缓存这些事件, 而我们只需要告诉RxJava一个缓存方式即可. 这里我们使用最普通的缓存方式onBackpressureBuffer(), 这个方法会将来不及处理的事件统统依次缓存到一个队列中, 在子线程中的上一个事件处理完毕后依次进入处理.

贴上最终代码:

MyBinder.bind(seekBar)
                .onBackpressureBuffer()
                .observeOn(Schedulers.computation())
                .map(new Func1<Float, Bitmap>() {
                    @Override
                    public Bitmap call(Float aFloat) {
                        Bitmap bitmap1 = null;
                        float radius = 25f * aFloat / 100;
                        bitmap1 = blurBitmap(MainActivity.this, bitmap, radius, 0.4);
                        return bitmap1;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap newImage) {
                        img_result.setImageBitmap(newImage);
                    }
                });

标签:高斯,seekBar,Bitmap,SeekBar,线程,Override,Android,public,毛玻璃
From: https://blog.51cto.com/NightFarmer/8042246

相关文章

  • Android使用Profiler查看应用内存分析
    内存分析是Profiler中的一个组件,可以帮助我们识别可能会导致应用卡顿、冻结甚至崩溃的内存泄露和内存抖动。可以显示应用内存使用情况实时图表,帮助我们捕获堆转储、强制执行垃圾回收以及跟踪内存的分配情况。打开内存分析步骤:1、依次点击View→ToolWindow→Profiler2、从Profile......
  • 利用滤镜-高斯模糊 制作图片
    原图 效果图 视频网址:https://mooc1-2.chaoxing.com/mooc-ans/mycourse/teacherstudy?chapterId=687030348&courseId=220576162&clazzid=85345057 ......
  • Android压测测试事件行为参数对照表
    一、压测命令参数说明执行参数参数说明颗粒度指标基础参数--throttle<ms>用于指定用户操作间的时延。-s随机数种子,用于指定伪随机数生成器的seed值,如果seed值相同,则产生的时间序列也相同。多用于重测、复现问题。-v指定输出日志的级别,共有3个级别。1)-v:仅提供启动提示、测试完......
  • 悲哀!大厂门槛成了很多Android开发无法企及的追求?这个机会到底怎么获得!
    大厂offer是每个技术人的追求许多程序员都梦想着能够获得大厂的offer,这并不是饭后闲聊的话题,而是每个技术人的追求。像阿里、腾讯、美团、字节跳动、京东等公司的技术氛围和技术规范度,相较于一些创业型公司或小公司,显然要高得多。如果能在这类公司工作几年,对个人能力的提升无疑会非......
  • Android系统SELinux详解
    前言SELinux是一种加强文件安全的一种策略,可以更好地保护我们的Android系统,比如限制系统服务的访问权限、控制应用对数据和系统日志的访问等措施,这样就降低了恶意软件的影响,并且可以防止因代码存在的缺陷而产生的对系统安全的影响。从系统安全方面考虑,SELinux是保护神,但是从软件开......
  • 最好用的Android APK第三方下载站,替代Google play
    最好用的AndroidAPK第三方下载站,推荐以下7个替代Googleplay方案可通过第三方应用程序下载各种apk历史版本1、APKPure:APKPure 提供:网页、AppAPKPure是知名度很高的免费安卓应用商店,基本上大部分GooglePlay上架的软件都可以在这里找到,但最近也有被屏蔽的倾向。2、APKMirror......
  • H5与Android的调试
    准备工作:PC下载并安装chrome(谷歌)浏览器一台安卓手机(4.4系统以上),用usb线链接电脑,打开开发者模式,且允许WebView进行调试,需新增如下代码:WebView.setWebContentsDebuggingEnabled(true);编译并运行代码chrome浏览器地址栏输入chrome://inspect,进入后点击inspect即进入调试模式(需要......
  • 悲哀!大厂门槛成了很多Android开发无法企及的追求?这个机会到底怎么获得!
    大厂offer是每个技术人的追求许多程序员都梦想着能够获得大厂的offer,这并不是饭后闲聊的话题,而是每个技术人的追求。像阿里、腾讯、美团、字节跳动、京东等公司的技术氛围和技术规范度,相较于一些创业型公司或小公司,显然要高得多。如果能在这类公司工作几年,对个人能力的提升无疑会非......
  • unity打包 android
    第一种:手动把没有的包都放入C:\ProgramFiles\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib 第二种:修改镜像urlC:\ProgramFiles\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools......
  • 基于Android的视频资讯-计算机毕业设计源码+LW文档
    摘 要随着互联网的发展,尤其是视频互联网的发在,越来越多的人喜欢在闲暇的时候通过刷视频来度过自己的闲暇时光,为了让更多的人能够看到跟多有趣的视频,我们开发了本次的基于Android的视频资讯APP。本基于Android的视频资讯APP是根据当前的实际情况开发的,在系统语言选择上我们使用......