Loading libs/renderengine/skia/SkiaGLRenderEngine.cpp +103 −29 Original line number Diff line number Diff line Loading @@ -667,6 +667,17 @@ void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect, canvas->drawRect(stretchBounds, paint); } static SkRRect getBlurRRect(const BlurRegion& region) { const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; SkRRect roundedRect; roundedRect.setRectRadii(rect, radii); return roundedRect; } status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, const std::shared_ptr<ExternalTexture>& buffer, Loading Loading @@ -840,7 +851,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // Layers have a local transform that should be applied to them canvas->concat(getSkM44(layer->geometry.positionTransform).asM33()); const auto bounds = getSkRect(layer->geometry.boundaries); const auto [bounds, roundRectClip] = getBoundsAndClip(layer); if (mBlurFilter && layerHasBlur(layer)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; Loading @@ -850,7 +861,14 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, blurInput = activeSurface->makeImageSnapshot(); } // rect to be blurred in the coordinate space of blurInput const auto blurRect = canvas->getTotalMatrix().mapRect(bounds); 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) { Loading @@ -862,10 +880,10 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, cachedBlurs[layer->backgroundBlurRadius] = blurredImage; mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage, blurInput); mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f, blurRect, blurredImage, blurInput); } SkAutoCanvasRestore acr(canvas, true); canvas->concat(getSkM44(layer->blurRegionTransform).asM33()); for (auto region : layer->blurRegions) { if (cachedBlurs[region.blurRadius] == nullptr) { Loading @@ -875,7 +893,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, blurRect); } mBlurFilter->drawBlurRegion(canvas, region, blurRect, mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, region.alpha, blurRect, cachedBlurs[region.blurRadius], blurInput); } } Loading @@ -888,7 +907,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, if (layer->shadow.length > 0) { const auto rect = layer->geometry.roundedCornersRadius > 0 ? getSkRect(layer->geometry.roundedCornersCrop) : bounds; : bounds.rect(); // This would require a new parameter/flag to SkShadowUtils::DrawShadow LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow"); drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); Loading Loading @@ -953,7 +972,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // The shader does not respect the translation, so we add it to the texture // transform for the SkImage. This will make sure that the correct layer contents // are drawn in the correct part of the screen. matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top); matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); sk_sp<SkShader> shader; Loading Loading @@ -1015,9 +1034,13 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, paint.setColorFilter(displayColorTransform); if (layer->geometry.roundedCornersRadius > 0) { if (!roundRectClip.isEmpty()) { canvas->clipRRect(roundRectClip, true); } if (!bounds.isRect()) { paint.setAntiAlias(true); canvas->drawRRect(getRoundedRect(layer), paint); canvas->drawRRect(bounds, paint); } else { auto& stretchEffect = layer->stretchEffect; // TODO (njawad) temporarily disable manipulation of geometry Loading @@ -1026,9 +1049,9 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // Keep the method call in a dead code path to make -Werror happy // with unused methods if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) { drawStretch(bounds, stretchEffect, canvas, paint); drawStretch(bounds.rect(), stretchEffect, canvas, paint); } else { canvas->drawRect(bounds, paint); canvas->drawRect(bounds.rect(), paint); } } if (kFlushAfterEveryLayer) { Loading Loading @@ -1077,25 +1100,76 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) { const auto rect = getSkRect(layer->geometry.roundedCornersCrop); inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip( const LayerSettings* layer) { const auto bounds = getSkRect(layer->geometry.boundaries); const auto crop = getSkRect(layer->geometry.roundedCornersCrop); const auto cornerRadius = layer->geometry.roundedCornersRadius; return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius); } inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) { const auto rect = getSkRect(layer->geometry.boundaries); const auto cornersRadius = layer->geometry.roundedCornersRadius; return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius), .cornerRadiusTL = cornersRadius, .cornerRadiusTR = cornersRadius, .cornerRadiusBL = cornersRadius, .cornerRadiusBR = cornersRadius, .alpha = 1, .left = static_cast<int>(rect.fLeft), .top = static_cast<int>(rect.fTop), .right = static_cast<int>(rect.fRight), .bottom = static_cast<int>(rect.fBottom)}; SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent then we don't need a clip if (bounds == crop) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { bool intersectionIsRoundRect = true; // check each cropped corner to ensure that it exactly matches the crop or is full SkVector radii[4]; const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); // compute the UpperLeft corner radius if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) { radii[0].set(cornerRadius, cornerRadius); } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) { radii[0].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the UpperRight corner radius if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) { radii[1].set(cornerRadius, cornerRadius); } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) { radii[1].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the BottomRight corner radius if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) { radii[2].set(cornerRadius, cornerRadius); } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) { radii[2].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the BottomLeft corner radius if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) { radii[3].set(cornerRadius, cornerRadius); } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) { radii[3].set(0, 0); } else { intersectionIsRoundRect = false; } if (intersectionIsRoundRect) { SkRRect intersectionBounds; intersectionBounds.setRectRadii(bounds, radii); return {intersectionBounds, clip}; } } // we didn't it any of our fast paths so set the clip to the cropRect clip.setRectXY(crop, cornerRadius, cornerRadius); } // if we hit this point then we either don't have rounded corners or we are going to rely // on the clip to round the corners for us return {SkRRect::MakeRect(bounds), clip}; } inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) { Loading libs/renderengine/skia/SkiaGLRenderEngine.h +1 −2 Original line number Diff line number Diff line Loading @@ -88,8 +88,7 @@ private: int hwcFormat, Protection protection); inline SkRect getSkRect(const FloatRect& layer); inline SkRect getSkRect(const Rect& layer); inline SkRRect getRoundedRect(const LayerSettings* layer); inline BlurRegion getBlurRegion(const LayerSettings* layer); inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer); inline bool layerHasBlur(const LayerSettings* layer); inline SkColor getSkColor(const vec4& color); inline SkM44 getSkM44(const mat4& matrix); Loading libs/renderengine/skia/filters/BlurFilter.cpp +12 −23 Original line number Diff line number Diff line Loading @@ -139,23 +139,21 @@ static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRec return matrix; } void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input) { ATRACE_CALL(); SkPaint paint; paint.setAlphaf(effectRegion.alpha); if (effectRegion.alpha == 1.0f) { paint.setBlendMode(SkBlendMode::kSrc); } paint.setAlphaf(blurAlpha); const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale); SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, &blurMatrix); if (effectRegion.blurRadius < kMaxCrossFadeRadius) { if (blurRadius < kMaxCrossFadeRadius) { // For sampling Skia's API expects the inverse of what logically seems appropriate. In this // case you might expect the matrix to simply be the canvas matrix. SkMatrix inputMatrix; Loading @@ -168,30 +166,21 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion blurBuilder.child("originalInput") = input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, inputMatrix); blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius; blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius; paint.setShader(blurBuilder.makeShader(nullptr, true)); } else { paint.setShader(blurShader); } // TODO we should AA at least the drawRoundRect which would mean no SRC blending // TODO this round rect calculation doesn't match the one used to draw in RenderEngine auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, effectRegion.bottom); if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { const SkVector radii[4] = {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; SkRRect roundedRect; roundedRect.setRectRadii(rect, radii); canvas->drawRRect(roundedRect, paint); if (effectRegion.isRect()) { if (blurAlpha == 1.0f) { paint.setBlendMode(SkBlendMode::kSrc); } canvas->drawRect(effectRegion.rect(), paint); } else { canvas->drawRect(rect, paint); paint.setAntiAlias(true); canvas->drawRRect(effectRegion, paint); } } Loading libs/renderengine/skia/filters/BlurFilter.h +13 −3 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include <SkImage.h> #include <SkRuntimeEffect.h> #include <SkSurface.h> #include <ui/BlurRegion.h> using namespace std; Loading Loading @@ -52,8 +51,19 @@ public: sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, const sk_sp<SkImage> blurInput, const SkRect& blurRect) const; void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); /** * Draw the blurred content (from the generate method) into the canvas. * @param canvas is the destination/output for the blur * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn * @param blurRect bounds of the blurredImage translated into canvas coordinates * @param blurredImage down-sampled blurred content that was produced by the generate() method * @param input original unblurred input that is used to crossfade with the blurredImage */ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); private: sk_sp<SkRuntimeEffect> mBlurEffect; Loading libs/renderengine/tests/RenderEngineTest.cpp +50 −0 Original line number Diff line number Diff line Loading @@ -263,6 +263,11 @@ public: } } void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance); } void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { Loading Loading @@ -1887,6 +1892,51 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { 0, 255, 0, 255); } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { initializeRenderEngine(); renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings redLayer; redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; redLayer.geometry.boundaries = fullscreenRect().toFloatRect(); redLayer.geometry.roundedCornersRadius = 5.0f; redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); // Red background. redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); redLayer.alpha = 1.0f; layers.push_back(&redLayer); // Green layer with 1/2 size with parent crop rect. renderengine::LayerSettings greenLayer = redLayer; greenLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2); greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f); layers.push_back(&greenLayer); invokeDraw(settings, layers); // Due to roundedCornersRadius, the corners are untouched. expectBufferColor(Point(0, 0), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); // top middle should be green and the bottom middle red expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255); // the bottom edge of the green layer should not be rounded expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); Loading Loading
libs/renderengine/skia/SkiaGLRenderEngine.cpp +103 −29 Original line number Diff line number Diff line Loading @@ -667,6 +667,17 @@ void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect, canvas->drawRect(stretchBounds, paint); } static SkRRect getBlurRRect(const BlurRegion& region) { const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; SkRRect roundedRect; roundedRect.setRectRadii(rect, radii); return roundedRect; } status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, const std::shared_ptr<ExternalTexture>& buffer, Loading Loading @@ -840,7 +851,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // Layers have a local transform that should be applied to them canvas->concat(getSkM44(layer->geometry.positionTransform).asM33()); const auto bounds = getSkRect(layer->geometry.boundaries); const auto [bounds, roundRectClip] = getBoundsAndClip(layer); if (mBlurFilter && layerHasBlur(layer)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; Loading @@ -850,7 +861,14 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, blurInput = activeSurface->makeImageSnapshot(); } // rect to be blurred in the coordinate space of blurInput const auto blurRect = canvas->getTotalMatrix().mapRect(bounds); 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) { Loading @@ -862,10 +880,10 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, cachedBlurs[layer->backgroundBlurRadius] = blurredImage; mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage, blurInput); mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f, blurRect, blurredImage, blurInput); } SkAutoCanvasRestore acr(canvas, true); canvas->concat(getSkM44(layer->blurRegionTransform).asM33()); for (auto region : layer->blurRegions) { if (cachedBlurs[region.blurRadius] == nullptr) { Loading @@ -875,7 +893,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, blurRect); } mBlurFilter->drawBlurRegion(canvas, region, blurRect, mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, region.alpha, blurRect, cachedBlurs[region.blurRadius], blurInput); } } Loading @@ -888,7 +907,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, if (layer->shadow.length > 0) { const auto rect = layer->geometry.roundedCornersRadius > 0 ? getSkRect(layer->geometry.roundedCornersCrop) : bounds; : bounds.rect(); // This would require a new parameter/flag to SkShadowUtils::DrawShadow LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow"); drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); Loading Loading @@ -953,7 +972,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // The shader does not respect the translation, so we add it to the texture // transform for the SkImage. This will make sure that the correct layer contents // are drawn in the correct part of the screen. matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top); matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); sk_sp<SkShader> shader; Loading Loading @@ -1015,9 +1034,13 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, paint.setColorFilter(displayColorTransform); if (layer->geometry.roundedCornersRadius > 0) { if (!roundRectClip.isEmpty()) { canvas->clipRRect(roundRectClip, true); } if (!bounds.isRect()) { paint.setAntiAlias(true); canvas->drawRRect(getRoundedRect(layer), paint); canvas->drawRRect(bounds, paint); } else { auto& stretchEffect = layer->stretchEffect; // TODO (njawad) temporarily disable manipulation of geometry Loading @@ -1026,9 +1049,9 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // Keep the method call in a dead code path to make -Werror happy // with unused methods if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) { drawStretch(bounds, stretchEffect, canvas, paint); drawStretch(bounds.rect(), stretchEffect, canvas, paint); } else { canvas->drawRect(bounds, paint); canvas->drawRect(bounds.rect(), paint); } } if (kFlushAfterEveryLayer) { Loading Loading @@ -1077,25 +1100,76 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) { const auto rect = getSkRect(layer->geometry.roundedCornersCrop); inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip( const LayerSettings* layer) { const auto bounds = getSkRect(layer->geometry.boundaries); const auto crop = getSkRect(layer->geometry.roundedCornersCrop); const auto cornerRadius = layer->geometry.roundedCornersRadius; return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius); } inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) { const auto rect = getSkRect(layer->geometry.boundaries); const auto cornersRadius = layer->geometry.roundedCornersRadius; return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius), .cornerRadiusTL = cornersRadius, .cornerRadiusTR = cornersRadius, .cornerRadiusBL = cornersRadius, .cornerRadiusBR = cornersRadius, .alpha = 1, .left = static_cast<int>(rect.fLeft), .top = static_cast<int>(rect.fTop), .right = static_cast<int>(rect.fRight), .bottom = static_cast<int>(rect.fBottom)}; SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent then we don't need a clip if (bounds == crop) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { bool intersectionIsRoundRect = true; // check each cropped corner to ensure that it exactly matches the crop or is full SkVector radii[4]; const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); // compute the UpperLeft corner radius if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) { radii[0].set(cornerRadius, cornerRadius); } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) { radii[0].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the UpperRight corner radius if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) { radii[1].set(cornerRadius, cornerRadius); } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) { radii[1].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the BottomRight corner radius if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) { radii[2].set(cornerRadius, cornerRadius); } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) { radii[2].set(0, 0); } else { intersectionIsRoundRect = false; } // compute the BottomLeft corner radius if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) { radii[3].set(cornerRadius, cornerRadius); } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) { radii[3].set(0, 0); } else { intersectionIsRoundRect = false; } if (intersectionIsRoundRect) { SkRRect intersectionBounds; intersectionBounds.setRectRadii(bounds, radii); return {intersectionBounds, clip}; } } // we didn't it any of our fast paths so set the clip to the cropRect clip.setRectXY(crop, cornerRadius, cornerRadius); } // if we hit this point then we either don't have rounded corners or we are going to rely // on the clip to round the corners for us return {SkRRect::MakeRect(bounds), clip}; } inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) { Loading
libs/renderengine/skia/SkiaGLRenderEngine.h +1 −2 Original line number Diff line number Diff line Loading @@ -88,8 +88,7 @@ private: int hwcFormat, Protection protection); inline SkRect getSkRect(const FloatRect& layer); inline SkRect getSkRect(const Rect& layer); inline SkRRect getRoundedRect(const LayerSettings* layer); inline BlurRegion getBlurRegion(const LayerSettings* layer); inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer); inline bool layerHasBlur(const LayerSettings* layer); inline SkColor getSkColor(const vec4& color); inline SkM44 getSkM44(const mat4& matrix); Loading
libs/renderengine/skia/filters/BlurFilter.cpp +12 −23 Original line number Diff line number Diff line Loading @@ -139,23 +139,21 @@ static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRec return matrix; } void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input) { ATRACE_CALL(); SkPaint paint; paint.setAlphaf(effectRegion.alpha); if (effectRegion.alpha == 1.0f) { paint.setBlendMode(SkBlendMode::kSrc); } paint.setAlphaf(blurAlpha); const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale); SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, &blurMatrix); if (effectRegion.blurRadius < kMaxCrossFadeRadius) { if (blurRadius < kMaxCrossFadeRadius) { // For sampling Skia's API expects the inverse of what logically seems appropriate. In this // case you might expect the matrix to simply be the canvas matrix. SkMatrix inputMatrix; Loading @@ -168,30 +166,21 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion blurBuilder.child("originalInput") = input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, inputMatrix); blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius; blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius; paint.setShader(blurBuilder.makeShader(nullptr, true)); } else { paint.setShader(blurShader); } // TODO we should AA at least the drawRoundRect which would mean no SRC blending // TODO this round rect calculation doesn't match the one used to draw in RenderEngine auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, effectRegion.bottom); if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { const SkVector radii[4] = {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; SkRRect roundedRect; roundedRect.setRectRadii(rect, radii); canvas->drawRRect(roundedRect, paint); if (effectRegion.isRect()) { if (blurAlpha == 1.0f) { paint.setBlendMode(SkBlendMode::kSrc); } canvas->drawRect(effectRegion.rect(), paint); } else { canvas->drawRect(rect, paint); paint.setAntiAlias(true); canvas->drawRRect(effectRegion, paint); } } Loading
libs/renderengine/skia/filters/BlurFilter.h +13 −3 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include <SkImage.h> #include <SkRuntimeEffect.h> #include <SkSurface.h> #include <ui/BlurRegion.h> using namespace std; Loading Loading @@ -52,8 +51,19 @@ public: sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, const sk_sp<SkImage> blurInput, const SkRect& blurRect) const; void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); /** * Draw the blurred content (from the generate method) into the canvas. * @param canvas is the destination/output for the blur * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn * @param blurRect bounds of the blurredImage translated into canvas coordinates * @param blurredImage down-sampled blurred content that was produced by the generate() method * @param input original unblurred input that is used to crossfade with the blurredImage */ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); private: sk_sp<SkRuntimeEffect> mBlurEffect; Loading
libs/renderengine/tests/RenderEngineTest.cpp +50 −0 Original line number Diff line number Diff line Loading @@ -263,6 +263,11 @@ public: } } void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance); } void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { Loading Loading @@ -1887,6 +1892,51 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { 0, 255, 0, 255); } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { initializeRenderEngine(); renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings redLayer; redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; redLayer.geometry.boundaries = fullscreenRect().toFloatRect(); redLayer.geometry.roundedCornersRadius = 5.0f; redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); // Red background. redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); redLayer.alpha = 1.0f; layers.push_back(&redLayer); // Green layer with 1/2 size with parent crop rect. renderengine::LayerSettings greenLayer = redLayer; greenLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2); greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f); layers.push_back(&greenLayer); invokeDraw(settings, layers); // Due to roundedCornersRadius, the corners are untouched. expectBufferColor(Point(0, 0), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); // top middle should be green and the bottom middle red expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255); // the bottom edge of the green layer should not be rounded expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); Loading