Add more shape drawing to new reorderer/renderer

bug:22480459

Add support for outsetting final bounds based on stroke.

Change-Id: I659318ccec51882bba1906ce3c7042288ce35c30
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index fde12dd..3d35dd5 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -20,6 +20,7 @@
 #include "Caches.h"
 #include "Glop.h"
 #include "GlopBuilder.h"
+#include "PathTessellator.h"
 #include "renderstate/OffscreenBufferPool.h"
 #include "renderstate/RenderState.h"
 #include "utils/GLUtils.h"
@@ -27,6 +28,7 @@
 
 #include <algorithm>
 #include <math.h>
+#include <SkPaintDefaults.h>
 
 namespace android {
 namespace uirenderer {
@@ -206,6 +208,90 @@
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
+namespace VertexBufferRenderFlags {
+    enum {
+        Offset = 0x1,
+        ShadowInterp = 0x2,
+    };
+}
+
+static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
+        const VertexBuffer& vertexBuffer, float translateX, float translateY,
+        const SkPaint& paint, int vertexBufferRenderFlags) {
+    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
+        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
+        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+                .setFillPaint(paint, state.alpha)
+                .setTransform(state.computedState.transform, transformFlags)
+                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+                .build();
+        renderer.renderGlop(state, glop);
+    }
+}
+
+static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
+        const SkPath& path, const SkPaint& paint) {
+    VertexBuffer vertexBuffer;
+    // TODO: try clipping large paths to viewport
+    PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
+    renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
+}
+
+static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
+        PathTexture& texture, const RecordedOp& op) {
+    Rect dest(texture.width, texture.height);
+    dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
+            texture.top + op.unmappedBounds.top - texture.offset);
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshTexturedUnitQuad(nullptr)
+            .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
+            .setTransform(state.computedState.transform,  TransformFlags::None)
+            .setModelViewMapUnitToRect(dest)
+            .build();
+    renderer.renderGlop(state, glop);
+}
+
+SkRect getBoundsOfFill(const RecordedOp& op) {
+    SkRect bounds = op.unmappedBounds.toSkRect();
+    if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
+        float outsetDistance = op.paint->getStrokeWidth() / 2;
+        bounds.outset(outsetDistance, outsetDistance);
+    }
+    return bounds;
+}
+
+void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
+    // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
+    if (op.paint->getStyle() != SkPaint::kStroke_Style
+            || op.paint->getPathEffect() != nullptr
+            || op.useCenter) {
+        PathTexture* texture = renderer.caches().pathCache.getArc(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+                op.startAngle, op.sweepAngle, op.useCenter, op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        SkRect rect = getBoundsOfFill(op);
+        SkPath path;
+        if (op.useCenter) {
+            path.moveTo(rect.centerX(), rect.centerY());
+        }
+        path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
+        if (op.useCenter) {
+            path.close();
+        }
+        renderConvexPath(renderer, state, path, *(op.paint));
+    }
+}
+
 void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
     Texture* texture = renderer.getTexture(op.bitmap);
     if (!texture) return;
@@ -225,43 +311,101 @@
 }
 
 void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("todo");
+    VertexBuffer buffer;
+    PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
+            state.computedState.transform, buffer);
+    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
 }
 
+void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
+    if (op.paint->getPathEffect() != nullptr) {
+        PathTexture* texture = renderer.caches().pathCache.getOval(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        SkPath path;
+        SkRect rect = getBoundsOfFill(op);
+        path.addOval(rect);
+        renderConvexPath(renderer, state, path, *(op.paint));
+    }
+}
+
+void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
+    PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
+    const AutoTexture holder(texture);
+    if (CC_LIKELY(holder.texture)) {
+        renderPathTexture(renderer, state, *texture, op);
+    }
+}
+
+void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
+    VertexBuffer buffer;
+    PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
+            state.computedState.transform, buffer);
+    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
+}
+
+// See SkPaintDefaults.h
+#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
+
 void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
-    Glop glop;
-    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-            .setRoundRectClipState(state.roundRectClipState)
-            .setMeshUnitQuad()
-            .setFillPaint(*op.paint, state.alpha)
-            .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewMapUnitToRect(op.unmappedBounds)
-            .build();
-    renderer.renderGlop(state, glop);
+    if (op.paint->getStyle() != SkPaint::kFill_Style) {
+        // only fill + default miter is supported by drawConvexPath, since others must handle joins
+        static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
+        if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
+                || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
+                || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
+             PathTexture* texture = renderer.caches().pathCache.getRect(
+                     op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+             const AutoTexture holder(texture);
+             if (CC_LIKELY(holder.texture)) {
+                 renderPathTexture(renderer, state, *texture, op);
+             }
+        } else {
+            SkPath path;
+            path.addRect(getBoundsOfFill(op));
+            renderConvexPath(renderer, state, path, *(op.paint));
+        }
+    } else {
+        if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
+            SkPath path;
+            path.addRect(op.unmappedBounds.toSkRect());
+            renderConvexPath(renderer, state, path, *(op.paint));
+        } else {
+            // render simple unit quad, no tessellation required
+            Glop glop;
+            GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                    .setRoundRectClipState(state.roundRectClipState)
+                    .setMeshUnitQuad()
+                    .setFillPaint(*op.paint, state.alpha)
+                    .setTransform(state.computedState.transform, TransformFlags::None)
+                    .setModelViewMapUnitToRect(op.unmappedBounds)
+                    .build();
+            renderer.renderGlop(state, glop);
+        }
+    }
 }
 
-namespace VertexBufferRenderFlags {
-    enum {
-        Offset = 0x1,
-        ShadowInterp = 0x2,
-    };
-}
-
-static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
-        const VertexBuffer& vertexBuffer, float translateX, float translateY,
-        SkPaint& paint, int vertexBufferRenderFlags) {
-    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
-        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
-        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
-        Glop glop;
-        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-                .setRoundRectClipState(state.roundRectClipState)
-                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
-                .setFillPaint(paint, state.alpha)
-                .setTransform(state.computedState.transform, transformFlags)
-                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
-                .build();
-        renderer.renderGlop(state, glop);
+void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
+    if (op.paint->getPathEffect() != nullptr) {
+        PathTexture* texture = renderer.caches().pathCache.getRoundRect(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+                op.rx, op.ry, op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
+                state.computedState.transform, *(op.paint),
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
+        renderVertexBuffer(renderer, state, *buffer,
+                op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
     }
 }
 
@@ -323,8 +467,6 @@
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
-    // TODO: extend this to handle HW layers & paint properties which
-    // reside in node.properties().layerProperties()
     float layerAlpha = op.alpha * state.alpha;
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)