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

Commit 678ff811 authored by Chris Craik's avatar Chris Craik
Browse files

Clip projected ripples to outlines

bug:27343928

Also fixes positioning of ripples to a scrolled projection receiver.

Change-Id: I74b7233c46d7c15839ca8bf50e188ba6646d7432
parent 82197c33
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <algorithm>
#include <math.h>
#include <SkPaintDefaults.h>
#include <SkPathOps.h>

namespace android {
namespace uirenderer {
@@ -527,6 +528,12 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co
        SkPath path;
        SkRect rect = getBoundsOfFill(op);
        path.addOval(rect);

        if (state.computedState.localProjectionPathMask != nullptr) {
            // Mask the ripple path by the local space projection mask in local space.
            // Note that this can create CCW paths.
            Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path);
        }
        renderConvexPath(renderer, state, path, *(op.paint));
    }
}
+18 −3
Original line number Diff line number Diff line
@@ -63,9 +63,22 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s
        clipState = nullptr;
        clippedBounds.setEmpty();
    } else {
        // Not rejected! compute true clippedBounds and clipSideFlags
        // Not rejected! compute true clippedBounds, clipSideFlags, and path mask
        clipSideFlags = computeClipSideFlags(clipRect, clippedBounds);
        clippedBounds.doIntersect(clipRect);

        if (CC_UNLIKELY(snapshot.projectionPathMask)) {
            // map projection path mask from render target space into op space,
            // so intersection with op geometry is possible
            Matrix4 inverseTransform;
            inverseTransform.loadInverse(transform);
            SkMatrix skInverseTransform;
            inverseTransform.copyTo(skInverseTransform);

            auto localMask = allocator.create<SkPath>();
            snapshot.projectionPathMask->transform(skInverseTransform, localMask);
            localProjectionPathMask = localMask;
        }
    }
}

@@ -73,13 +86,15 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s
        : transform(*snapshot.transform)
        , clipState(snapshot.mutateClipArea().serializeClip(allocator))
        , clippedBounds(clipState->rect)
        , clipSideFlags(OpClipSideFlags::Full) {}
        , clipSideFlags(OpClipSideFlags::Full)
        , localProjectionPathMask(nullptr) {}

ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect)
        : transform(Matrix4::identity())
        , clipState(clipRect)
        , clippedBounds(dstRect)
        , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) {
        , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect))
        , localProjectionPathMask(nullptr) {
    clippedBounds.doIntersect(clipRect->rect);
}

+1 −4
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ public:
    const ClipBase* clipState = nullptr;
    Rect clippedBounds;
    int clipSideFlags = 0;
    const SkPath* localProjectionPathMask = nullptr;
};

/**
@@ -154,7 +155,6 @@ public:
    // simple state (straight pointer/value storage):
    const float alpha;
    const RoundRectClipState* roundRectClipState;
    const ProjectionPathMask* projectionPathMask;
    const RecordedOp* op;

private:
@@ -165,21 +165,18 @@ private:
            : computedState(allocator, snapshot, recordedOp, expandForStroke)
            , alpha(snapshot.alpha)
            , roundRectClipState(snapshot.roundRectClipState)
            , projectionPathMask(snapshot.projectionPathMask)
            , op(&recordedOp) {}

    BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr)
            : computedState(allocator, snapshot)
            , alpha(snapshot.alpha)
            , roundRectClipState(snapshot.roundRectClipState)
            , projectionPathMask(snapshot.projectionPathMask)
            , op(shadowOpPtr) {}

    BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp)
            : computedState(clipRect, dstRect)
            , alpha(1.0f)
            , roundRectClipState(nullptr)
            , projectionPathMask(nullptr)
            , op(&recordedOp) {}
};

+15 −18
Original line number Diff line number Diff line
@@ -389,34 +389,31 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
}

void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
    const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
    int count = mCanvasState.save(SaveFlags::MatrixClip);
    const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();

    // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
    const DisplayList& displayList = *(renderNode.getDisplayList());

    const RecordedOp* op = (displayList.getOps()[displayList.projectionReceiveIndex]);
    const RenderNodeOp* backgroundOp = static_cast<const RenderNodeOp*>(op);
    const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();

    // Transform renderer to match background we're projecting onto
    // (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
    mCanvasState.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());

    // If the projection receiver has an outline, we mask projected content to it
    // (which we know, apriori, are all tessellated paths)
    mCanvasState.setProjectionPathMask(mAllocator, projectionReceiverOutline);
    SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy
    if (projectionReceiverOutline) {
        // transform the mask for this projector into render target space
        // TODO: consider combining both transforms by stashing transform instead of applying
        SkMatrix skCurrentTransform;
        mCanvasState.currentTransform()->copyTo(skCurrentTransform);
        projectionReceiverOutline->transform(
                skCurrentTransform,
                &transformedMaskPath);
        mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath);
    }

    // draw projected nodes
    for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
        RenderNodeOp* childOp = renderNode.mProjectedNodes[i];

        int restoreTo = mCanvasState.save(SaveFlags::Matrix);

        // Apply transform between ancestor and projected descendant
        mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);

        deferRenderNodeOpImpl(*childOp);
        mCanvasState.restoreToCount(restoreTo);
    }

    mCanvasState.restoreToCount(count);
}

+4 −1
Original line number Diff line number Diff line
@@ -140,7 +140,10 @@ public:
        // Identical round rect clip state means both ops will clip in the same way, or not at all.
        // As the state objects are const, we can compare their pointers to determine mergeability
        if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
        if (lhs->projectionPathMask != rhs->projectionPathMask) return false;

        // Local masks prevent merge, since they're potentially in different coordinate spaces
        if (lhs->computedState.localProjectionPathMask
                || rhs->computedState.localProjectionPathMask) return false;

        /* Clipping compatibility check
         *
Loading