Use path outlines to define shadow shapes

Fixes the simplifying assumption that shadow casters were always
rectangular.

Java side APIs + plumbing to pass down correct shapes still need to be added.

Change-Id: Ic4fee90af15679951a44bb5cc6ae454b98c4c194
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e3d4e2d..5024880 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1589,7 +1589,9 @@
             mWidth(width), mHeight(height) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
-        return renderer.drawShadow(mCasterTransform, mCasterAlpha, mWidth, mHeight);
+        SkPath casterOutline; // TODO: drive with path from view
+        casterOutline.addRect(0, 0, mWidth, mHeight);
+        return renderer.drawShadow(mCasterTransform, mCasterAlpha, &casterOutline);
     }
 
     virtual void output(int level, uint32_t logFlags) const {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 741e953..41f34e5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3185,28 +3185,43 @@
 }
 
 status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha,
-        float width, float height) {
+        const SkPath* casterOutline) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
-    // For now, always and scissor
-    // TODO: use quickReject
+    // TODO: use quickRejectWithScissor. For now, always force enable scissor.
     mCaches.enableScissor();
 
     SkPaint paint;
-    paint.setColor(mCaches.propertyShadowStrength << 24);
+    paint.setARGB(mCaches.propertyShadowStrength, 0, 0, 0);
     paint.setAntiAlias(true); // want to use AlphaVertex
 
+    // tessellate caster outline into a 2d polygon
+    Vector<Vertex> casterVertices2d;
+    const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
+    PathTessellator::approximatePathOutlineVertices(*casterOutline,
+            casterRefinementThresholdSquared, casterVertices2d);
+
+    // map 2d caster poly into 3d
+    const int casterVertexCount = casterVertices2d.size();
+    Vector3 casterPolygon[casterVertexCount];
+    for (int i = 0; i < casterVertexCount; i++) {
+        const Vertex& point2d = casterVertices2d[i];
+        casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
+        casterTransform.mapPoint3d(casterPolygon[i]);
+    }
+
+    // draw caster's shadows
     VertexBuffer ambientShadowVertexBuffer;
-    ShadowTessellator::tessellateAmbientShadow(width, height, casterTransform,
+    ShadowTessellator::tessellateAmbientShadow(casterPolygon, casterVertexCount,
             ambientShadowVertexBuffer);
     drawVertexBuffer(ambientShadowVertexBuffer, &paint);
 
     VertexBuffer spotShadowVertexBuffer;
     Vector3 lightPosScale(mCaches.propertyLightPosXScale,
             mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
-    ShadowTessellator::tessellateSpotShadow(width, height, lightPosScale,
-            *currentTransform(), getWidth(), getHeight(),
-            casterTransform, spotShadowVertexBuffer);
+    ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount,
+            lightPosScale, *currentTransform(), getWidth(), getHeight(),
+            spotShadowVertexBuffer);
 
     drawVertexBuffer(spotShadowVertexBuffer, &paint);
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fb780ce..806e805 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -214,7 +214,7 @@
     virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
 
     status_t drawShadow(const mat4& casterTransform, float casterAlpha,
-            float width, float height);
+            const SkPath* casterOutline);
 
     virtual void resetShader();
     virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index c6ce67c..cc56333 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -53,7 +53,7 @@
 namespace android {
 namespace uirenderer {
 
-#define THRESHOLD 0.5f
+#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
 #define ROUND_CAP_THRESH 0.25f
 #define PI 3.1415926535897932f
 
@@ -739,7 +739,8 @@
     // force close if we're filling the path, since fill path expects closed perimeter.
     bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
     bool wasClosed = approximatePathOutlineVertices(path, forceClose,
-            threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices);
+            threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
+            OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
 
     if (!tempVertices.size()) {
         // path was empty, return without allocating vertex buffer
@@ -824,7 +825,8 @@
     Vector<Vertex> outlineVertices;
     approximatePathOutlineVertices(path, true,
             paintInfo.inverseScaleX * paintInfo.inverseScaleX,
-            paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+            paintInfo.inverseScaleY * paintInfo.inverseScaleY,
+            OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
 
     if (!outlineVertices.size()) return;
 
@@ -897,6 +899,11 @@
 // Simple path line approximation
 ///////////////////////////////////////////////////////////////////////////////
 
+bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
+        Vector<Vertex>& outputVertices) {
+    return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
+}
+
 void pushToVector(Vector<Vertex>& vertices, float x, float y) {
     // TODO: make this not yuck
     vertices.push();
@@ -905,7 +912,8 @@
 }
 
 bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+        float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+        Vector<Vertex>& outputVertices) {
     ATRACE_CALL();
 
     // TODO: to support joins other than sharp miter, join vertices should be labelled in the
@@ -932,7 +940,7 @@
                         pts[0].x(), pts[0].y(),
                         pts[2].x(), pts[2].y(),
                         pts[1].x(), pts[1].y(),
-                        sqrInvScaleX, sqrInvScaleY, outputVertices);
+                        sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
                 break;
             case SkPath::kCubic_Verb:
                 ALOGV("kCubic_Verb");
@@ -941,7 +949,7 @@
                         pts[1].x(), pts[1].y(),
                         pts[3].x(), pts[3].y(),
                         pts[2].x(), pts[2].y(),
-                        sqrInvScaleX, sqrInvScaleY, outputVertices);
+                        sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
                 break;
             default:
                 break;
@@ -964,7 +972,8 @@
 void PathTessellator::recursiveCubicBezierVertices(
         float p1x, float p1y, float c1x, float c1y,
         float p2x, float p2y, float c2x, float c2y,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+        float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+        Vector<Vertex>& outputVertices) {
     float dx = p2x - p1x;
     float dy = p2y - p1y;
     float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -973,7 +982,7 @@
 
     // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
 
-    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+    if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
         // below thresh, draw line by adding endpoint
         pushToVector(outputVertices, p2x, p2y);
     } else {
@@ -997,11 +1006,11 @@
         recursiveCubicBezierVertices(
                 p1x, p1y, p1c1x, p1c1y,
                 mx, my, p1c1c2x, p1c1c2y,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
+                sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
         recursiveCubicBezierVertices(
                 mx, my, p2c1c2x, p2c1c2y,
                 p2x, p2y, p2c2x, p2c2y,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
+                sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
     }
 }
 
@@ -1009,12 +1018,13 @@
         float ax, float ay,
         float bx, float by,
         float cx, float cy,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+        float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+        Vector<Vertex>& outputVertices) {
     float dx = bx - ax;
     float dy = by - ay;
     float d = (cx - bx) * dy - (cy - by) * dx;
 
-    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+    if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
         // below thresh, draw line by adding endpoint
         pushToVector(outputVertices, bx, by);
     } else {
@@ -1028,9 +1038,9 @@
         float my = (acy + bcy) * 0.5f;
 
         recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
+                sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
         recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
+                sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
     }
 }
 
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index e43b101..a215b7a 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -31,18 +31,64 @@
 public:
     static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint);
 
