Clipping performance improvements

Create a ClipArea class to handle tracking clip regions. This class can
select the most efficient implementation depending on the types of
clipping presented.

ClipArea re-used the rectangle and region-based clipping
implementations as well as adding a "list of rotated rectangles"
approach that is more efficient for rotated views with children.

Change-Id: I2133761a2462ebc0852b394220e265974b3086f0
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index d4ca99e..49fb4ba 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -35,11 +35,10 @@
         , invisible(false)
         , empty(false)
         , alpha(1.0f)
-        , roundRectClipState(nullptr) {
+        , roundRectClipState(nullptr)
+        , mClipArea(&mClipAreaRoot) {
     transform = &mTransformRoot;
-    clipRect = &mClipRectRoot;
     region = nullptr;
-    clipRegion = &mClipRegionRoot;
 }
 
 /**
@@ -55,6 +54,7 @@
         , empty(false)
         , alpha(s->alpha)
         , roundRectClipState(s->roundRectClipState)
+        , mClipArea(nullptr)
         , mViewportData(s->mViewportData)
         , mRelativeLightCenter(s->mRelativeLightCenter) {
     if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
@@ -65,15 +65,10 @@
     }
 
     if (saveFlags & SkCanvas::kClip_SaveFlag) {
-        mClipRectRoot.set(*s->clipRect);
-        clipRect = &mClipRectRoot;
-        if (!s->clipRegion->isEmpty()) {
-            mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
-        }
-        clipRegion = &mClipRegionRoot;
+        mClipAreaRoot = s->getClipArea();
+        mClipArea = &mClipAreaRoot;
     } else {
-        clipRect = s->clipRect;
-        clipRegion = s->clipRegion;
+        mClipArea = s->mClipArea;
     }
 
     if (s->flags & Snapshot::kFlagFboTarget) {
@@ -88,88 +83,23 @@
 // Clipping
 ///////////////////////////////////////////////////////////////////////////////
 
-void Snapshot::ensureClipRegion() {
-    if (clipRegion->isEmpty()) {
-        clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
-    }
-}
-
-void Snapshot::copyClipRectFromRegion() {
-    if (!clipRegion->isEmpty()) {
-        const SkIRect& bounds = clipRegion->getBounds();
-        clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
-
-        if (clipRegion->isRect()) {
-            clipRegion->setEmpty();
-        }
-    } else {
-        clipRect->setEmpty();
-    }
-}
-
-bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
-    SkIRect tmp;
-    tmp.set(left, top, right, bottom);
-    clipRegion->op(tmp, op);
-    copyClipRectFromRegion();
-    return true;
-}
-
 bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
-    ensureClipRegion();
-    clipRegion->op(region, op);
-    copyClipRectFromRegion();
     flags |= Snapshot::kFlagClipSet;
-    return true;
+    return mClipArea->clipRegion(region, op);
 }
 
 bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
-    Rect r(left, top, right, bottom);
-    transform->mapRect(r);
-    return clipTransformed(r, op);
+    flags |= Snapshot::kFlagClipSet;
+    return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
 }
 
-bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
-    bool clipped = false;
-
-    switch (op) {
-        case SkRegion::kIntersect_Op: {
-            if (CC_UNLIKELY(!clipRegion->isEmpty())) {
-                ensureClipRegion();
-                clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
-            } else {
-                clipped = clipRect->intersect(r);
-                if (!clipped) {
-                    clipRect->setEmpty();
-                    clipped = true;
-                }
-            }
-            break;
-        }
-        case SkRegion::kReplace_Op: {
-            setClip(r.left, r.top, r.right, r.bottom);
-            clipped = true;
-            break;
-        }
-        default: {
-            ensureClipRegion();
-            clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
-            break;
-        }
-    }
-
-    if (clipped) {
-        flags |= Snapshot::kFlagClipSet;
-    }
-
-    return clipped;
+bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
+    flags |= Snapshot::kFlagClipSet;
+    return mClipArea->clipPathWithTransform(path, transform, op);
 }
 
 void Snapshot::setClip(float left, float top, float right, float bottom) {
-    clipRect->set(left, top, right, bottom);
-    if (!clipRegion->isEmpty()) {
-        clipRegion->setEmpty();
-    }
+    mClipArea->setClip(left, top, right, bottom);
     flags |= Snapshot::kFlagClipSet;
 }
 
@@ -181,7 +111,7 @@
     mat4 inverse;
     inverse.loadInverse(*transform);
 
-    mLocalClip.set(*clipRect);
+    mLocalClip.set(mClipArea->getClipRect());
     inverse.mapRect(mLocalClip);
 
     return mLocalClip;
@@ -191,8 +121,7 @@
     // TODO: This is incorrect, when we start rendering into a new layer,
     // we may have to modify the previous snapshot's clip rect and clip
     // region if the previous restore() call did not restore the clip
-    clipRect = &mClipRectRoot;
-    clipRegion = &mClipRegionRoot;
+    mClipArea = &mClipAreaRoot;
     setClip(left, top, right, bottom);
 }
 
@@ -219,7 +148,7 @@
 void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
         float radius, bool highPriority) {
     if (bounds.isEmpty()) {
-        clipRect->setEmpty();
+        mClipArea->setEmpty();
         return;
     }
 
@@ -272,9 +201,10 @@
 
 void Snapshot::dump() const {
     ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
-            this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty());
+            this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+    const Rect& clipRect(mClipArea->getClipRect());
     ALOGD("  ClipRect (at %p) %.1f %.1f %.1f %.1f",
-            clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+            clipRect, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
     ALOGD("  Transform (at %p):", transform);
     transform->dump();
 }