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 continueif (!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 shadersblurInput = 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 datecanvas = 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 activeSurfaceactiveSurface = 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 themcanvas->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 earlierif (!blurInput) {blurInput = activeSurface->makeImageSnapshot();}// rect to be blurred in the coordinate space of blurInputconst 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 earlierif (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实战开发干货,请关注下面“千里马学框架”