副驾屏高斯模糊/Kawase方案---无wallpaper,透明区域如何实现高斯模糊/Kawase效果(卷2: 副驾屏Kawase 模糊实现方案)

发布于:2025-06-24 ⋅ 阅读:(17) ⋅ 点赞:(0)

副驾屏高斯模糊/Kawase方案—无wallpaper,透明区域如何实现高斯模糊/Kawase效果(卷2: 副驾屏Kawase 模糊实现方案)

一、问题描述
**项目背景:**整个30寸的屏幕,以及主驾屏(左半屏)是display0,wallpaper是全屏壁纸,显示在display0上;副驾屏(右半屏)是display5,display5是一块虚拟屏,没有壁纸。

当副驾屏未设置壁纸且运行透明背景 App 时,Android 原生高斯模糊算法会导致透明区域呈现全黑现象,具体表现为(入下图所示):

  1. 透明背景区域失去通透感,显示为纯黑色块
  2. 多任务界面视觉断层,影响车载系统交互体验。

在这里插入图片描述

采用了副驾屏Kawase 模糊实现方案之后,效果如下图所示:
在这里插入图片描述

出现透明区域黑块的原因是:
参考:副驾屏高斯模糊/Kawase方案—无wallpaper,透明区域如何实现高斯模糊/Kawase效果(卷1: Kawase 模糊的相关概念,以及在android中的流程)

Kawase模糊方案计算的是一块Display的所有layer的合成情况,而不是获取肉眼看到的区域下的所有layer,当副驾屏没有wallpaper的时候(虽然肉眼能够看到底下有wallpaper,其实是虚拟屏副驾屏display5透的下面主驾屏display0的壁纸),那么就会出现,透明区域之下的layer不存在,就是一块黑的buffer,通过Kawase模糊算法计算之后的结果,就是黑块。

如果要解决上面的Kawase模糊的黑屏问题,必须将display0的layer加入到Kawase模糊的计算过程中。我们的解决方案,就是在计算副驾屏display5 Kawase模糊之前,先截取主驾屏(全屏),然后将截取的图片在一块buffer上绘制出来,然后将其送给display5的kawase模糊计算中去。

二、详细实现方案

根据卷1我们知道,Kawase模糊的流程如下:

在这里插入图片描述

根据前面解释的Kawase模糊的流程,我们设计新的Kawase模糊的流程图如下,

在这里插入图片描述
根据上面的流程图,我们知道,当SurfaceFlinger合成的时候,会调用Output::composeSurfaces方法,接着会将Output对应的display中的使用gpu合成的layers,传给RenderEngine::drawLayers方法,紧接着就会通过SkiaGLRenderEngine::drawLayersInternal方法进行Kawase模糊计算。那么我们就必须在display5 合成之前,将display0的合成的图保存起来,然后在display5合成的时候,传过来参与合成。

(1)Output::composeSurfaces的修改

    if (renderengine::RenderEngine::virtualDisplayId > 10000) {
        bool isMainDisplay = false;
        bool isVirtualDisplay = false;
        std::optional<DisplayId> displayIdOpt = getDisplayId();
        if (displayIdOpt.has_value()) {
            DisplayId displayId = displayIdOpt.value();
            clientCompositionDisplay.displayId = displayId.value;
            //ALOGW("Kawase, present2222 DisplayId is: %" PRIu64, displayId.value);
            isMainDisplay = (displayId.value == static_cast<uint64_t>(129));
            isVirtualDisplay = (displayId.value == renderengine::RenderEngine::virtualDisplayId);
            if (isMainDisplay && !Output::hasBlurLayer) {
                //ALOGW("Kawase, -------present222 DisplayId is: %" PRIu64, displayId.value);
                renderengine::RenderEngine::mainDisplayBuffer = tex;
                renderengine::RenderEngine::mainDisplayBufferUpdate = true;
            } /*else if (isVirtualDisplay) {
                ALOGW("Kawase, -------present222 DisplayId is: %" PRIu64, displayId.value);
            }*/
        }

        if (isVirtualDisplay) {
            Output::hasBlurLayer = false;
            for (const auto& layer : clientRenderEngineLayers) {
                if (layer.backgroundBlurRadius > 0.0f || !layer.blurRegions.empty()) {
                    Output::hasBlurLayer = true;
                    //ALOGW("Kawase, -------present222 has blur layer");
                    break;
                }
            }
            /*if (!Output::hasBlurLayer) {
                ALOGW("Kawase, -------present222 no blur layer");
            }

            if (renderengine::RenderEngine::mainDisplayBuffer) {
                ALOGW("Kawase, -------present222 mainDisplayBuffer=%p", renderengine::RenderEngine::mainDisplayBuffer.get());
            }*/

        }
    }