+    /**
+     * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single
+     * triangle strip. Note: joins are not currently supported.
+     *
+     * @param path The path to be approximated
+     * @param paint The paint the path will be drawn with, indicating AA, painting style
+     *        (stroke vs fill), stroke width, stroke cap & join style, etc.
+     * @param transform The transform the path is to be drawn with, used to drive stretch-aware path
+     *        vertex approximation, and correct AA ramp offsetting.
+     * @param vertexBuffer The output buffer
+     */
     static void tessellatePath(const SkPath& path, const SkPaint* paint,
             const mat4& transform, VertexBuffer& vertexBuffer);
 
+    /**
+     * Populates a VertexBuffer with a tessellated approximation of points as a single triangle
+     * strip (with degenerate tris separating), respecting the shape defined by the paint cap.
+     *
+     * @param points The center vertices of the points to be drawn
+     * @param count The number of floats making up the point vertices
+     * @param paint The paint the points will be drawn with indicating AA, stroke width & cap
+     * @param transform The transform the points will be drawn with, used to drive stretch-aware path
+     *        vertex approximation, and correct AA ramp offsetting
+     * @param bounds An output rectangle, which returns the total area covered by the output buffer
+     * @param vertexBuffer The output buffer
+     */
     static void tessellatePoints(const float* points, int count, const SkPaint* paint,
             const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
 
+    /**
+     * Populates a VertexBuffer with a tessellated approximation of lines as a single triangle
+     * strip (with degenerate tris separating).
+     *
+     * @param points Pairs of endpoints defining the lines to be drawn
+     * @param count The number of floats making up the line vertices
+     * @param paint The paint the lines will be drawn with indicating AA, stroke width & cap
+     * @param transform The transform the points will be drawn with, used to drive stretch-aware path
+     *        vertex approximation, and correct AA ramp offsetting
+     * @param bounds An output rectangle, which returns the total area covered by the output buffer
+     * @param vertexBuffer The output buffer
+     */
     static void tessellateLines(const float* points, int count, const SkPaint* paint,
             const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
 
+    /**
+     * Approximates a convex, CW outline into a Vector of 2d vertices.
+     *
+     * @param path The outline to be approximated
+     * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating
+     * @param outputVertices An empty Vector which will be populated with the output
+     */
+    static bool approximatePathOutlineVertices(const SkPath &path, float thresholdSquared,
+            Vector<Vertex> &outputVertices);
+
 private:
     static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex> &outputVertices);
+            float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
+            Vector<Vertex> &outputVertices);
 
 /*
   endpoints a & b,
@@ -52,7 +98,7 @@
             float ax, float ay,
             float bx, float by,
             float cx, float cy,
-            float sqrInvScaleX, float sqrInvScaleY,
+            float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
             Vector<Vertex> &outputVertices);
 
 /*
@@ -64,7 +110,7 @@
             float c1x, float c1y,
             float p2x, float p2y,
             float c2x, float c2y,
-            float sqrInvScaleX, float sqrInvScaleY,
+            float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
             Vector<Vertex> &outputVertices);
 };
 
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 6dfd925..7700ea0 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -31,46 +31,8 @@
     return a > b ? a : b;
 }
 
-// TODO: Support path as the input of the polygon instead of the rect's width
-// and height. And the z values need to be computed according to the
-// transformation for each vertex.
-/**
- * Generate the polygon for the caster.
- *
- * @param width the width of the caster
- * @param height the height of the caster
- * @param casterTransform transformation info of the caster
- * @param polygon return the caster's polygon
- *
- */
-void ShadowTessellator::generateCasterPolygon(float width, float height,
-        const mat4& casterTransform, int vertexCount, Vector3* polygon) {
-    Rect blockRect(0, 0, width, height);
-    polygon[0].x = blockRect.left;
-    polygon[0].y = blockRect.top;
-    polygon[0].z = 0;
-    polygon[1].x = blockRect.right;
-    polygon[1].y = blockRect.top;
-    polygon[1].z = 0;
-    polygon[2].x = blockRect.right;
-    polygon[2].y = blockRect.bottom;
-    polygon[2].z = 0;
-    polygon[3].x = blockRect.left;
-    polygon[3].y = blockRect.bottom;
-    polygon[3].z = 0;
-    casterTransform.mapPoint3d(polygon[0]);
-    casterTransform.mapPoint3d(polygon[1]);
-    casterTransform.mapPoint3d(polygon[2]);
-    casterTransform.mapPoint3d(polygon[3]);
-}
-
-void ShadowTessellator::tessellateAmbientShadow(float width, float height,
-        const mat4& casterTransform, VertexBuffer& shadowVertexBuffer) {
-
-    const int vertexCount = 4;
-    Vector3 polygon[vertexCount];
-    generateCasterPolygon(width, height, casterTransform, vertexCount, polygon);
-
+void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon, int casterVertexCount,
+        VertexBuffer& shadowVertexBuffer) {
     // A bunch of parameters to tweak the shadow.
     // TODO: Allow some of these changable by debug settings or APIs.
     const int rays = 128;
@@ -79,19 +41,14 @@
     const float heightFactor = 128;
     const float geomFactor = 64;
 
-    AmbientShadow::createAmbientShadow(polygon, vertexCount, rays, layers, strength,
+    AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount, rays, layers, strength,
             heightFactor, geomFactor, shadowVertexBuffer);
 
 }
 
