Create one hole inside the umbra area to avoid overdraw.

bug:13439450

Change-Id: I859575196bd5a3029f447883025a6ec3a1f1face
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 8327ef7..c0b5402 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -31,6 +31,7 @@
  * Calculate the shadows as a triangle strips while alpha value as the
  * shadow values.
  *
+ * @param isCasterOpaque Whether the caster is opaque.
  * @param vertices The shadow caster's polygon, which is represented in a Vector3
  *                  array.
  * @param vertexCount The length of caster's polygon in terms of number of
@@ -43,17 +44,18 @@
  * @param shadowVertexBuffer Return an floating point array of (x, y, a)
  *               triangle strips mode.
  */
-void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount,
-        const Vector3& centroid3d, float heightFactor, float geomFactor,
-        VertexBuffer& shadowVertexBuffer) {
+VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
+        const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
+        float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
     const int rays = SHADOW_RAY_COUNT;
+    VertexBufferMode mode = kVertexBufferMode_OnePolyRingShadow;
     // Validate the inputs.
     if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
         || geomFactor <= 0) {
 #if DEBUG_SHADOW
-        ALOGE("Invalid input for createAmbientShadow(), early return!");
+        ALOGW("Invalid input for createAmbientShadow(), early return!");
 #endif
-        return;
+        return mode; // vertex buffer is empty, so any mode doesn't matter.
     }
 
     Vector<Vector2> dir; // TODO: use C++11 unique_ptr
@@ -75,7 +77,7 @@
         rayDist[i] = rayDistance;
         if (edgeIndex < 0 || edgeIndex >= vertexCount) {
 #if DEBUG_SHADOW
-            ALOGE("Invalid edgeIndex!");
+            ALOGW("Invalid edgeIndex!");
 #endif
             edgeIndex = 0;
         }
@@ -86,7 +88,8 @@
 
     // The output buffer length basically is roughly rays * layers, but since we
     // need triangle strips, so we need to duplicate vertices to accomplish that.
-    AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+    AlphaVertex* shadowVertices =
+            shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
 
     // Calculate the vertex of the shadows.
     //
@@ -95,6 +98,7 @@
     // calculate the normal N, which should be perpendicular to the edge of the
     // polygon (represented by the neighbor intersection points) .
     // Shadow's vertices will be generated as : P + N * scale.
+    const Vector2 centroid2d = Vector2(centroid3d.x, centroid3d.y);
     for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
         Vector2 normal(1.0f, 0.0f);
         calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
@@ -102,7 +106,7 @@
         // The vertex should be start from rayDist[i] then scale the
         // normalizeNormal!
         Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
-                Vector2(centroid3d.x, centroid3d.y);
+                centroid2d;
 
         // outer ring of points, expanded based upon height of each ray intersection
         float expansionDist = rayHeight[rayIndex] * heightFactor *
@@ -114,25 +118,31 @@
 
         // inner ring of points
         float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
-        AlphaVertex::set(&shadowVertices[rayIndex + rays],
+        AlphaVertex::set(&shadowVertices[rays + rayIndex],
                 intersection.x,
                 intersection.y,
                 opacity);
     }
-    float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
-    AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1],
-            centroid3d.x, centroid3d.y, centroidAlpha);
+
+    // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
+    // centroid in the innermost ring of vertices.
+    if (!isCasterOpaque) {
+        mode = kVertexBufferMode_TwoPolyRingShadow;
+        float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
+        AlphaVertex centroidXYA;
+        AlphaVertex::set(&centroidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
+        for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+            shadowVertices[2 * rays + rayIndex] = centroidXYA;
+        }
+    }
 
 #if DEBUG_SHADOW
-    if (currentVertexIndex != SHADOW_VERTEX_COUNT) {
-        ALOGE("number of vertex generated for ambient shadow is wrong! "
-              "current: %d , expected: %d", currentVertexIndex, SHADOW_VERTEX_COUNT);
-    }
     for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
         ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
                 shadowVertices[i].y, shadowVertices[i].alpha);
     }
 #endif
