❤️Android 12 高斯模糊-RenderEffect❤️

简介: Android 12 高斯模糊 新功能:更易用的模糊、彩色滤镜等特效 。 新的 API 让你能更轻松地将常见图形效果应用到视图和渲染结构上。使用 RenderEffect 将模糊、色彩滤镜等效果应用于 RenderNode 或 View。使用新的 Window.setBackgroundBlurRadius() API 为窗口背景创建雾面玻璃效果,使用 blurBehindRadius 来模糊窗口后面的所有内容。 咱们一个一个玩。

🔥 Android 12 高斯模糊


       新功能:更易用的模糊、彩色滤镜等特效 。


       新的 API 让你能更轻松地将常见图形效果应用到视图和渲染结构上。


  • 使用 RenderEffect 将模糊、色彩滤镜等效果应用于 RenderNode 或 View


  • 使用新的 Window.setBackgroundBlurRadius() API 为窗口背景创建雾面玻璃效果,


  • 使用 blurBehindRadius 来模糊窗口后面的所有内容。


       咱们一个一个玩。


🔥 RenderEffect


💥 实现效果


    private void setBlur(){
        View.setRenderEffect(RenderEffect.createBlurEffect(3, 3, Shader.TileMode.REPEAT));
        ...
    }


使用特别简单,走你。


🌀 X 轴的模糊效果图


微信图片_20220523234122.png


咱再看看代码


    private void setBlur(){
        agb.iv1.setRenderEffect(RenderEffect.createBlurEffect(3, 0, Shader.TileMode.CLAMP));
        agb.iv2.setRenderEffect(RenderEffect.createBlurEffect(8, 0, Shader.TileMode.REPEAT));
        agb.iv3.setRenderEffect(RenderEffect.createBlurEffect(18, 0 ,Shader.TileMode.MIRROR));
        agb.iv4.setRenderEffect(RenderEffect.createBlurEffect(36, 0,Shader.TileMode.DECAL));
    }


RenderEffect.createBlurEffect()的四个参数:


  • radiusX 沿 X 轴的模糊半径


  • radiusY 沿 Y 轴的模糊半径


  • inputEffect 模糊一次(传入 RenderEffect)


  • edgeTreatment 用于如何模糊模糊内核边缘附近的内容


       下面两种仅看效果图。就不做代码设置了。


🌀 Y 轴的模糊效果图


微信图片_20220523234231.png


🌀 XY同时模糊效果图


微信图片_20220523234258.png


第四个参数对边缘模糊,效果图如下:


微信图片_20220523234321.png


  Shader.TileMode 提供了四个选项恕我没看出来。。

       这里还有一堆方法等你玩。


微信图片_20220523234343.png


注意:注意如此完美的画面只能在 Android 12(SDK31)及以上的设备上使用,其他版本的设备使用会导致崩溃,谨记谨记。 效果有了,下面咱们一起看看源码。


💥 源码


🌀 View.setRenderEffect()


    public void setRenderEffect(@Nullable RenderEffect renderEffect) {
        ...
    }


这个方法就是:renderEffect 应用于 View。 传入 null清除之前配置的RenderEffect 。这里咱们先看传入的 RenderEffect。


🌀 RenderEffect.createBlurEffect()


    public static RenderEffect createBlurEffect(
            float radiusX,
            float radiusY,
            @NonNull RenderEffect inputEffect,
            @NonNull TileMode edgeTreatment
    ) {
        long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0;
        return new RenderEffect(
                nativeCreateBlurEffect(
                        radiusX,
                        radiusY,
                        nativeInputEffect,
                        edgeTreatment.nativeInt
                )
            );
    }


两个 createBlurEffect() 方法,分别为三参(模糊一次)和四参(模糊两次)。inputEffect 先进行了一次模糊。


看效果图:


微信图片_20220523234518.png


模糊程度一样,但是实现方式不同:


    private void setBlur() {
        RenderEffect radiusXRenderEffect = RenderEffect.createBlurEffect(10, 0, Shader.TileMode.MIRROR);
        RenderEffect radiusYRenderEffect = RenderEffect.createBlurEffect(0, 10, Shader.TileMode.MIRROR);
        agb.iv1.setRenderEffect(RenderEffect.createBlurEffect(10, 10, Shader.TileMode.CLAMP));
        agb.iv2.setRenderEffect(RenderEffect.createBlurEffect(10, 10, Shader.TileMode.REPEAT));
        //自身radiusY 为 0 ,传入的radiusYRenderEffect设置的radiusY为10;
        agb.iv3.setRenderEffect(RenderEffect.createBlurEffect(10, 0, radiusYRenderEffect, Shader.TileMode.MIRROR));
        //自身radiusX 为 0 ,传入的radiusXRenderEffect设置的radiusX为10;
        agb.iv4.setRenderEffect(RenderEffect.createBlurEffect(0, 10, radiusXRenderEffect, Shader.TileMode.DECAL));
    }


