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

Commit f28f5cec authored by Rob Tsuk's avatar Rob Tsuk Committed by Android (Google) Code Review
Browse files

Merge "Clipping performance improvements"

parents 45a669e5 487a92ca
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ LOCAL_SRC_FILES := \
    AssetAtlas.cpp \
    Caches.cpp \
    CanvasState.cpp \
    ClipArea.cpp \
    DamageAccumulator.cpp \
    DisplayList.cpp \
    DeferredDisplayList.cpp \
+7 −36
Original line number Diff line number Diff line
@@ -155,47 +155,18 @@ void CanvasState::concatMatrix(const Matrix4& matrix) {
///////////////////////////////////////////////////////////////////////////////

bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
    if (CC_LIKELY(currentTransform()->rectToRect())) {
    mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
        return !mSnapshot->clipRect->isEmpty();
    }

    SkPath path;
    path.addRect(left, top, right, bottom);

    return CanvasState::clipPath(&path, op);
    return !mSnapshot->clipIsEmpty();
}

bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
    SkMatrix transform;
    currentTransform()->copyTo(transform);

    SkPath transformed;
    path->transform(transform, &transformed);

    SkRegion clip;
    if (!mSnapshot->previous->clipRegion->isEmpty()) {
        clip.setRegion(*mSnapshot->previous->clipRegion);
    } else {
        if (mSnapshot->previous == firstSnapshot()) {
            clip.setRect(0, 0, getWidth(), getHeight());
        } else {
            Rect* bounds = mSnapshot->previous->clipRect;
            clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
        }
    }

    SkRegion region;
    region.setPath(transformed, clip);

    // region is the transformed input path, masked by the previous clip
    mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
    return !mSnapshot->clipRect->isEmpty();
    mDirtyClip |= mSnapshot->clipPath(*path, op);
    return !mSnapshot->clipIsEmpty();
}

bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
    mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
    return !mSnapshot->clipRect->isEmpty();
    return !mSnapshot->clipIsEmpty();
}

void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
@@ -245,7 +216,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top,
    currentTransform()->mapRect(r);
    r.snapGeometryToPixelBoundaries(snapOut);

    Rect clipRect(*currentClipRect());
    Rect clipRect(currentClipRect());
    clipRect.snapToPixelBoundaries();

    if (!clipRect.intersects(r)) return true;
@@ -273,7 +244,7 @@ bool CanvasState::quickRejectConservative(float left, float top,
    currentTransform()->mapRect(r);
    r.roundOut(); // rounded out to be conservative

    Rect clipRect(*currentClipRect());
    Rect clipRect(currentClipRect());
    clipRect.snapToPixelBoundaries();

    if (!clipRect.intersects(r)) return true;
+1 −5
Original line number Diff line number Diff line
@@ -120,10 +120,6 @@ public:
    bool clipPath(const SkPath* path, SkRegion::Op op);
    bool clipRegion(const SkRegion* region, SkRegion::Op op);

    bool isCurrentClipSimple() const {
        return currentSnapshot()->clipRegion->isEmpty();
    }

    /**
     * Sets a "clipping outline", which is independent from the regular clip.
     * Currently only supports rectangles or rounded rectangles; passing in a
@@ -150,7 +146,7 @@ public:
    void setInvisible(bool value) { mSnapshot->invisible = value; }

    inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
    inline const Rect* currentClipRect() const { return currentSnapshot()->clipRect; }
    inline const Rect& currentClipRect() const { return currentSnapshot()->getClipRect(); }
    inline Region* currentRegion() const { return currentSnapshot()->region; }
    inline int currentFlags() const { return currentSnapshot()->flags; }
    const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }

libs/hwui/ClipArea.cpp

0 → 100644
+368 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "ClipArea.h"

#include <SkPath.h>
#include <limits>

#include "Rect.h"

namespace android {
namespace uirenderer {

static bool intersect(Rect& r, const Rect& r2) {
    bool hasIntersection = r.intersect(r2);
    if (!hasIntersection) {
        r.setEmpty();
    }
    return hasIntersection;
}

static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
    Vertex v;
    v.x = x;
    v.y = y;
    transform.mapPoint(v.x, v.y);
    transformedBounds.expandToCoverVertex(v.x, v.y);
}

Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
    const float kMinFloat = std::numeric_limits<float>::lowest();
    const float kMaxFloat = std::numeric_limits<float>::max();
    Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
    handlePoint(transformedBounds, transform, r.left, r.top);
    handlePoint(transformedBounds, transform, r.right, r.top);
    handlePoint(transformedBounds, transform, r.left, r.bottom);
    handlePoint(transformedBounds, transform, r.right, r.bottom);
    return transformedBounds;
}

/*
 * TransformedRectangle
 */

TransformedRectangle::TransformedRectangle() {
}

TransformedRectangle::TransformedRectangle(const Rect& bounds,
        const Matrix4& transform)
        : mBounds(bounds)
        , mTransform(transform) {
}

bool TransformedRectangle::canSimplyIntersectWith(
        const TransformedRectangle& other) const {

    return mTransform == other.mTransform;
}

bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
    Rect translatedBounds(other.mBounds);
    return intersect(mBounds, translatedBounds);
}