(2)SkiaGLRenderEngine::drawLayersInternal的修改

                // --- handle main display ExternalTexture,extract right part snapshot ---
                if (display.displayId == RenderEngine::virtualDisplayId && RenderEngine::mainDisplayBuffer != nullptr) {
                    if (RenderEngine::mainDisplayBuffer->getWidth() >= 5120) {
                        if (reusedScreenshotImage()) {
                            //ALOGW("Kawase reusedScreenshotImage-222 ok");
                            blurInput = SkiaGLRenderEngine::mainDisplayBufferImage;
                        } else {
                            ALOGW("Kawase reusedScreenshotImage-222 no");
                            sk_sp<SkImage> mainImage  = nullptr;
                            auto grContext = getActiveGrContext();
                            if (grContext) {
                                // create AutoBackendTexture::LocalRef,manager AHardwareBuffer texture resource
                                std::shared_ptr<AutoBackendTexture::LocalRef> texRef = std::make_shared<AutoBackendTexture::LocalRef>(
                                        grContext,
                                        RenderEngine::mainDisplayBuffer->getBuffer()->toAHardwareBuffer(),
                                        true,  // isRenderable
                                        mTextureCleanupMgr);
                                if (texRef) {
                                    // get surface,and then get snapshot
                                    sk_sp<SkSurface> surface = texRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR, grContext);
                                    if (surface) {
                                        mainImage = surface->makeImageSnapshot();
                                    }
                                }
                            }
                            if (mainImage) {
                                sk_sp<SkImage> blurInputRight = extractRightHalf(mainImage, grContext);
                                if (blurInputRight != nullptr) {
                                    blurInput = blurInputRight;//right part
                                } else {
                                    blurInput = mainImage;//full part
                                }
                            }
                        }
                    }
                    if (blurInput == nullptr) {
                        blurInput = activeSurface->makeImageSnapshot();//fix null pointer exception
                    }
                } else {
                    blurInput = activeSurface->makeImageSnapshot();
                }

(3)CustomizedRenderEngine::drawLayersInternal的修改

bool CustomizedRenderEngine::reusedScreenshotImage() {
    /*ALOGW("Kawase reusedScreenshotImage RenderEngine::mainDisplayBuffer =  %p, SkiaGLRenderEngine::mainDisplayBufferImage = %p,"
        "RenderEngine::mainDisplayBufferUpdate = %d", RenderEngine::mainDisplayBuffer.get(),
        SkiaGLRenderEngine::mainDisplayBufferImage.get(), RenderEngine::mainDisplayBufferUpdate);
    */

    if (!RenderEngine::mainDisplayBufferUpdate && SkiaGLRenderEngine::mainDisplayBufferImage) {
        return true;
    }

    return false;

}

sk_sp<SkImage> CustomizedRenderEngine::extractRightHalf(sk_sp<SkImage> blurInput, GrRecordingContext* grCtx) {
    if (!blurInput) return nullptr;

    int width = blurInput->width();
    int height = blurInput->height();
    if (width <= 1 || height <= 1) return nullptr;

    int halfWidth = width / 2;

    SkImageInfo info = SkImageInfo::Make(halfWidth, height, kRGBA_8888_SkColorType,
                                         kPremul_SkAlphaType, blurInput->refColorSpace());

    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grCtx, SkBudgeted::kYes, info);
    if (!surface) {
        ALOGE("Failed to create SkSurface");
        return nullptr;
    }

    SkCanvas* canvas = surface->getCanvas();

    SkRect srcRect = SkRect::MakeXYWH(halfWidth, 0, halfWidth, height); // extract right part
    SkRect dstRect = SkRect::MakeWH(halfWidth, height);

    ALOGW("Kawase blurInput size: %d x %d", width, height);
    ALOGW("Kawase srcRect: [%f, %f, %f, %f]", srcRect.left(), srcRect.top(), srcRect.right(), srcRect.bottom());

    SkSamplingOptions sampling;
    SkPaint paint;
    canvas->drawImageRect(blurInput.get(), srcRect, dstRect, sampling, &paint, SkCanvas::kFast_SrcRectConstraint);//kStrict_SrcRectConstraint

    sk_sp<SkImage> result = surface->makeImageSnapshot();

    SkiaGLRenderEngine::mainDisplayBufferImage = result;
    RenderEngine::mainDisplayBufferUpdate = false;

    return result;
}

网站公告

今日签到

点亮在社区的每一天
去签到