+    return mode;
 }
 
 /**
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 20d1384..45b8bef 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -19,6 +19,7 @@
 #define ANDROID_HWUI_AMBIENT_SHADOW_H
 
 #include "Debug.h"
+#include "OpenGLRenderer.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
 
@@ -34,9 +35,9 @@
  */
 class AmbientShadow {
 public:
-    static void createAmbientShadow(const Vector3* poly, int polyLength,
-            const Vector3& centroid3d, float heightFactor, float geomFactor,
-            VertexBuffer& shadowVertexBuffer);
+    static VertexBufferMode createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
+            int polyLength, const Vector3& centroid3d, float heightFactor,
+            float geomFactor, VertexBuffer& shadowVertexBuffer);
 
 private:
     static void calculateRayDirections(int rays, Vector2* dir);
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 2dfc873..477d691 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -445,11 +445,11 @@
 
 bool Caches::bindShadowIndicesBuffer() {
     if (!mShadowStripsIndices) {
-        uint16_t* shadowIndices = new uint16_t[SHADOW_INDEX_COUNT];
+        uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT];
         ShadowTessellator::generateShadowIndices(shadowIndices);
         glGenBuffers(1, &mShadowStripsIndices);
         bool force = bindIndicesBufferInternal(mShadowStripsIndices);
-        glBufferData(GL_ELEMENT_ARRAY_BUFFER, SHADOW_INDEX_COUNT * sizeof(uint16_t),
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
             shadowIndices, GL_STATIC_DRAW);
 
         delete[] shadowIndices;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1475953..cb8155b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2414,9 +2414,12 @@
     if (mode == kVertexBufferMode_Standard) {
         mCaches.unbindIndicesBuffer();
         glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
-    } else {
+    } else if (mode == kVertexBufferMode_OnePolyRingShadow) {
         mCaches.bindShadowIndicesBuffer();
-        glDrawElements(GL_TRIANGLE_STRIP, SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+        glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+    } else if (mode == kVertexBufferMode_TwoPolyRingShadow) {
+        mCaches.bindShadowIndicesBuffer();
+        glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
     }
 
     if (isAA) {
@@ -3245,14 +3248,15 @@
         }
         centroid3d.z += casterLift;
     }
-
+    bool isCasterOpaque = (casterAlpha == 1.0f);
     // draw caster's shadows
     if (mCaches.propertyAmbientShadowStrength > 0) {
         paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
         VertexBuffer ambientShadowVertexBuffer;
-        ShadowTessellator::tessellateAmbientShadow(casterPolygon, casterVertexCount,
-                centroid3d, ambientShadowVertexBuffer);
-        drawVertexBuffer(kVertexBufferMode_Shadow, ambientShadowVertexBuffer, &paint);
+        VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
+                isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
+                ambientShadowVertexBuffer);
+        drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
     }
 
     if (mCaches.propertySpotShadowStrength > 0) {
@@ -3260,10 +3264,10 @@
         VertexBuffer spotShadowVertexBuffer;
         Vector3 lightPosScale(mCaches.propertyLightPosXScale,
                 mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
-        ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount,
-                lightPosScale, *currentTransform(), getWidth(), getHeight(),
-                spotShadowVertexBuffer);
-        drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint);
+        VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
+                isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale,
+                *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer);
+        drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
     }
 
     return DrawGlInfo::kStatusDrew;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 94abfa7..059c64f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -119,7 +119,8 @@
 
 enum VertexBufferMode {
     kVertexBufferMode_Standard = 0,
-    kVertexBufferMode_Shadow = 1
+    kVertexBufferMode_OnePolyRingShadow = 1,
+    kVertexBufferMode_TwoPolyRingShadow = 2
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index f138222..771904a 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -33,9 +33,9 @@
     return a > b ? a : b;
 }
 
-void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon,
-        int casterVertexCount, const Vector3& centroid3d,
-        VertexBuffer& shadowVertexBuffer) {
+VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
+        const Vector3* casterPolygon, int casterVertexCount,
+        const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
     // A bunch of parameters to tweak the shadow.
@@ -43,12 +43,14 @@
     const float heightFactor = 1.0f / 128;
     const float geomFactor = 64;
 
-    AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount,
-            centroid3d, heightFactor, geomFactor, shadowVertexBuffer);
+    return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
+            casterVertexCount, centroid3d, heightFactor, geomFactor,
+            shadowVertexBuffer);
 
 }
 