这个方法返回一个 new RenderEffect(nativeCreateBlurEffect(...)。


       那咱们去看看 nativeCreateBlurEffect()


🌀 nativeCreateBlurEffect()


       frameworks/base/libs/hwui/jni/RenderEffect.cpp


static const JNINativeMethod gRenderEffectMethods[] = {
    ...
    {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
    ...
};
static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
        jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
    auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
    sk_sp<SkImageFilter> blurFilter =
            SkImageFilters::Blur(
                    Blur::convertRadiusToSigma(radiusX),
                    Blur::convertRadiusToSigma(radiusY),
                    static_cast<SkTileMode>(edgeTreatment),
                    sk_ref_sp(inputImageFilter),
                    nullptr);
    return reinterpret_cast<jlong>(blurFilter.release());
}


这里有两个函数来处理我们传过来的模糊的值,咱进去看看。


🌀 convertRadiusToSigma(convertSigmaToRadius)


//该常数近似于在SkBlurMask::Blur()(1/sqrt(3)中,在软件路径的"高质量"模式下进行的缩放。
static const float BLUR_SIGMA_SCALE = 0.57735f;
float Blur::convertRadiusToSigma(float radius) {
    return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
}
float Blur::convertSigmaToRadius(float sigma) {
    return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
}


🌀 sk_ref_sp(inputImageFilter)


       external/skia/include/core/SkRefCnt.h


/*
 *  返回包装提供的 ptr 的 sk_sp 并对其调用 ref (如果不为空)
 */
template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
    //sk_sp<SkImageFilter> :
    return sk_sp<T>(SkSafeRef(obj));
}
//SkSafeRef:检查参数是否为非空,如果是,则调用 obj->ref() 并返回 obj。
template <typename T> static inline T* SkSafeRef(T* obj) {
    if (obj) {
        obj->ref();
    }
    return obj;
}


🌀 SkImageFilters::Blur()


#define SK_Scalar1                  1.0f
#define SK_ScalarNearlyZero         (SK_Scalar1 / (1 << 12))
sk_sp<SkImageFilter> SkImageFilters::Blur(
        SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp<SkImageFilter> input,
        const CropRect& cropRect) {
    if (sigmaX < SK_ScalarNearlyZero && sigmaY < SK_ScalarNearlyZero && !cropRect) {
        return input;
    }
    return sk_sp<SkImageFilter>(
          new SkBlurImageFilter(sigmaX, sigmaY, tileMode, input, cropRect));
}


   附上最后的倔强


    constexpr sk_sp() : fPtr(nullptr) {}
    constexpr sk_sp(std::nullptr_t) : fPtr(nullptr) {}
    /**
     *  Shares the underlying object by calling ref(), so that both the argument and the newly
     *  created sk_sp both have a reference to it.
     */
    sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {}
    template <typename U,
              typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
    sk_sp(const sk_sp<U>& that) : fPtr(SkSafeRef(that.get())) {}
    /**
     *  Move the underlying object from the argument to the newly created sk_sp. Afterwards only
     *  the new sk_sp will have a reference to the object, and the argument will point to null.
     *  No call to ref() or unref() will be made.
     */
    sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {}
    template <typename U,
              typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
    sk_sp(sk_sp<U>&& that) : fPtr(that.release()) {}
    /**
     *  Adopt the bare pointer into the newly created sk_sp.
     *  No call to ref() or unref() will be made.
     */
    explicit sk_sp(T* obj) : fPtr(obj) {}


createBlurEffect() 得到 long 类型的 native 分配的的非零地址, 传入 new RenderEffect()


🌀 new RenderEffect()


1.    /* 构造方法:仅从静态工厂方法构造 */
    private RenderEffect(long nativeRenderEffect) {
        mNativeRenderEffect = nativeRenderEffect;
        RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
                this, mNativeRenderEffect);
    }


继续


    /**
      * @param classLoader ClassLoader 类加载器。
      * @param freeFunction 类型为 nativePtr 的本机函数的地址,用于释放这种本机分配
      * @return 由系统内存分配器分配的本机内存的 NativeAllocationRegistry。此版本更适合较小的对象(通常小于几百 KB)。
      */
    private static class RenderEffectHolder {
        public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        RenderEffect.class.getClassLoader(), nativeGetFinalizer());
    }


🌀 NativeAllocationRegistry.createMalloced()


       libcore/luni/src/main/java/libcore/util/NativeAllocationRegistry.java


    @SystemApi(client = MODULE_LIBRARIES)
    public static NativeAllocationRegistry createMalloced(
            @NonNull ClassLoader classLoader, long freeFunction, long size) {
        return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
    }


🌀 NativeAllocationRegistry()


       libcore/luni/src/main/java/libcore/util/NativeAllocationRegistry.java


