副驾屏高斯模糊/Kawase方案---无wallpaper,透明区域如何实现高斯模糊/Kawase效果(卷2: 副驾屏Kawase 模糊实现方案)
副驾屏高斯模糊/Kawase方案—无wallpaper,透明区域如何实现高斯模糊/Kawase效果(卷2: 副驾屏Kawase 模糊实现方案)
一、问题描述
**项目背景:**整个30寸的屏幕,以及主驾屏(左半屏)是display0,wallpaper是全屏壁纸,显示在display0上;副驾屏(右半屏)是display5,display5是一块虚拟屏,没有壁纸。
当副驾屏未设置壁纸且运行透明背景 App 时,Android 原生高斯模糊算法会导致透明区域呈现全黑现象,具体表现为(入下图所示):
- 透明背景区域失去通透感,显示为纯黑色块
- 多任务界面视觉断层,影响车载系统交互体验。
采用了副驾屏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 resourcestd::shared_ptr<AutoBackendTexture::LocalRef> texRef = std::make_shared<AutoBackendTexture::LocalRef>(grContext,RenderEngine::mainDisplayBuffer->getBuffer()->toAHardwareBuffer(),true, // isRenderablemTextureCleanupMgr);if (texRef) {// get surface,and then get snapshotsk_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 partSkRect 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_SrcRectConstraintsk_sp<SkImage> result = surface->makeImageSnapshot();SkiaGLRenderEngine::mainDisplayBufferImage = result;RenderEngine::mainDisplayBufferUpdate = false;return result;
}