-void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
+VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
+        const Vector3* casterPolygon, int casterVertexCount,
         const Vector3& lightPosScale, const mat4& receiverTransform,
         int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
@@ -71,37 +73,40 @@
     const float lightSize = maximal / 4;
     const int lightVertexCount = 8;
 
-    SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter,
-            lightSize, lightVertexCount, shadowVertexBuffer);
+    VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
+            casterPolygon, casterVertexCount, lightCenter, lightSize,
+            lightVertexCount, shadowVertexBuffer);
 
+#if DEBUG_SHADOW
+     if(shadowVertexBuffer.getVertexCount() <= 0) {
+        ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
+     }
+#endif
+     return mode;
 }
 
 void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
     int currentIndex = 0;
     const int rays = SHADOW_RAY_COUNT;
     // For the penumbra area.
-    for (int i = 0; i < rays; i++) {
-        shadowIndices[currentIndex++] = i;
-        shadowIndices[currentIndex++] = rays + i;
+    for (int layer = 0; layer < 2; layer ++) {
+        int baseIndex = layer * rays;
+        for (int i = 0; i < rays; i++) {
+            shadowIndices[currentIndex++] = i + baseIndex;
+            shadowIndices[currentIndex++] = rays + i + baseIndex;
+        }
+        // To close the loop, back to the ray 0.
+        shadowIndices[currentIndex++] = 0 + baseIndex;
+         // Note this is the same as the first index of next layer loop.
+        shadowIndices[currentIndex++] = rays + baseIndex;
     }
-    // To close the loop, back to the ray 0.
-    shadowIndices[currentIndex++] = 0;
-    shadowIndices[currentIndex++] = rays;
-
-    uint16_t centroidIndex = 2 * rays;
-    // For the umbra area, using strips to simulate the fans.
-    for (int i = 0; i < rays; i++) {
-        shadowIndices[currentIndex++] = rays + i;
-        shadowIndices[currentIndex++] = centroidIndex;
-    }
-    shadowIndices[currentIndex++] = rays;
 
 #if DEBUG_SHADOW
-    if (currentIndex != SHADOW_INDEX_COUNT) {
-        ALOGE("vertex index count is wrong. current %d, expected %d",
-                currentIndex, SHADOW_INDEX_COUNT);
+    if (currentIndex != MAX_SHADOW_INDEX_COUNT) {
+        ALOGW("vertex index count is wrong. current %d, expected %d",
+                currentIndex, MAX_SHADOW_INDEX_COUNT);
     }
-    for (int i = 0; i < SHADOW_INDEX_COUNT; i++) {
+    for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) {
         ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]);
     }
 #endif
@@ -135,7 +140,7 @@
     if (area != 0) {
         centroid = Vector2(sumx / (3 * area), sumy / (3 * area));
     } else {
-        ALOGE("Area is 0 while computing centroid!");
+        ALOGW("Area is 0 while computing centroid!");
     }
     return centroid;
 }
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index c558460..ab039fa 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -20,6 +20,7 @@
 
 #include "Debug.h"
 #include "Matrix.h"
