背景:
关于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实战开发干货,请关注下面“千里马学框架”

1634

被折叠的 条评论
为什么被折叠?



