diff options
| author | 2021-04-29 15:35:28 -0400 | |
|---|---|---|
| committer | 2021-05-05 08:28:22 -0400 | |
| commit | 8e8b3bf3ce440ca7f1873a9448dcba2a0ed5656b (patch) | |
| tree | 805b8e5b17afa79c1f996c2701f7bacbb416ae67 /libs | |
| parent | f938c3002201876665b12117604ff4520aca53ce (diff) | |
Update RenderEngine to properly respect the roundRectCrop.
Test: librenderengine_test; camera and SilkFX demo app
Bug: 186773838
Change-Id: Ib52d37ab08f1d0b7b4c41e187105ca7e002f3b99
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/renderengine/skia/SkiaGLRenderEngine.cpp | 132 | ||||
| -rw-r--r-- | libs/renderengine/skia/SkiaGLRenderEngine.h | 3 | ||||
| -rw-r--r-- | libs/renderengine/skia/filters/BlurFilter.cpp | 35 | ||||
| -rw-r--r-- | libs/renderengine/skia/filters/BlurFilter.h | 16 | ||||
| -rw-r--r-- | libs/renderengine/tests/RenderEngineTest.cpp | 50 |
5 files changed, 179 insertions, 57 deletions
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 3e3649edd4..d221a5855c 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -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, @@ -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; @@ -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) { @@ -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) { @@ -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); } } @@ -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); @@ -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; @@ -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 @@ -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) { @@ -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) { diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 98f5ee2081..3f5591a541 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -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); diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 5cba8fb011..4ad6e94edf 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -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; @@ -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); } } diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h index 731ba11483..a8e6dd7579 100644 --- a/libs/renderengine/skia/filters/BlurFilter.h +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -20,7 +20,6 @@ #include <SkImage.h> #include <SkRuntimeEffect.h> #include <SkSurface.h> -#include <ui/BlurRegion.h> using namespace std; @@ -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; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 85322a9f71..72e32ed9fd 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -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) { @@ -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(); |