bool TransformedRectangle::isEmpty() const {
    return mBounds.isEmpty();
}

/*
 * RectangleList
 */

RectangleList::RectangleList()
        : mTransformedRectanglesCount(0) {
}

bool RectangleList::isEmpty() const {
    if (mTransformedRectanglesCount < 1) {
        return true;
    }

    for (int i = 0; i < mTransformedRectanglesCount; i++) {
        if (mTransformedRectangles[i].isEmpty()) {
            return true;
        }
    }
    return false;
}

int RectangleList::getTransformedRectanglesCount() const {
    return mTransformedRectanglesCount;
}

const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
    return mTransformedRectangles[i];
}

void RectangleList::setEmpty() {
    mTransformedRectanglesCount = 0;
}

void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
    mTransformedRectanglesCount = 1;
    mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
}

bool RectangleList::intersectWith(const Rect& bounds,
        const Matrix4& transform) {
    TransformedRectangle newRectangle(bounds, transform);

    // Try to find a rectangle with a compatible transformation
    int index = 0;
    for (; index < mTransformedRectanglesCount; index++) {
        TransformedRectangle& tr(mTransformedRectangles[index]);
        if (tr.canSimplyIntersectWith(newRectangle)) {
            tr.intersectWith(newRectangle);
            return true;
        }
    }

    // Add it to the list if there is room
    if (index < kMaxTransformedRectangles) {
        mTransformedRectangles[index] = newRectangle;
        mTransformedRectanglesCount += 1;
        return true;
    }

    // This rectangle list is full
    return false;
}

Rect RectangleList::calculateBounds() const {
    Rect bounds;
    for (int index = 0; index < mTransformedRectanglesCount; index++) {
        const TransformedRectangle& tr(mTransformedRectangles[index]);
        if (index == 0) {
            bounds = tr.transformedBounds();
        } else {
            bounds.intersect(tr.transformedBounds());
        }
    }
    return bounds;
}

static SkPath pathFromTransformedRectangle(const Rect& bounds,
        const Matrix4& transform) {
    SkPath rectPath;
    SkPath rectPathTransformed;
    rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
    SkMatrix skTransform;
    transform.copyTo(skTransform);
    rectPath.transform(skTransform, &rectPathTransformed);
    return rectPathTransformed;
}

SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
    SkRegion rectangleListAsRegion;
    for (int index = 0; index < mTransformedRectanglesCount; index++) {
        const TransformedRectangle& tr(mTransformedRectangles[index]);
        SkPath rectPathTransformed = pathFromTransformedRectangle(
                tr.getBounds(), tr.getTransform());
        if (index == 0) {
            rectangleListAsRegion.setPath(rectPathTransformed, clip);
        } else {
            SkRegion rectRegion;
            rectRegion.setPath(rectPathTransformed, clip);
            rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
        }
    }
    return rectangleListAsRegion;
}

/*
 * ClipArea
 */

ClipArea::ClipArea()
        : mMode(kModeRectangle) {
}

/*
 * Interface
 */

void ClipArea::setViewportDimensions(int width, int height) {
    mViewportBounds.set(0, 0, width, height);
    mClipRect = mViewportBounds;
}

void ClipArea::setEmpty() {
    mMode = kModeRectangle;
    mClipRect.setEmpty();
    mClipRegion.setEmpty();
    mRectangleList.setEmpty();
}

