Update RenderEngine to properly respect the roundRectCrop.

Test: librenderengine_test; camera and SilkFX demo app
Bug: 186773838
Change-Id: Ib52d37ab08f1d0b7b4c41e187105ca7e002f3b99
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 3e3649e..d221a58 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -667,6 +667,17 @@
     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 @@
         // 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 @@
                 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 @@
 
                     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 @@
                                                       blurRect);
                     }
 
-                    mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+                    mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+                                                region.alpha, blurRect,
                                                 cachedBlurs[region.blurRadius], blurInput);
                 }
             }
@@ -888,7 +907,7 @@
         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 @@
             // 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 @@
 
         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 @@
             // 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 @@
     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 98f5ee2..3f5591a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -88,8 +88,7 @@
                                                          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 5cba8fb..4ad6e94 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -139,23 +139,21 @@
     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 @@
         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 731ba11..a8e6dd7 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 @@
     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 85322a9..72e32ed 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -263,6 +263,11 @@
         }
     }
 
+    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 @@
                       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();