1.    private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size,
            boolean mallocAllocation) {
        if (size < 0) {
            throw new IllegalArgumentException("Invalid native allocation size: " + size);
        }
        this.classLoader = classLoader;
        this.freeFunction = freeFunction;
        this.size = mallocAllocation ? (size | IS_MALLOCED) : (size & ~IS_MALLOCED);
    }


既然拿到 NativeAllocationRegistry 那就继续调用其 registerNativeAllocation() 方法。


🌀 registerNativeAllocation ()


1.    @SystemApi(client = MODULE_LIBRARIES)
    @libcore.api.IntraCoreApi
    public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
        //当 referent 或nativePtr 为空
        ...
        CleanerThunk thunk;
        CleanerRunner result;
        try {
            thunk = new CleanerThunk();
            Cleaner cleaner = Cleaner.create(referent, thunk);
            result = new CleanerRunner(cleaner);
            registerNativeAllocation(this.size);
        } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
            applyFreeFunction(freeFunction, nativePtr);
            throw vme;
        } 
        // Enable the cleaner only after we can no longer throw anything, including OOME.
        thunk.setNativePtr(nativePtr);
        // Ensure that cleaner doesn't get invoked before we enable it.
        Reference.reachabilityFence(referent);
        return result;
    }


向 ART 注册新的 NativePtr 和关联的 Java 对象(也就是咱们设置的模糊类)。


       返回的 Runnable 可用于在引用变得无法访问之前释放本机分配。如果运行时或使用 runnable 已经释放了本机分配,则 runnable 将不起作用。


       RenderEffect 算是搞完了,咱们回到View.setRenderEffect()


🌀 View.setRenderEffect()


    public void setRenderEffect(@Nullable RenderEffect renderEffect) {
        if (mRenderNode.setRenderEffect(renderEffect)) {
            //视图属性更改(alpha、translationXY 等)的快速失效。
            invalidateViewProperty(true, true);
        }
    }


这里有个 mRenderNode.setRenderEffect(renderEffect)。咱们近距离观望一番。


🌀 mRenderNode 的创建


       咱们先找找他是在什么地方创建的。


    public View(Context context) {
        ...
        //在View的构造方法中创建
        mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));
        ...
    }


🌀 RenderNode.create()


    /** @hide */
    public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
        return new RenderNode(name, animationHost);
    }
    private RenderNode(String name, AnimationHost animationHost) {
        mNativeRenderNode = nCreate(name);
        //注册 Native Allocation。
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
        mAnimationHost = animationHost;
    }    


 再往下感觉也看不到啥了 跟上面类似,看.cpp动态分配类的地址还是有点懵。让我缓缓~以后补充。


相关文章
|
XML 前端开发 算法
Android高级模糊技术
今天我们来更深入了解一下Android开发上的模糊技术。我读过几篇有关的文章,也在StackOverFlow上看过一些相关教程的帖子,所以我想在这里总结一下学到的东西。
347 0
Android高级模糊技术
|
Android开发
Android之路 - 实现高斯模糊的菜单
前言 本章主要用原生的方式实现一个菜单页面,主要用到的知识点为位移动画,我们可以先看看效果。 高斯模糊的菜单效果图.gif 分析 高斯模糊背景 我们的菜单背景是一个高斯模糊的背景,虽然看上去高大上,但是不要被吓到了,实现原理非常的简单:截取当前屏幕转换为bitmap,将bitmap进行高斯模糊,然后设置为菜单的背景。
1552 0
|
资源调度 算法 Java
Android图像处理 - 高斯模糊的原理及实现
原文:Android图像处理 - 高斯模糊的原理及实现 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 由 天天P图攻城狮 发布在云+社区 作者简介:damonxia(夏正冬),天天P图Android工程师 前言 高斯模糊是图像处理中几乎每个程序员都或多或少听过的名词,但是对其原理大家可能并不了解,只知道通过高斯模糊能实现图像毛玻璃效果。
2238 0
|
前端开发 Android开发
在 Android 下进行实时模糊渲染
本文讲的是在 Android 下进行实时模糊渲染,模糊渲染能生动地表达内容间的层次感。当专注于当前特定内容的时候,它允许用户维持相对的上下文,即使模糊层下面的内容发生了视差移动或者动态变化。
2155 0
|
前端开发 API Android开发
|
算法 数据建模 Android开发
Android高斯模糊、高斯平滑(Gaussian Blur)【1】
 Android高斯模糊、高斯平滑(Gaussian Blur)【1】 Android高斯模糊、高斯平滑(Gaussian Blur),图形图像处理的一种效果,经过高斯模糊处理后的图片有一种“毛玻璃”的效果。
1363 0
|
前端开发 数据建模 Java
Android高斯模糊、高斯平滑(Gaussian Blur)【2】
Android高斯模糊、高斯平滑(Gaussian Blur)【2】 Android上的高斯模糊效果实现,策略不唯一,在github上有一个开源的实现算法:https://github.com/paveldudka/blurring 性能上对附录参考文章【1】进行了改进和提升。
1030 0
|
21天前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
5天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
21 5