aosp13/14/15/16如何实现窗口局部区域高斯模糊毛玻璃效果及Winscope原生重大bug发现

背景:

关于aosp高版本系统自带的高斯模糊我们之前已经分享过相关的实战文章,具体可以点击如下链接:

android13 FLAG_BLUR_BEHIND 壁纸高斯模糊,毛玻璃背景方案设计-千里马framework实战

壁纸模糊前:
在这里插入图片描述

壁纸模糊后:
在这里插入图片描述
但是上面分享模糊都是针对整个窗口进行的,就是桌面窗口设置一下后面窗口进行模糊的标志位,然后针对桌面窗口后面的壁纸窗口进行整体模糊。

但是学员们又提出新的需求,他想要的是局部模糊,需求如下:
在这里插入图片描述

简单说学员就是想实现对状态栏等区域局部进行高斯模糊,以前方案是针对窗口layer所有进行模糊,那么是否可以实现窗口的局部高斯模糊呢?
那么接下来要去探索aosp原生系统是否有接口支持局部高斯模糊呢?

SurfaceFlinger高斯接口代码探索:

以前高斯模糊原理也分析过,知道是在SurfaceFlinger触摸绘制时候进行的高斯模糊,那么再来分析一下高斯模糊相关实现时候是否有相关区域设置参数,从而反推出相关接口。
frameworks/native/libs/renderengine/skia/SkiaGLRenderEngine.cpp

void SkiaGLRenderEngine::drawLayersInternal(
        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
        const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
        base::unique_fd&& bufferFence) {
  //省略
    if (mBlurFilter) {
        bool requiresCompositionLayer = false;
        for (const auto& layer : layers) {
            // if the layer doesn't have blur or it is not visible then continue
            if (!layerHasBlur(layer, ctModifiesAlpha)) {  //这里会判断layer是否有高斯模糊
                continue;
            }
            //判断1就是backgroundBlurRadius,就是之前一直用的
            if (layer.backgroundBlurRadius > 0 &&
                layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
                requiresCompositionLayer = true;
            }
             //判断2这个之前没了解过,看着明显blurRegions就是有一个模糊区域数组,这里其实就是我们突破口
            for (auto region : layer.blurRegions) {
                if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
                    requiresCompositionLayer = true;
                }
            }
            if (requiresCompositionLayer) {
                activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
                canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
                blurCompositionLayer = &layer;//这里赋值模糊的layer给blurCompositionLayer变量
                break;
            }
        }
    }
//省略
    for (const auto& layer : layers) {
     //省略
        sk_sp<SkImage> blurInput;
        //这里就是核心
        if (blurCompositionLayer == &layer) {
            LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
            LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);

            // save a snapshot of the activeSurface to use as input to the blur shaders
            blurInput = activeSurface->makeImageSnapshot();

            // blit the offscreen framebuffer into the destination AHB, but only
            // if there are blur regions. backgroundBlurRadius blurs the entire
            // image below, so it can skip this step.
            if (layer.blurRegions.size()) {
                SkPaint paint;
                paint.setBlendMode(SkBlendMode::kSrc);
                if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
                    uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
                    dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
                                              String8::format("SurfaceID|%" PRId64, id).c_str(),
                                              nullptr);
                    dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
                } else {
                    activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
                }
            }

            // assign dstCanvas to canvas and ensure that the canvas state is up to date
            canvas = dstCanvas;
            surfaceAutoSaveRestore.replace(canvas);
            initCanvas(canvas, display);

            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
                                dstSurface->getCanvas()->getSaveCount());
            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
                                dstSurface->getCanvas()->getTotalMatrix());

            // assign dstSurface to activeSurface
            activeSurface = dstSurface;
        }

        SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
        if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
            // Record the name of the layer if the capture is running.
            std::stringstream layerSettings;
            PrintTo(layer, &layerSettings);
            // Store the LayerSettings in additional information.
            canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
                                   SkData::MakeWithCString(layerSettings.str().c_str()));
        }
        // Layers have a local transform that should be applied to them
        canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());

        const auto [bounds, roundRectClip] =
                getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
                                 layer.geometry.roundedCornersRadius);
        if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
            std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;

            // if multiple layers have blur, then we need to take a snapshot now because
            // only the lowest layer will have blurImage populated earlier
            if (!blurInput) {
                blurInput = activeSurface->makeImageSnapshot();
            }
            // rect to be blurred in the coordinate space of blurInput
            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());

            // if the clip needs to be applied then apply it now and make sure
            // it is restored before we attempt to draw any shadows.
            SkAutoCanvasRestore acr(canvas, true);
            if (!roundRectClip.isEmpty()) {
                canvas->clipRRect(roundRectClip, true);
            }

            // TODO(b/182216890): Filter out empty layers earlier
            if (blurRect.width() > 0 && blurRect.height() > 0) {
                if (layer.backgroundBlurRadius > 0) {
                    ATRACE_NAME("BackgroundBlur");
                    auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
                                                              blurInput, blurRect);

                    cachedBlurs[layer.backgroundBlurRadius] = blurredImage;

                    mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
                                                blurRect, blurredImage, blurInput);
                }

                canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
                for (auto region : layer.blurRegions) {
                    if (cachedBlurs[region.blurRadius] == nullptr) {
                        ATRACE_NAME("BlurRegion");
                        //明显看到这里会传递相关的模糊区域参数
                        cachedBlurs[region.blurRadius] =
                                mBlurFilter->generate(grContext, region.blurRadius, blurInput,
                                                      blurRect);
                    }

                    mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
                                                region.alpha, blurRect,
                                                cachedBlurs[region.blurRadius], blurInput);
                }
            }
        }
//省略
    return;
}

从SurfaceFlinger的模糊实现来看,确实有layer.blurRegions可以控制相关图层layer的区域模糊。

最后根据这个layer.blurRegions线索一路追踪发现上层其实有现成的接口可以进行设置。
frameworks/base/core/java/android/view/SurfaceControl.java


        /**
         * Specify what regions should be blurred on the {@link SurfaceControl}.
         *
         * @param sc SurfaceControl.
         * @param regions List of regions that will have blurs.
         * @return itself.
         * @see BlurRegion#toFloatArray()
         * @hide
         */
        public Transaction setBlurRegions(SurfaceControl sc, float[][] regions) {
            checkPreconditions(sc);
            nativeSetBlurRegions(mNativeObject, sc.mNativeObject, regions, regions.length);
            return this;
        }

所以完全可以通过这个接口来进行实现

代码实现成果展示

这里以桌面作为demo,针对桌面的上部分500像素局部区域进行模糊,这样可以明显区分出来是否模糊生效。

核心代码如下:

在这里插入图片描述需要完整patch的学员可以私聊马哥
成果展示:
先看看没有模糊时候壁纸
在这里插入图片描述

代码模糊上面500像素后最后效果如下:

在这里插入图片描述

Winscope重大bug发现

上面确实调用了setBlurRegions是有效果的,本来想通过Winscope来再次验证一下,但是发现如下情况:
在这里插入图片描述
这里是啥情况?为啥Winscope显示这个BlurRegions是个空,明明我们现象看都发现成功了,这里就刚好把这个Winscope的重大bug留个学员们探索的一个作业了哈。

学员朋友们尝试看看是否可以修复android这个重大的原生bug,大家先尝试自己探索修复,实在搞不出来,更多答案及修复方案马哥到时候可能会分享群里哈。

更多framework实战开发干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值