+#include "OpenGLRenderer.h"
 #include "VertexBuffer.h"
 
 namespace android {
@@ -30,15 +31,14 @@
 // Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which
 // are 0 to 5 and 6 to 11. The area between them will be the penumbra area, and
 // the area inside the 2nd hexagon is the umbra.
-// Also, we need to add the centroid "12" to draw the umbra area as triangle fans.
-//
+// Ambient shadow is using only 1 layer for opaque caster, otherwise, spot
+// shadow and ambient shadow are using 2 layers.
 // Triange strip indices for penumbra area: (0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11, 0, 6)
-// Triange strip indices for numbra area: (6, 12, 7, 12, 8, 12, 9, 12, 10, 12, 11, 12, 6)
 //                 0
 //
 //      5          6         1
 //           11         7
-//                12
+//
 //           10         8
 //      4          9         2
 //
@@ -49,20 +49,27 @@
 #define SHADOW_RAY_COUNT 128
 
 // The total number of all the vertices representing the shadow.
-#define SHADOW_VERTEX_COUNT (2 * SHADOW_RAY_COUNT + 1)
+// For the case we only have 1 layer, then we will just fill only 2/3 of it.
+#define SHADOW_VERTEX_COUNT (3 * SHADOW_RAY_COUNT)
 
 // The total number of indices used for drawing the shadow geometry as triangle strips.
-#define SHADOW_INDEX_COUNT (2 * SHADOW_RAY_COUNT + 1 + 2 * (SHADOW_RAY_COUNT + 1))
+// Depending on the mode we are drawing, we can have 1 layer or 2 layers.
+// Therefore, we only build the longer index buffer.
+#define TWO_POLY_RING_SHADOW_INDEX_COUNT (4 * (SHADOW_RAY_COUNT + 1))
+#define ONE_POLY_RING_SHADOW_INDEX_COUNT (2 * (SHADOW_RAY_COUNT + 1))
+
+#define MAX_SHADOW_INDEX_COUNT TWO_POLY_RING_SHADOW_INDEX_COUNT
 
 #define SHADOW_MIN_CASTER_Z 0.001f
 
 class ShadowTessellator {
 public:
-    static void tessellateAmbientShadow(const Vector3* casterPolygon,
-            int casterVertexCount, const Vector3& centroid3d,
-            VertexBuffer& shadowVertexBuffer);
+    static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
+            const Vector3* casterPolygon, int casterVertexCount,
+            const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer);
 
-    static void tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
+    static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
+            const Vector3* casterPolygon, int casterVertexCount,
             const Vector3& lightPosScale, const mat4& receiverTransform,
             int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer);
 
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 8538b29..1b49083 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -161,7 +161,7 @@
 
 /**
  * Calculates the intersection of poly1 with poly2 and put in poly2.
- *
+ * Note that both poly1 and poly2 must be in CW order already!
  *
  * @param poly1 The 1st polygon, as a Vector2 array.
  * @param poly1Length The number of vertices of 1st polygon.
@@ -169,11 +169,16 @@
  * @param poly2Length The number of vertices of 2nd polygon.
  * @return number of vertices in output polygon as poly2.
  */