-void ShadowTessellator::tessellateSpotShadow(float width, float height,
+void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
         const Vector3& lightPosScale, const mat4& receiverTransform,
-        int screenWidth, int screenHeight, const mat4& casterTransform,
-        VertexBuffer& shadowVertexBuffer) {
-    const int vertexCount = 4;
-    Vector3 polygon[vertexCount];
-    generateCasterPolygon(width, height, casterTransform, vertexCount, polygon);
-
+        int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
     // A bunch of parameters to tweak the shadow.
     // TODO: Allow some of these changable by debug settings or APIs.
     const int rays = 256;
@@ -113,7 +70,7 @@
     const float lightSize = maximal / 4;
     const int lightVertexCount = 16;
 
-    SpotShadow::createSpotShadow(polygon, vertexCount, lightCenter, lightSize,
+    SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter, lightSize,
             lightVertexCount, rays, layers, strength, shadowVertexBuffer);
 
 }
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 2399f8f..ef95609 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -26,18 +26,12 @@
 
 class ShadowTessellator {
 public:
-    static void tessellateAmbientShadow(float width, float height,
-            const mat4& casterTransform, VertexBuffer& shadowVertexBuffer);
-
-    static void tessellateSpotShadow(float width, float height,
-            const Vector3& lightPosScale, const mat4& receiverTransform,
-            int screenWidth, int screenHeight, const mat4& casterTransform,
+    static void tessellateAmbientShadow(const Vector3* casterPolygon, int casterVertexCount,
             VertexBuffer& shadowVertexBuffer);
 
-private:
-    static void generateCasterPolygon(float width, float height,
-            const mat4& casterTransform, int vertexCount, Vector3* polygon);
-
+    static void tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
+            const Vector3& lightPosScale, const mat4& receiverTransform,
+            int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer);
 }; // ShadowTessellator
 
 }; // namespace uirenderer