void ClipArea::setClip(float left, float top, float right, float bottom) {
    mMode = kModeRectangle;
    mClipRect.set(left, top, right, bottom);
    mClipRegion.setEmpty();
}

bool ClipArea::clipRectWithTransform(float left, float top, float right,
        float bottom, const mat4* transform, SkRegion::Op op) {
    Rect r(left, top, right, bottom);
    return clipRectWithTransform(r, transform, op);
}

bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
        SkRegion::Op op) {
    switch (mMode) {
    case kModeRectangle:
        return rectangleModeClipRectWithTransform(r, transform, op);
    case kModeRectangleList:
        return rectangleListModeClipRectWithTransform(r, transform, op);
    case kModeRegion:
        return regionModeClipRectWithTransform(r, transform, op);
    }
    return false;
}

bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
    enterRegionMode();
    mClipRegion.op(region, op);
    setClipRectToRegionBounds();
    return true;
}

bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
        SkRegion::Op op) {
    SkMatrix skTransform;
    transform->copyTo(skTransform);
    SkPath transformed;
    path.transform(skTransform, &transformed);
    SkRegion region;
    regionFromPath(transformed, region);
    return clipRegion(region, op);
}

/*
 * Rectangle mode
 */

void ClipArea::enterRectangleMode() {
    // Entering rectangle mode discards any
    // existing clipping information from the other modes.
    // The only way this occurs is by a clip setting operation.
    mMode = kModeRectangle;
}

bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
        const mat4* transform, SkRegion::Op op) {

    if (op != SkRegion::kIntersect_Op) {
        enterRegionMode();
        return regionModeClipRectWithTransform(r, transform, op);
    }

    if (transform->rectToRect()) {
        Rect transformed(r);
        transform->mapRect(transformed);
        bool hasIntersection = mClipRect.intersect(transformed);
        if (!hasIntersection) {
            mClipRect.setEmpty();
        }
        return true;
    }

    enterRectangleListMode();
    return rectangleListModeClipRectWithTransform(r, transform, op);
}

bool ClipArea::rectangleModeClipRectWithTransform(float left, float top,
        float right, float bottom, const mat4* transform, SkRegion::Op op) {
    Rect r(left, top, right, bottom);
    bool result = rectangleModeClipRectWithTransform(r, transform, op);
    mClipRect = mRectangleList.calculateBounds();
    return result;
}

/*
 * RectangleList mode implementation
 */

void ClipArea::enterRectangleListMode() {
    // Is is only legal to enter rectangle list mode from
    // rectangle mode, since rectangle list mode cannot represent
    // all clip areas that can be represented by a region.
    ALOG_ASSERT(mMode == kModeRectangle);
    mMode = kModeRectangleList;
    mRectangleList.set(mClipRect, Matrix4::identity());
}

bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
        const mat4* transform, SkRegion::Op op) {
    if (op != SkRegion::kIntersect_Op
            || !mRectangleList.intersectWith(r, *transform)) {
        enterRegionMode();
        return regionModeClipRectWithTransform(r, transform, op);
    }
    return true;
}

bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
        float right, float bottom, const mat4* transform, SkRegion::Op op) {
    Rect r(left, top, right, bottom);
    return rectangleListModeClipRectWithTransform(r, transform, op);
}

/*
 * Region mode implementation
 */

void ClipArea::enterRegionMode() {
    if (mMode != kModeRegion) {
        if (mMode == kModeRectangle) {
            mClipRegion.setRect(mClipRect.left, mClipRect.top,
                    mClipRect.right, mClipRect.bottom);
        } else {
            mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
            setClipRectToRegionBounds();
        }
        mMode = kModeRegion;
    }
}

bool ClipArea::regionModeClipRectWithTransform(const Rect& r,
        const mat4* transform, SkRegion::Op op) {
    SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
    SkRegion transformedRectRegion;
    regionFromPath(transformedRect, transformedRectRegion);
    mClipRegion.op(transformedRectRegion, op);
    setClipRectToRegionBounds();
    return true;
}

bool ClipArea::regionModeClipRectWithTransform(float left, float top,
        float right, float bottom, const mat4* transform, SkRegion::Op op) {
    return regionModeClipRectWithTransform(Rect(left, top, right, bottom),
            transform, op);
}

