android13 FLAG_BLUR_BEHIND wallpaper Gaussian blur, frosted glass background scheme design – Maxima framework actual combat

hi, fans and friends!
Today, a student friend asked a question related to Gaussian blur. I am relatively familiar with the requirements related to Gaussian blur. Let’s focus on the implementation of the new version of Gaussian blur.
More framework dry goods knowledge hands-on teaching

Log.i("qq group", "422901085");

1. Gaussian blur application scenario

When the status bar is pulled down, you can see the desktop image as the background, but at this time the desktop image is blurred by Gaussian, which greatly improves the interactive experience for people. The aesthetic level and primary and secondary are distinct, just like taking pictures Like the characters at the time, the background will be blurred and the characters will be the focus.

2. Implementation of Gaussian Blur

Scheme 1 Screenshot This is the most conventional scheme, which has been used in the past, and the main implementation principle is also very simple:
Take a screenshot of the background -” perform Gaussian blur image processing on the screenshot bitmap -” display the processed bitmap as the background
Advantages: the scheme is simple, all fuzzy and so on are controlled by oneself, and the control flexibility is large
Disadvantages: Because of the screenshots, there is no way to make real-time blurring, and there are many function modification logics

Solution 2 This is a built-in method unique to the new version of Android. Its implementation principle is to set the flag for a certain window. If FLAG_BLUR_BEHIND is set, then the window layer behind it will be set to be blurred, which belongs to the surfaceflinger level. Yes, layer processing during rendering, this blur is real-time, that is, even if the picture behind is moving, Gaussian blur will follow
Advantages: It is related to the interface provided by the system, simple setting is enough, no additional operation is required, and it can be blurred in real time

Disadvantage: Because the operation object is window, but the area below the window will be blurred, which may cause bugs in some window switching scenes
Let’s focus on

3. Focus on the new solution 2 FLAG_BLUR_BEHIND usage

frameworks/base/core/java/android/view/WindowManager.java

 /** Window flag: enable blur behind for this window. */
    public static final int FLAG_BLUR_BEHIND = 0x00000004;

This FLAG_BLUR_BEHIND belongs to LayoutParams

The comment means that FLAG_BLUR_BEHIND will blur the window below the window
Specifically if you want to use:
In fact, it is to set this flag to the LayoutParams of the window

 getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

At the same time there is another method setBlurBehindRadius

 public void setBlurBehindRadius(@IntRange(from = 0) int blurBehindRadius) {<!-- -->
            mBlurBehindRadius = blurBehindRadius;
        }

Everyone knows that Gaussian blur has a blur filter, which represents the degree of blur. Generally, the larger the mBlurBehindRadius, the stronger the blur level. code show as below:

 @Override
    protected void onResume() {<!-- -->
        super.onResume();
        
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
        getWindow().getAttributes().setBlurBehindRadius(100);//Set a larger value to 100, which is obvious
    }

The effect comparison before and after blurring is as follows:
Before blurring:

After blurring:

4. Principle source code analysis

After setting FLAG_BLUR_BEHIND, finally read and related business processing will be performed in WindowState:
frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    private boolean shouldDrawBlurBehind() {<!-- -->
        return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0
             & amp; & amp; mWmService.mBlurController.getBlurEnabled();
    }

Then perform relevant execution based on this in the applyDims method:

    private void applyDims() {<!-- -->
        if (!mAnimatingExit & amp; & amp; mAppDied) {<!-- -->
            mIsDimming = true;
            getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
        } else if (((mAttrs.flags & amp; FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
                    & amp; & amp; isVisibleNow() & amp; & amp; !mHidden) {<!-- -->
            // Only show the Dimmer when the following is satisfied:
            // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
            // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
            // 3. The WS is considered visible according to the isVisible() method
            // 4. The WS is not hidden.
            mIsDimming = true;
            final float dimAmount = (mAttrs.flags & amp; FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
            final int blurRadius = shouldDrawBlurBehind() ? mAttrs. getBlurBehindRadius() : 0;
            getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
        }
    }

Let’s look at the execution of the getDimmer().dimBelow method:

 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha,
                  int blurRadius) {<!-- -->
        dim(t, container, -1, alpha, blurRadius);//Call the dim method
    }
      private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
            float alpha, int blurRadius) {<!-- -->
        final DimState d = getDimState(container);//The key here is to create a corresponding EffectLayer

        if (d == null) {<!-- -->
            return;
        }

        if (container != null) {<!-- -->
            // The dim method is called from WindowState. prepareSurfaces(), which is always called
            // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
            // relative to the highest Z layer with a dim.
            t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
        } else {<!-- -->
            t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
        }
        t.setAlpha(d.mDimLayer, alpha);
        t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);//Set BlurRadius for this layer

        d.mDimming = true;
    }

Let’s focus on getDimState to see how it was created:

 /**
     * Retrieve the DimState, creating one if it doesn't exist.
     */
    private DimState getDimState(WindowContainer container) {<!-- -->
        if (mDimState == null) {<!-- -->
            try {<!-- -->
                final SurfaceControl ctl = makeDimLayer();//Call makeDimLayer to create
                mDimState = new DimState(ctl);
                /**
                 * See documentation on {@link #dimAbove} to understand lifecycle management of
                 * Dim's via state resetting for Dim's with containers.
                 */
                if (container == null) {<!-- -->
                    mDimState.mDontReset = true;
                }
            } catch (Surface. OutOfResourcesException e) {<!-- -->
                Log.w(TAG, "OutOfResourcesException creating dim surface");
            }
        }

        mLastRequestedDimContainer = container;
        return mDimState;
    }
  private SurfaceControl makeDimLayer() {<!-- -->
        return mHost. makeChildSurface(null)
                .setParent(mHost.getSurfaceControl())//The host here is the task of our activity
                .setColorLayer()//Attention is this type of layer
                .setName("Dim Layer for - " + mHost.getName())
                .setCallsite("Dimmer.makeDimLayer")
                .build();
    }

After the analysis of the above code level, let’s take a look at the changes in the SurfaceFlinger-related layers