🔥 Android 12 高斯模糊
新功能:更易用的模糊、彩色滤镜等特效 。
新的 API 让你能更轻松地将常见图形效果应用到视图和渲染结构上。
- 使用 RenderEffect 将模糊、色彩滤镜等效果应用于 RenderNode 或 View。
- 使用新的 Window.setBackgroundBlurRadius() API 为窗口背景创建雾面玻璃效果,
- 使用 blurBehindRadius 来模糊窗口后面的所有内容。
咱们一个一个玩。
🔥 RenderEffect
💥 实现效果
private void setBlur(){ View.setRenderEffect(RenderEffect.createBlurEffect(3, 3, Shader.TileMode.REPEAT)); ... }
使用特别简单,走你。
🌀 X 轴的模糊效果图
咱再看看代码
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 轴的模糊效果图
🌀 XY同时模糊效果图
第四个参数对边缘模糊,效果图如下:
Shader.TileMode 提供了四个选项恕我没看出来。。
这里还有一堆方法等你玩。
注意:注意如此完美的画面只能在 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 先进行了一次模糊。
看效果图:
模糊程度一样,但是实现方式不同:
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动态分配类的地址还是有点懵。让我缓缓~以后补充。