Preliminary Support for region clipping

Region clipping, using Canvas.clipPath or Canvas.clipRegion, requires
a stencil buffer to be always present. In addition, extra wiring is
required in JNI and display lists.

This change only adds the necessary JNI/C++ APIs and some extra
plumbing to start the real work on properly supporting region
clipping.

A new debug define called DEBUG_CLIP_REGIONS can be used to draw
the current clip region. It is off by default, as is region
clipping.

The default implementation of clipPath() and clipRegion(), now
in native, mimics the previous Dalvik implementation to prevent
regressions.

Change-Id: I7903e7cfd7412b9b9b622566d4dbfce7bdcec00c
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 6795ac3..dfc4e25 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -35,6 +35,9 @@
 // Turn on to enable layers debugging when rendered as regions
 #define DEBUG_LAYERS_AS_REGIONS 0
 
+// Turn on to enable debugging when the clip is not a rect
+#define DEBUG_CLIP_REGIONS 0
+
 // Turn on to display debug info about vertex/fragment shaders
 #define DEBUG_PROGRAMS 0
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 06574cd..f0c9ce4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -44,6 +44,8 @@
     "SetMatrix",
     "ConcatMatrix",
     "ClipRect",
+    "ClipPath",
+    "ClipRegion",
     "DrawDisplayList",
     "DrawLayer",
     "DrawBitmap",
@@ -166,6 +168,10 @@
         delete mPaints.itemAt(i);
     }
 
+    for (size_t i = 0; i < mRegions.size(); i++) {
+        delete mRegions.itemAt(i);
+    }
+
     for (size_t i = 0; i < mPaths.size(); i++) {
         SkPath* path = mPaths.itemAt(i);
         caches.pathCache.remove(path);
@@ -182,6 +188,7 @@
     mShaders.clear();
     mSourcePaths.clear();
     mPaints.clear();
+    mRegions.clear();
     mPaths.clear();
     mMatrices.clear();
     mLayers.clear();
@@ -259,20 +266,10 @@
 
     caches.resourceCache.unlock();
 
-    const Vector<SkPaint*>& paints = recorder.getPaints();
-    for (size_t i = 0; i < paints.size(); i++) {
-        mPaints.add(paints.itemAt(i));
-    }
-
-    const Vector<SkPath*>& paths = recorder.getPaths();
-    for (size_t i = 0; i < paths.size(); i++) {
-        mPaths.add(paths.itemAt(i));
-    }
-
-    const Vector<SkMatrix*>& matrices = recorder.getMatrices();
-    for (size_t i = 0; i < matrices.size(); i++) {
-        mMatrices.add(matrices.itemAt(i));
-    }
+    mPaints.appendVector(recorder.getPaints());
+    mRegions.appendVector(recorder.getRegions());
+    mPaths.appendVector(recorder.getPaths());
+    mMatrices.appendVector(recorder.getMatrices());
 }
 
 void DisplayList::init() {
@@ -429,6 +426,18 @@
                         f1, f2, f3, f4, regionOp);
             }
             break;
+            case ClipPath: {
+                SkPath* path = getPath();
+                int regionOp = getInt();
+                ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+            }
+            break;
+            case ClipRegion: {
+                SkRegion* region = getRegion();
+                int regionOp = getInt();
+                ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+            }
+            break;
             case DrawDisplayList: {
                 DisplayList* displayList = getDisplayList();
                 int32_t flags = getInt();
@@ -1031,6 +1040,20 @@
                 renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
             }
             break;
+            case ClipPath: {
+                SkPath* path = getPath();
+                int32_t regionOp = getInt();
+                DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+                renderer.clipPath(path, (SkRegion::Op) regionOp);
+            }
+            break;
+            case ClipRegion: {
+                SkRegion* region = getRegion();
+                int32_t regionOp = getInt();
+                DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+                renderer.clipRegion(region, (SkRegion::Op) regionOp);
+            }
+            break;
             case DrawDisplayList: {
                 DisplayList* displayList = getDisplayList();
                 int32_t flags = getInt();
@@ -1415,6 +1438,9 @@
     mPaints.clear();
     mPaintMap.clear();
 
+    mRegions.clear();
+    mRegionMap.clear();
+
     mPaths.clear();
     mPathMap.clear();
 
@@ -1571,6 +1597,20 @@
     return OpenGLRenderer::clipRect(left, top, right, bottom, op);
 }
 
+bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+    addOp(DisplayList::ClipPath);
+    addPath(path);
+    addInt(op);
+    return OpenGLRenderer::clipPath(path, op);
+}
+
+bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+    addOp(DisplayList::ClipRegion);
+    addRegion(region);
+    addInt(op);
+    return OpenGLRenderer::clipRegion(region, op);
+}
+
 status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
         Rect& dirty, int32_t flags, uint32_t level) {
     // dirty is an out parameter and should not be recorded,
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index fb01753..f55f1f2 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -85,6 +85,8 @@
         SetMatrix,
         ConcatMatrix,
         ClipRect,
+        ClipPath,
+        ClipRegion,
         // Drawing operations
         DrawDisplayList,
         DrawLayer,
@@ -457,6 +459,10 @@
         return (SkPath*) getInt();
     }
 
+    SkRegion* getRegion() {
+        return (SkRegion*) getInt();
+    }
+
     SkPaint* getPaint(OpenGLRenderer& renderer) {
         return renderer.filterPaint((SkPaint*) getInt());
     }
@@ -496,6 +502,7 @@
     Vector<SkPaint*> mPaints;
     Vector<SkPath*> mPaths;
     SortedVector<SkPath*> mSourcePaths;