-int SpotShadow::intersection(Vector2* poly1, int poly1Length,
+int SpotShadow::intersection(const Vector2* poly1, int poly1Length,
         Vector2* poly2, int poly2Length) {
-    makeClockwise(poly1, poly1Length);
-    makeClockwise(poly2, poly2Length);
-
+#if DEBUG_SHADOW
+    if (!isClockwise(poly1, poly1Length)) {
+        ALOGW("Poly1 is not clockwise! Intersection is wrong!");
+    }
+    if (!isClockwise(poly2, poly2Length)) {
+        ALOGW("Poly2 is not clockwise! Intersection is wrong!");
+    }
+#endif
     Vector2 poly[poly1Length * poly2Length + 2];
     int count = 0;
     int pcount = 0;
@@ -411,7 +416,7 @@
  * @param polygon the polygon as a Vector2 array
  * @param len the number of points of the polygon
  */
-bool SpotShadow::isClockwise(Vector2* polygon, int len) {
+bool SpotShadow::isClockwise(const Vector2* polygon, int len) {
     double sum = 0;
     double p1x = polygon[len - 1].x;
     double p1y = polygon[len - 1].y;
@@ -514,13 +519,14 @@
 *                            empty strip if error.
 *
 */
-void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength,
-        const Vector3& lightCenter, float lightSize, int lightVertexCount,
-        VertexBuffer& retStrips) {
+VertexBufferMode SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+        int polyLength, const Vector3& lightCenter, float lightSize,
+        int lightVertexCount, VertexBuffer& retStrips) {
     Vector3 light[lightVertexCount * 3];
     computeLightPolygon(lightVertexCount, lightCenter, lightSize, light);
-    computeSpotShadow(light, lightVertexCount, lightCenter, poly, polyLength,
-            retStrips);
+    computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly,
+            polyLength, retStrips);
+    return kVertexBufferMode_TwoPolyRingShadow;
 }
 
 /**
@@ -533,9 +539,9 @@
  * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
  *                            empty strip if error.
  */
-void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength,
-        const Vector3& lightCenter, const Vector3* poly, int polyLength,
-        VertexBuffer& shadowTriangleStrip) {
+void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly,
+        int lightPolyLength, const Vector3& lightCenter, const Vector3* poly,
+        int polyLength, VertexBuffer& shadowTriangleStrip) {
     // Point clouds for all the shadowed vertices
     Vector2 shadowRegion[lightPolyLength * polyLength];
     // Shadow polygon from one point light.
@@ -565,13 +571,13 @@
     for (int j = 0; j < lightPolyLength; j++) {
         int m = 0;
         for (int i = 0; i < polyLength; i++) {
-            float t = lightPoly[j].z - poly[i].z;
-            if (t == 0) {
+            float deltaZ = lightPoly[j].z - poly[i].z;
+            if (deltaZ == 0) {
                 return;
             }
-            t = lightPoly[j].z / t;
-            float x = lightPoly[j].x - t * (lightPoly[j].x - poly[i].x);
-            float y = lightPoly[j].y - t * (lightPoly[j].y - poly[i].y);
+            float ratioZ = lightPoly[j].z / deltaZ;
+            float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
+            float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
 
             Vector2 newPoint = Vector2(x, y);
             shadowRegion[k] = newPoint;
@@ -606,13 +612,13 @@
     if (umbraLength < 3) {
         // If there is no real umbra, make a fake one.
         for (int i = 0; i < polyLength; i++) {
-            float t = lightCenter.z - poly[i].z;
-            if (t == 0) {
+            float deltaZ = lightCenter.z - poly[i].z;
+            if (deltaZ == 0) {
                 return;
             }
-            t = lightCenter.z / t;
-            float x = lightCenter.x - t * (lightCenter.x - poly[i].x);
-            float y = lightCenter.y - t * (lightCenter.y - poly[i].y);
+            float ratioZ = lightCenter.z / deltaZ;
+            float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x);
+            float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y);
 
             fakeUmbra[i].x = x;
             fakeUmbra[i].y = y;
@@ -635,8 +641,8 @@
         umbraLength = polyLength;
     }
 
-    generateTriangleStrip(penumbra, penumbraLength, umbra, umbraLength,
-            shadowTriangleStrip);
+    generateTriangleStrip(isCasterOpaque, penumbra, penumbraLength, umbra,
+            umbraLength, poly, polyLength, shadowTriangleStrip);
 }
 
 /**
@@ -684,7 +690,12 @@
                     cos(rayIndex * step),
                     sin(rayIndex * step),
                     *lastVertex, poly[polyIndex]);
-            if (distanceToIntersect < 0) return false; // error case, abort
+            if (distanceToIntersect < 0) {
+#if DEBUG_SHADOW
+                ALOGW("ERROR: convertPolyToRayDist failed");
+#endif
+                return false; // error case, abort
+            }
 
             rayDist[rayIndex] = distanceToIntersect;
 
@@ -696,6 +707,22 @@
    return true;
 }
 
+int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
+        const Vector3* poly, int polyLength, Vector2* occludedUmbra) {
+    // Occluded umbra area is computed as the intersection of the projected 2D
+    // poly and umbra.
+    for (int i = 0; i < polyLength; i++) {
+        occludedUmbra[i].x = poly[i].x;
+        occludedUmbra[i].y = poly[i].y;
+    }
+
+    // Both umbra and incoming polygon are guaranteed to be CW, so we can call
+    // intersection() directly.
+    return intersection(umbra, umbraLength,
+            occludedUmbra, polyLength);
+}
+
+#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f
 /**
  * Generate a triangle strip given two convex polygons
  *
@@ -706,10 +733,10 @@
  * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
  *                            empty strip if error.
 **/
-void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLength,
-        const Vector2* umbra, int umbraLength, VertexBuffer& shadowTriangleStrip) {
+void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra,
+        int penumbraLength, const Vector2* umbra, int umbraLength,
+        const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip) {
     const int rays = SHADOW_RAY_COUNT;
-
     const int size = 2 * rays;
     const float step = M_PI * 2 / rays;
     // Centroid of the umbra.
@@ -721,37 +748,66 @@
     float penumbraDistPerRay[rays];
     // Intersection to the umbra.
     float umbraDistPerRay[rays];
+    // Intersection to the occluded umbra area.
+    float occludedUmbraDistPerRay[rays];
 
     // convert CW polygons to ray distance encoding, aborting on conversion failure
     if (!convertPolyToRayDist(umbra, umbraLength, centroid, umbraDistPerRay)) return;
     if (!convertPolyToRayDist(penumbra, penumbraLength, centroid, penumbraDistPerRay)) return;
 
-    AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(getStripSize(rays));
+    bool hasOccludedUmbraArea = false;
+    if (isCasterOpaque) {
+        Vector2 occludedUmbra[polyLength + umbraLength];
+        int occludedUmbraLength = calculateOccludedUmbra(umbra, umbraLength, poly, polyLength,
+                occludedUmbra);
+        // Make sure the centroid is inside the umbra, otherwise, fall back to the
+        // approach as if there is no occluded umbra area.
+        if (testPointInsidePolygon(centroid, occludedUmbra, occludedUmbraLength)) {
+            hasOccludedUmbraArea = true;
+            // Shrink the occluded umbra area to avoid pixel level artifacts.
+            for (int i = 0; i < occludedUmbraLength; i ++) {
+                occludedUmbra[i] = centroid + (occludedUmbra[i] - centroid) *
+                        OCLLUDED_UMBRA_SHRINK_FACTOR;
+            }
+            if (!convertPolyToRayDist(occludedUmbra, occludedUmbraLength, centroid,
+                    occludedUmbraDistPerRay)) {
+                return;
+            }
+        }
+    }
+
+    AlphaVertex* shadowVertices =
+            shadowTriangleStrip.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
 
     // Calculate the vertices (x, y, alpha) in the shadow area.
+    AlphaVertex centroidXYA;
+    AlphaVertex::set(&centroidXYA, centroid.x, centroid.y, 1.0f);
     for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
         float dx = cosf(step * rayIndex);
         float dy = sinf(step * rayIndex);
 
-        // outer ring
-        float currentDist = penumbraDistPerRay[rayIndex];
+        // penumbra ring
+        float penumbraDistance = penumbraDistPerRay[rayIndex];
         AlphaVertex::set(&shadowVertices[rayIndex],
-                dx * currentDist + centroid.x, dy * currentDist + centroid.y, 0.0f);
+                dx * penumbraDistance + centroid.x,
+                dy * penumbraDistance + centroid.y, 0.0f);
 
-        // inner ring
-        float deltaDist = umbraDistPerRay[rayIndex] - penumbraDistPerRay[rayIndex];
-        currentDist += deltaDist;
+        // umbra ring
+        float umbraDistance = umbraDistPerRay[rayIndex];
         AlphaVertex::set(&shadowVertices[rays + rayIndex],
-                dx * currentDist + centroid.x, dy * currentDist + centroid.y, 1.0f);
+                dx * umbraDistance + centroid.x, dy * umbraDistance + centroid.y, 1.0f);
+
+        // occluded umbra ring
+        if (hasOccludedUmbraArea) {
+            float occludedUmbraDistance = occludedUmbraDistPerRay[rayIndex];
+            AlphaVertex::set(&shadowVertices[2 * rays + rayIndex],
+                    dx * occludedUmbraDistance + centroid.x,
+                    dy * occludedUmbraDistance + centroid.y, 1.0f);
+        } else {
+            // Put all vertices of the occluded umbra ring at the centroid.
+            shadowVertices[2 * rays + rayIndex] = centroidXYA;
+        }
     }
-    // The centroid is in the umbra area, so the opacity is considered as 1.0.
-    AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1], centroid.x, centroid.y, 1.0f);
-#if DEBUG_SHADOW
-    for (int i = 0; i < currentIndex; i++) {
-        ALOGD("spot shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
-                shadowVertices[i].y, shadowVertices[i].alpha);
-    }
-#endif
 }
 
 /**
@@ -775,17 +831,6 @@
     }
 }
 
-/**
- * Calculate the number of vertex we will create given a number of rays and layers
- *
- * @param rays number of points around the polygons you want
- * @param layers number of layers of triangle strips you need
- * @return number of vertex (multiply by 3 for number of floats)
- */
-int SpotShadow::getStripSize(int rays) {
-    return  (2 + rays + (2 * (rays + 1)));
-}
-
 #if DEBUG_SHADOW
 
 #define TEST_POINT_NUMBER 128
