Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
🔥 author
Author: Shuai times
About the author: CSDN blog expert, welcome to like, favorites, comments
Fan benefits: the public account “Shuaitimes” a share of Android system technology, related knowledge, interview questions database, technical assistance, dry goods, information, high-paying positions, tutorial place.
🔥 Android 12 Gaussian Blur
New features: Easier to use blur, color filters and other special effects.
The new API makes it easier to apply common graphical effects to views and render structures.
-
Use RenderEffect to apply effects such as blur and color filters to a RenderNode or View.
-
Using the new Window. SetBackgroundBlurRadius () API to create fog glass Window background effect,
-
Use blurBehindRadius to blur everything behind the window.
Let’s play one by one.
🔥 RenderEffect
💥 Effect
private void setBlur(a){
View.setRenderEffect(RenderEffect.createBlurEffect(3.3, Shader.TileMode.REPEAT)); . }Copy the code
Easy to use, go.
🌀 Fuzzy rendering of the X-axis
Let’s look at the code
private void setBlur(a){
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));
}
Copy the code
RenderEffect. CreateBlurEffect () four parameters:
-
The fuzzy radius of radiusX along the X axis
-
RadiusY Fuzzy radius along the Y-axis
-
InputEffect blur once (passing in RenderEffect)
-
How is edgeTreatment used to blur content near the edge of the kernel
The following two only look at the renderings. I’m not going to do the code setup.
🌀 Fuzzy rendering of the Y axis
🌀 XY simultaneously fuzzy renderings
The fourth parameter is fuzzy to the edge, and the effect picture is as follows:
Shader.TileMode provides four options that I didn’t see.
There are more ways to do it.
Note: Note that this perfect screen only works on Android 12(SDK31) and later devices, other versions will crash, keep in mind.
The effect is, let’s have a look at the source code.
💥 source
🌀 View. SetRenderEffect ()
public void setRenderEffect(@Nullable RenderEffect renderEffect) {... }Copy the code
This method is renderEffect applied to the View. Passing null clears the previously configured RenderEffect. Let’s first look at the RenderEffect that was passed in.
🌀 RenderEffect. CreateBlurEffect ()
public static RenderEffect createBlurEffect(
float radiusX,
float radiusY,
@NonNull RenderEffect inputEffect,
@NonNull TileMode edgeTreatment
) {
longnativeInputEffect = inputEffect ! =null ? inputEffect.mNativeRenderEffect : 0;
return new RenderEffect(
nativeCreateBlurEffect(
radiusX,
radiusY,
nativeInputEffect,
edgeTreatment.nativeInt
)
);
}
Copy the code
Two createBlurEffect() methods with three parameters (blur once) and four parameters (blur twice). InputEffect does a blur first.
Look at the renderings:
The ambiguity is the same, but the implementation is different:
private void setBlur(a) {
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));
// Set the radiusY to 0 and the passed radiusYRenderEffect to 10.
agb.iv3.setRenderEffect(RenderEffect.createBlurEffect(10.0, radiusYRenderEffect, Shader.TileMode.MIRROR));
// The incoming radiusXRenderEffect radiusX is set to 10;
agb.iv4.setRenderEffect(RenderEffect.createBlurEffect(0.10, radiusXRenderEffect, Shader.TileMode.DECAL));
}
Copy the code
This method returns a new RenderEffect(nativeCreateBlurEffect(…) .
Let’s go to 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());
}
Copy the code
There are two functions here to deal with the fuzzy values that we passed, so let’s go inside.
🌀 convertRadiusToSigma (convertSigmaToRadius)
// This constant is approximate to the zoom performed in the "good quality" mode of the software path in SkBlurMask::Blur()(1/ SQRT (3).
static const float BLUR_SIGMA_SCALE = 0.57735 f;
float Blur::convertRadiusToSigma(float radius) {
return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5 f : 0.0 f;
}
float Blur::convertSigmaToRadius(float sigma) {
return sigma > 0.5 f ? (sigma - 0.5 f) / BLUR_SIGMA_SCALE : 0.0 f;
}
Copy the code
🌀 sk_ref_sp (inputImageFilter)
external/skia/include/core/SkRefCnt.h
/* * returns the sk_sp of the wrapper supplied PTR and calls ref (if not empty) */ on it
template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
//sk_sp<SkImageFilter> :
return sk_sp<T>(SkSafeRef(obj));
}
//SkSafeRef: checks if the argument is non-empty, and if so, calls obj->ref() and returns obj.
template <typename T> static inline T* SkSafeRef(T* obj) {
if (obj) {
obj->ref(a); }return obj;
}
Copy the code
Go down again
🌀 SkImageFilters: : the Blur ()
#defineSK_Scalar1 1.0 f
#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));
}
Copy the code
With the last stubborn
constexpr sk_sp(a) : 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) {}
Copy the code
CreateBlurEffect () gets the long native assigned non-zero address, passing new RenderEffect()
🌀 new RenderEffect ()
/* Constructor: construct only from static factory method */
private RenderEffect(long nativeRenderEffect) {
mNativeRenderEffect = nativeRenderEffect;
RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
this, mNativeRenderEffect);
}
Copy the code
Continue to
/ * * *@paramClassLoader classLoader classLoader. *@paramFreeFunction The address of the native function of type nativePtr used to release this native allocation *@returnThe NativeAllocationRegistry of native memory allocated by the system memory allocator. This version is better for smaller objects (typically less than a few hundred KB). * /
private static class RenderEffectHolder {
public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
NativeAllocationRegistry.createMalloced(
RenderEffect.class.getClassLoader(), nativeGetFinalizer());
}
Copy the code
🌀 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);
}
Copy the code
🌀 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);
}
Copy the code
Now that you have the NativeAllocationRegistry, proceed to call its registerNativeAllocation() method.
🌀 registerNativeAllocation ()
@SystemApi(client = MODULE_LIBRARIES)
@libcore.api.IntraCoreApi
public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
// When referent or nativePtr is null. 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;
}
Copy the code
Register the new NativePtr and associated Java objects with ART (that is, the fuzzy class we set up).
The returned Runnable can be used to release native allocations before the reference becomes inaccessible. Runnable will have no effect if the native allocation has been freed at runtime or using runnable.
RenderEffect is done, let’s go back to View.setrenderEffect ()
🌀 View. SetRenderEffect ()
public void setRenderEffect(@Nullable RenderEffect renderEffect) {
if (mRenderNode.setRenderEffect(renderEffect)) {
// Fast invalidation of view property changes (alpha, translationXY, etc.)
invalidateViewProperty(true.true); }}Copy the code
Here is a mRenderNode. SetRenderEffect (renderEffect). Let’s take a closer look.
🌀 Creation of mRenderNode
Let’s find out where he was founded.
public View(Context context) {...// Created in the View constructor
mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this)); . }Copy the code
🌀 RenderNode. The 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);
// Register Native Allocation.
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
mAnimationHost = animationHost;
}
Copy the code
You can’t really see anything further down here and again, look.cpP dynamically allocates the address of the class and it’s still kind of confusing. Let me fill in later.