+    Vector<SkRegion*> mRegions;
     Vector<SkMatrix*> mMatrices;
     Vector<SkiaShader*> mShaders;
     Vector<Layer*> mLayers;
@@ -577,6 +584,8 @@
     virtual void concatMatrix(SkMatrix* matrix);
 
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
 
     virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
             uint32_t level = 0);
@@ -657,6 +666,10 @@
         return mSourcePaths;
     }
 
+    const Vector<SkRegion*>& getRegions() const {
+        return mRegions;
+    }
+
     const Vector<Layer*>& getLayers() const {
         return mLayers;
     }
@@ -802,6 +815,26 @@
         return paintCopy;
     }
 
+    inline SkRegion* addRegion(SkRegion* region) {
+        if (!region) {
+            addInt((int) NULL);
+            return region;
+        }
+
+        SkRegion* regionCopy = mRegionMap.valueFor(region);
+        // TODO: Add generation ID to SkRegion
+        if (regionCopy == NULL) {
+            regionCopy = new SkRegion(*region);
+            // replaceValueFor() performs an add if the entry doesn't exist
+            mRegionMap.replaceValueFor(region, regionCopy);
+            mRegions.add(regionCopy);
+        }
+
+        addInt((int) regionCopy);
+
+        return regionCopy;
+    }
+
     inline void addDisplayList(DisplayList* displayList) {
         // TODO: To be safe, the display list should be ref-counted in the
         //       resources cache, but we rely on the caller (UI toolkit) to
@@ -876,6 +909,9 @@
 
     SortedVector<SkPath*> mSourcePaths;
 
+    Vector<SkRegion*> mRegions;
+    DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap;
+
     Vector<SkiaShader*> mShaders;
     DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8cda729..bb1edbb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1288,10 +1288,38 @@
     bool clipped = mSnapshot->clip(left, top, right, bottom, op);
     if (clipped) {
         dirtyClip();
+#if DEBUG_CLIP_REGIONS
+        if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) {
+            int count = 0;
+            Vector<float> rects;
+            SkRegion::Iterator it(*mSnapshot->clipRegion);
+            while (!it.done()) {
+                const SkIRect& r = it.rect();
+                rects.push(r.fLeft);
+                rects.push(r.fTop);
+                rects.push(r.fRight);
+                rects.push(r.fBottom);
+                count++;
+                it.next();
+            }
+
+            drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true);
+        }
+#endif
     }
     return !mSnapshot->clipRect->isEmpty();
 }
 
+bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+    const SkRect& bounds = path->getBounds();
+    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
+bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+    const SkIRect& bounds = region->getBounds();
+    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
 Rect* OpenGLRenderer::getClipRect() {
     return mSnapshot->clipRect;
 }
@@ -3046,6 +3074,19 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    int color = paint->getColor();
+    // If a shader is set, preserve only the alpha
+    if (mShader) {
+        color |= 0x00ffffff;
+    }
+    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+
+    return drawColorRects(rects, count, color, mode);
+}
+
+status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
+        SkXfermode::Mode mode, bool ignoreTransform) {
+
     float left = FLT_MAX;
     float top = FLT_MAX;
     float right = FLT_MIN;
@@ -3081,13 +3122,6 @@
 
     if (count == 0) return DrawGlInfo::kStatusDone;
 
-    int color = paint->getColor();
-    // If a shader is set, preserve only the alpha
-    if (mShader) {
-        color |= 0x00ffffff;
-    }
-    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
-
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
@@ -3096,7 +3130,7 @@
     setupDrawBlending(mode);
     setupDrawProgram();
     setupDrawDirtyRegionsDisabled();
-    setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f);
+    setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true);
     setupDrawColorUniforms();
     setupDrawShaderUniforms();
     setupDrawColorFilterUniforms();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5520edb..f07325f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -164,6 +164,8 @@
     ANDROID_API bool quickReject(float left, float top, float right, float bottom);
     bool quickRejectNoScissor(float left, float top, float right, float bottom);
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
     virtual Rect* getClipRect();
 
     virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
@@ -498,7 +500,8 @@
 
     /**
      * Draws a colored rectangle with the specified color. The specified coordinates
-     * are transformed by the current snapshot's transform matrix.
+     * are transformed by the current snapshot's transform matrix unless specified
+     * otherwise.
      *
      * @param left The left coordinate of the rectangle
      * @param top The top coordinate of the rectangle
@@ -512,6 +515,20 @@
             int color, SkXfermode::Mode mode, bool ignoreTransform = false);
 
     /**
+     * Draws a series of colored rectangles with the specified color. The specified
+     * coordinates are transformed by the current snapshot's transform matrix unless
+     * specified otherwise.
+     *
+     * @param rects A list of rectangles, 4 floats (left, top, right, bottom)
+     *              per rectangle
+     * @param color The rectangles' ARGB color, defined as a packed 32 bits word
+     * @param mode The Skia xfermode to use
+     * @param ignoreTransform True if the current transform should be ignored
+     */
+    status_t drawColorRects(const float* rects, int count, int color,
+            SkXfermode::Mode mode, bool ignoreTransform = false);
+
+    /**
      * Draws the shape represented by the specified path texture.
      * This method invokes drawPathTexture() but takes into account
      * the extra left/top offset and the texture offset to correctly
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index fbc8455..d947299 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -130,6 +130,7 @@
     switch (op) {
         case SkRegion::kIntersect_Op: {
             if (CC_UNLIKELY(clipRegion)) {
+                ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
             } else {
                 clipped = clipRect->intersect(r);
@@ -142,6 +143,7 @@
         }
         case SkRegion::kUnion_Op: {
             if (CC_UNLIKELY(clipRegion)) {
+                ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
             } else {
                 clipped = clipRect->unionWith(r);