@@ -837,7 +882,7 @@
         bool isCCWOrCoLinear = (delta >= EPSILON);
 
         if (isCCWOrCoLinear) {
-            ALOGE("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f),"
+            ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f),"
                     "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!",
                     name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta);
             isConvex = false;
@@ -879,14 +924,14 @@
         if (testPointInsidePolygon(testPoint, intersection, intersectionLength)) {
             if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) {
                 dumpPoly = true;
-                ALOGE("(Error Type 1): one point (%f, %f) in the intersection is"
+                ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
                       " not in the poly1",
                         testPoint.x, testPoint.y);
             }
 
             if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) {
                 dumpPoly = true;
-                ALOGE("(Error Type 1): one point (%f, %f) in the intersection is"
+                ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
                       " not in the poly2",
                         testPoint.x, testPoint.y);
             }
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 7839dc3..599d37e 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -26,19 +26,20 @@
 
 class SpotShadow {
 public:
-    static void createSpotShadow(const Vector3* poly, int polyLength,
-            const Vector3& lightCenter, float lightSize, int lightVertexCount,
-            VertexBuffer& retStrips);
+    static VertexBufferMode createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+            int polyLength, const Vector3& lightCenter, float lightSize,
+            int lightVertexCount, VertexBuffer& retStrips);
 
 private:
