Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03fd6277 authored by Derek Sollenberger's avatar Derek Sollenberger Committed by Android (Google) Code Review
Browse files

Merge "Update RenderEngine to properly respect the roundRectCrop." into sc-dev

parents 50e37fef 8e8b3bf3
Loading
Loading
Loading
Loading
+103 −29
Original line number Diff line number Diff line
@@ -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) {
+1 −2
Original line number Diff line number Diff line
@@ -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);
+12 −23
Original line number Diff line number Diff line
@@ -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);
    }
}

+13 −3
Original line number Diff line number Diff line
@@ -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;
+50 −0
Original line number Diff line number Diff line
@@ -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();