void ClipArea::setClipRectToRegionBounds() {
    if (!mClipRegion.isEmpty()) {
        mClipRect.set(mClipRegion.getBounds());

        if (mClipRegion.isRect()) {
            mClipRegion.setEmpty();
        }
    } else {
        mClipRect.setEmpty();
    }
}

} /* namespace uirenderer */
} /* namespace android */

libs/hwui/ClipArea.h

0 → 100644
+175 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef CLIPAREA_H
#define CLIPAREA_H

#include <SkRegion.h>

#include "Matrix.h"
#include "Rect.h"
#include "utils/Pair.h"

namespace android {
namespace uirenderer {

Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);

class TransformedRectangle {
public:
    TransformedRectangle();
    TransformedRectangle(const Rect& bounds, const Matrix4& transform);

    bool canSimplyIntersectWith(const TransformedRectangle& other) const;
    bool intersectWith(const TransformedRectangle& other);

    bool isEmpty() const;

    const Rect& getBounds() const {
        return mBounds;
    }

    Rect transformedBounds() const {
        Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
        return transformedBounds;
    }

    const Matrix4& getTransform() const {
        return mTransform;
    }

private:
    Rect mBounds;
    Matrix4 mTransform;
};

class RectangleList {
public:
    RectangleList();

    bool isEmpty() const;
    int getTransformedRectanglesCount() const;
    const TransformedRectangle& getTransformedRectangle(int i) const;

    void setEmpty();
    void set(const Rect& bounds, const Matrix4& transform);
    bool intersectWith(const Rect& bounds, const Matrix4& transform);

    SkRegion convertToRegion(const SkRegion& clip) const;
    Rect calculateBounds() const;

private:
    enum {
        kMaxTransformedRectangles = 5
    };

    int mTransformedRectanglesCount;
    TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
};

class ClipArea {
public:
    ClipArea();

    void setViewportDimensions(int width, int height);

    bool isEmpty() const {
        return mClipRect.isEmpty();
    }

    void setEmpty();
    void setClip(float left, float top, float right, float bottom);
    bool clipRectWithTransform(float left, float top, float right, float bottom,
            const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op);
    bool clipRectWithTransform(const Rect& r, const mat4* transform,
            SkRegion::Op op = SkRegion::kIntersect_Op);
    bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op);
    bool clipPathWithTransform(const SkPath& path, const mat4* transform,
            SkRegion::Op op);

    const Rect& getClipRect() const {
        return mClipRect;
    }

    const SkRegion& getClipRegion() const {
        return mClipRegion;
    }

    const RectangleList& getRectangleList() const {
        return mRectangleList;
    }

    bool isRegion() const {
        return kModeRegion == mMode;
    }

    bool isSimple() const {
        return mMode == kModeRectangle;
    }

    bool isRectangleList() const {
        return mMode == kModeRectangleList;
    }

private:
    void enterRectangleMode();
    bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
    bool rectangleModeClipRectWithTransform(float left, float top, float right,
            float bottom, const mat4* transform, SkRegion::Op op);

    void enterRectangleListMode();
    bool rectangleListModeClipRectWithTransform(float left, float top,
            float right, float bottom, const mat4* transform, SkRegion::Op op);
    bool rectangleListModeClipRectWithTransform(const Rect& r,
            const mat4* transform, SkRegion::Op op);

    void enterRegionModeFromRectangleMode();
    void enterRegionModeFromRectangleListMode();
    void enterRegionMode();
    bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
            SkRegion::Op op);
    bool regionModeClipRectWithTransform(float left, float top, float right,
            float bottom, const mat4* transform, SkRegion::Op op);

    void ensureClipRegion();
    void setClipRectToRegionBounds();
    bool clipRegionOp(float left, float top, float right, float bottom,
            SkRegion::Op op);

    SkRegion createViewportRegion() {
        return SkRegion(mViewportBounds.toSkIRect());
    }

    void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
        pathAsRegion.setPath(path, createViewportRegion());
    }

    enum Mode {
        kModeRectangle,
        kModeRegion,
        kModeRectangleList
    };

    Mode mMode;
    Rect mViewportBounds;
    Rect mClipRect;
    SkRegion mClipRegion;
    RectangleList mRectangleList;
};

} /* namespace uirenderer */
} /* namespace android */

#endif /* CLIPAREA_H_ */
Loading