-    static void computeSpotShadow(const Vector3* lightPoly, int lightPolyLength,
-            const Vector3& lightCenter, const Vector3* poly, int polyLength,
-            VertexBuffer& retstrips);
+    static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
+            const Vector3* poly, int polyLength, Vector2* occludedUmbra);
+    static void computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly,
+            int lightPolyLength, const Vector3& lightCenter, const Vector3* poly,
+            int polyLength, VertexBuffer& retstrips);
 
     static void computeLightPolygon(int points, const Vector3& lightCenter,
             float size, Vector3* ret);
 
-    static int  getStripSize(int rays);
     static void smoothPolygon(int level, int rays, float* rayDist);
     static float rayIntersectPoly(const Vector2* poly, int polyLength,
             const Vector2& point, float dx, float dy);
@@ -46,7 +47,7 @@
     static void xsort(Vector2* points, int pointsLength);
     static int hull(Vector2* points, int pointsLength, Vector2* retPoly);
     static bool ccw(double ax, double ay, double bx, double by, double cx, double cy);
-    static int intersection(Vector2* poly1, int poly1length, Vector2* poly2, int poly2length);
+    static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length);
     static void sort(Vector2* poly, int polyLength, const Vector2& center);
 
     static void swap(Vector2* points, int i, int j);
@@ -55,13 +56,14 @@
 
     static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
     static void makeClockwise(Vector2* polygon, int len);
-    static bool isClockwise(Vector2* polygon, int len);
+    static bool isClockwise(const Vector2* polygon, int len);
     static void reverse(Vector2* polygon, int len);
     static inline bool lineIntersection(double x1, double y1, double x2, double y2,
             double x3, double y3, double x4, double y4, Vector2& ret);
 
-    static void generateTriangleStrip(const Vector2* penumbra, int penumbraLength,
-            const Vector2* umbra, int umbraLength, VertexBuffer& retstrips);
+    static void generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra,
+            int penumbraLength, const Vector2* umbra, int umbraLength,
+            const Vector3* poly, int polyLength, VertexBuffer& retstrips);
 
 #if DEBUG_SHADOW
     // Verification utility function.