Merge "Preliminary support for clipRect(Rect, Op)"
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8d32edc..7658d04 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12734,9 +12734,9 @@
      * </p>
      *
      * <p>
-     * The actual mesurement work of a view is performed in
+     * The actual measurement work of a view is performed in
      * {@link #onMeasure(int, int)}, called by this method. Therefore, only
-     * {@link #onMeasure(int, int)} can and must be overriden by subclasses.
+     * {@link #onMeasure(int, int)} can and must be overridden by subclasses.
      * </p>
      *
      *
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 6c9a620..f242f18 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -55,43 +55,51 @@
             void        set(uint32_t w, uint32_t h);
         
             Region&     orSelf(const Rect& rhs);
+            Region&     xorSelf(const Rect& rhs);
             Region&     andSelf(const Rect& rhs);
             Region&     subtractSelf(const Rect& rhs);
 
             // boolean operators, applied on this
             Region&     orSelf(const Region& rhs);
+            Region&     xorSelf(const Region& rhs);
             Region&     andSelf(const Region& rhs);
             Region&     subtractSelf(const Region& rhs);
 
             // boolean operators
     const   Region      merge(const Rect& rhs) const;
+    const   Region      mergeExclusive(const Rect& rhs) const;
     const   Region      intersect(const Rect& rhs) const;
     const   Region      subtract(const Rect& rhs) const;
 
             // boolean operators
     const   Region      merge(const Region& rhs) const;
+    const   Region      mergeExclusive(const Region& rhs) const;
     const   Region      intersect(const Region& rhs) const;
     const   Region      subtract(const Region& rhs) const;
 
             // these translate rhs first
             Region&     translateSelf(int dx, int dy);
             Region&     orSelf(const Region& rhs, int dx, int dy);
+            Region&     xorSelf(const Region& rhs, int dx, int dy);
             Region&     andSelf(const Region& rhs, int dx, int dy);
             Region&     subtractSelf(const Region& rhs, int dx, int dy);
 
             // these translate rhs first
     const   Region      translate(int dx, int dy) const;
     const   Region      merge(const Region& rhs, int dx, int dy) const;
+    const   Region      mergeExclusive(const Region& rhs, int dx, int dy) const;
     const   Region      intersect(const Region& rhs, int dx, int dy) const;
     const   Region      subtract(const Region& rhs, int dx, int dy) const;
 
     // convenience operators overloads
     inline  const Region      operator | (const Region& rhs) const;
+    inline  const Region      operator ^ (const Region& rhs) const;
     inline  const Region      operator & (const Region& rhs) const;
     inline  const Region      operator - (const Region& rhs) const;
     inline  const Region      operator + (const Point& pt) const;
 
     inline  Region&     operator |= (const Region& rhs);
+    inline  Region&     operator ^= (const Region& rhs);
     inline  Region&     operator &= (const Region& rhs);
     inline  Region&     operator -= (const Region& rhs);
     inline  Region&     operator += (const Point& pt);
@@ -158,6 +166,9 @@
 const Region Region::operator | (const Region& rhs) const {
     return merge(rhs);
 }
+const Region Region::operator ^ (const Region& rhs) const {
+    return mergeExclusive(rhs);
+}
 const Region Region::operator & (const Region& rhs) const {
     return intersect(rhs);
 }
@@ -172,6 +183,9 @@
 Region& Region::operator |= (const Region& rhs) {
     return orSelf(rhs);
 }
+Region& Region::operator ^= (const Region& rhs) {
+    return xorSelf(rhs);
+}
 Region& Region::operator &= (const Region& rhs) {
     return andSelf(rhs);
 }
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index a85362d..5f801fb 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -31,6 +31,7 @@
     transform = &mTransformRoot;
     clipRect = &mClipRectRoot;
     region = NULL;
+    clipRegion = NULL;
 }
 
 /**
@@ -52,8 +53,21 @@
     if (saveFlags & SkCanvas::kClip_SaveFlag) {
         mClipRectRoot.set(*s->clipRect);
         clipRect = &mClipRectRoot;
+#if STENCIL_BUFFER_SIZE
+        if (s->clipRegion) {
+            mClipRegionRoot.merge(*s->clipRegion);
+            clipRegion = &mClipRegionRoot;
+        } else {
+            clipRegion = NULL;
+        }
+#else
+        clipRegion = NULL;
+#endif
     } else {
         clipRect = s->clipRect;
+#if STENCIL_BUFFER_SIZE
+        clipRegion = s->clipRegion;
+#endif
     }
 
     if (s->flags & Snapshot::kFlagFboTarget) {
@@ -68,6 +82,77 @@
 // Clipping
 ///////////////////////////////////////////////////////////////////////////////
 
+void Snapshot::ensureClipRegion() {
+#if STENCIL_BUFFER_SIZE
+    if (!clipRegion) {
+        clipRegion = &mClipRegionRoot;
+        android::Rect tmp(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+        clipRegion->set(tmp);
+    }
+#endif
+}
+
+void Snapshot::copyClipRectFromRegion() {
+#if STENCIL_BUFFER_SIZE
+    if (!clipRegion->isEmpty()) {
+        android::Rect bounds(clipRegion->bounds());
+        clipRect->set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+
+        if (clipRegion->isRect()) {
+            clipRegion->clear();
+            clipRegion = NULL;
+        }
+    } else {
+        clipRect->setEmpty();
+        clipRegion = NULL;
+    }
+#endif
+}
+
+bool Snapshot::clipRegionOr(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->orSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionXor(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->xorSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionAnd(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->andSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionNand(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->subtractSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
 bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
     Rect r(left, top, right, bottom);
     transform->mapRect(r);
@@ -77,32 +162,46 @@
 bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
     bool clipped = false;
 
-    // NOTE: The unimplemented operations require support for regions
-    // Supporting regions would require using a stencil buffer instead
-    // of the scissor. The stencil buffer itself is not too expensive
-    // (memory cost excluded) but on fillrate limited devices, managing
-    // the stencil might have a negative impact on the framerate.
     switch (op) {
-        case SkRegion::kDifference_Op:
+        case SkRegion::kDifference_Op: {
+            ensureClipRegion();
+            clipped = clipRegionNand(r.left, r.top, r.right, r.bottom);
             break;
-        case SkRegion::kIntersect_Op:
-            clipped = clipRect->intersect(r);
-            if (!clipped) {
-                clipRect->setEmpty();
-                clipped = true;
+        }
+        case SkRegion::kIntersect_Op: {
+            if (CC_UNLIKELY(clipRegion)) {
+                clipped = clipRegionOr(r.left, r.top, r.right, r.bottom);
+            } else {
+                clipped = clipRect->intersect(r);
+                if (!clipped) {
+                    clipRect->setEmpty();
+                    clipped = true;
+                }
             }
             break;
-        case SkRegion::kUnion_Op:
-            clipped = clipRect->unionWith(r);
+        }
+        case SkRegion::kUnion_Op: {
+            if (CC_UNLIKELY(clipRegion)) {
+                clipped = clipRegionAnd(r.left, r.top, r.right, r.bottom);
+            } else {
+                clipped = clipRect->unionWith(r);
+            }
             break;
-        case SkRegion::kXOR_Op:
+        }
+        case SkRegion::kXOR_Op: {
+            ensureClipRegion();
+            clipped = clipRegionXor(r.left, r.top, r.right, r.bottom);
             break;
-        case SkRegion::kReverseDifference_Op:
+        }
+        case SkRegion::kReverseDifference_Op: {
+            // TODO!!!!!!!
             break;
-        case SkRegion::kReplace_Op:
-            clipRect->set(r);
+        }
+        case SkRegion::kReplace_Op: {
+            setClip(r.left, r.top, r.right, r.bottom);
             clipped = true;
             break;
+        }
     }
 
     if (clipped) {
@@ -114,6 +213,10 @@
 
 void Snapshot::setClip(float left, float top, float right, float bottom) {
     clipRect->set(left, top, right, bottom);
+    if (clipRegion) {
+        clipRegion->clear();
+        clipRegion = NULL;
+    }
     flags |= Snapshot::kFlagClipSet;
 }
 
@@ -129,8 +232,7 @@
 
 void Snapshot::resetClip(float left, float top, float right, float bottom) {
     clipRect = &mClipRectRoot;
-    clipRect->set(left, top, right, bottom);
-    flags |= Snapshot::kFlagClipSet;
+    setClip(left, top, right, bottom);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index c94af7e..b2bc879 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -181,7 +181,7 @@
     mat4* transform;
 
     /**
-     * Current clip region. The clip is stored in canvas-space coordinates,
+     * Current clip rect. The clip is stored in canvas-space coordinates,
      * (screen-space coordinates in the regular case.)
      *
      * This is a reference to a rect owned by this snapshot or another
@@ -190,6 +190,17 @@
     Rect* clipRect;
 
     /**
+     * Current clip region. The clip is stored in canvas-space coordinates,
+     * (screen-space coordinates in the regular case.)
+     *
+     * This is a reference to a region owned by this snapshot or another
+     * snapshot. This pointer must not be freed. See ::mClipRegionRoot.
+     *
+     * This field is used only if STENCIL_BUFFER_SIZE is > 0.
+     */
+    Region* clipRegion;
+
+    /**
      * The ancestor layer's dirty region.
      *
      * This is a reference to a region owned by a layer. This pointer must
@@ -198,10 +209,22 @@
     Region* region;
 
 private:
+    void ensureClipRegion();
+    void copyClipRectFromRegion();
+
+    bool clipRegionOr(float left, float top, float right, float bottom);
+    bool clipRegionXor(float left, float top, float right, float bottom);
+    bool clipRegionAnd(float left, float top, float right, float bottom);
+    bool clipRegionNand(float left, float top, float right, float bottom);
+
     mat4 mTransformRoot;
     Rect mClipRectRoot;
     Rect mLocalClip;
 
+#if STENCIL_BUFFER_SIZE
+    Region mClipRegionRoot;
+#endif
+
 }; // class Snapshot
 
 }; // namespace uirenderer
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 8cd047a..6e2e731 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -126,6 +126,9 @@
 Region& Region::orSelf(const Rect& r) {
     return operationSelf(r, op_or);
 }
+Region& Region::xorSelf(const Rect& r) {
+    return operationSelf(r, op_xor);
+}
 Region& Region::andSelf(const Rect& r) {
     return operationSelf(r, op_and);
 }
@@ -143,6 +146,9 @@
 Region& Region::orSelf(const Region& rhs) {
     return operationSelf(rhs, op_or);
 }
+Region& Region::xorSelf(const Region& rhs) {
+    return operationSelf(rhs, op_xor);
+}
 Region& Region::andSelf(const Region& rhs) {
     return operationSelf(rhs, op_and);
 }
@@ -165,6 +171,9 @@
 const Region Region::merge(const Rect& rhs) const {
     return operation(rhs, op_or);
 }
+const Region Region::mergeExclusive(const Rect& rhs) const {
+    return operation(rhs, op_xor);
+}
 const Region Region::intersect(const Rect& rhs) const {
     return operation(rhs, op_and);
 }
@@ -182,6 +191,9 @@
 const Region Region::merge(const Region& rhs) const {
     return operation(rhs, op_or);
 }
+const Region Region::mergeExclusive(const Region& rhs) const {
+    return operation(rhs, op_xor);
+}
 const Region Region::intersect(const Region& rhs) const {
     return operation(rhs, op_and);
 }
@@ -205,6 +217,9 @@
 Region& Region::orSelf(const Region& rhs, int dx, int dy) {
     return operationSelf(rhs, dx, dy, op_or);
 }
+Region& Region::xorSelf(const Region& rhs, int dx, int dy) {
+    return operationSelf(rhs, dx, dy, op_xor);
+}
 Region& Region::andSelf(const Region& rhs, int dx, int dy) {
     return operationSelf(rhs, dx, dy, op_and);
 }
@@ -222,6 +237,9 @@
 const Region Region::merge(const Region& rhs, int dx, int dy) const {
     return operation(rhs, dx, dy, op_or);
 }
+const Region Region::mergeExclusive(const Region& rhs, int dx, int dy) const {
+    return operation(rhs, dx, dy, op_xor);
+}
 const Region Region::intersect(const Region& rhs, int dx, int dy) const {
     return operation(rhs, dx, dy, op_and);
 }
@@ -421,6 +439,7 @@
     SkRegion::Op sk_op;
     switch (op) {
         case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break;
+        case op_xor: sk_op = SkRegion::kUnion_XOR; name="XOR"; break;
         case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break;
         case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break;
     }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 643cb8d..3904c21 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -40,6 +40,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="ClipRegionActivity"
+                android:label="_ClipRegion">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="DisplayListLayersActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
new file mode 100644
index 0000000..b2a508b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ClipRegionActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final RegionView view = new RegionView(this);
+        setContentView(view);
+    }
+
+    public static class RegionView extends View {
+        public RegionView(Context c) {
+            super(c);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.save();
+            canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
+                    Region.Op.DIFFERENCE);
+            canvas.drawARGB(255, 255, 0, 0);
+            canvas.restore();
+        }
+    }
+}