注册

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

 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()


    /* 构造方法:仅从静态工厂方法构造 */
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


    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 ()


    @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动态分配类的地址还是有点懵。让我缓缓~以后补充。


作者:Android帅次
链接:https://juejin.cn/post/7020322106353123365
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册