Implement stroking a path with nv_path_rendering

Initialize the path stroke properties in the GrGLPath constructor.
Use StencilStrokePath and CoverStrokePath to stroke the path.

The order of the GL calls is:
1. StencilFill, if needed
2. StencilStroke, if needed
2a. CoverStroke, if stroke was applied
2b. CoverFill, if stroke was not applied

The reason for not pairing StencilFill + CoverFill, StencilStroke +
CoverStroke is that Skia API does not allow separate fill and stroke
color within one call. Covering the stroke bounding box should also
cover the fill bounding box.

Causes different rendering in gm/dashcubics due to different rendering
algorithm. (?) (TODO: this should be resolved somehow.)

R=bsalomon@google.com, markkilgard@gmail.com, cdalton@nvidia.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/23440049

git-svn-id: http://skia.googlecode.com/svn/trunk@11672 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
index c5a4425..c85644e 100644
--- a/src/gpu/gl/GrGLPath.cpp
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -8,6 +8,7 @@
 
 #include "GrGLPath.h"
 #include "GrGpuGL.h"
+#include "SkStrokeRec.h"
 
 #define GPUGL static_cast<GrGpuGL*>(this->getGpu())
 
@@ -15,7 +16,7 @@
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(GPUGL->glInterface(), R, X)
 
 namespace {
-inline GrGLubyte verb_to_gl_path_cmd(const SkPath::Verb verb) {
+inline GrGLubyte verb_to_gl_path_cmd(SkPath::Verb verb) {
     static const GrGLubyte gTable[] = {
         GR_GL_MOVE_TO,
         GR_GL_LINE_TO,
@@ -35,7 +36,7 @@
 }
 
 #ifdef SK_DEBUG
-inline int num_pts(const SkPath::Verb verb) {
+inline int num_pts(SkPath::Verb verb) {
     static const int gTable[] = {
         1, // move
         1, // line
@@ -54,11 +55,39 @@
     return gTable[verb];
 }
 #endif
+
+inline GrGLenum join_to_gl_join(SkPaint::Join join) {
+    static GrGLenum gSkJoinsToGrGLJoins[] = {
+        GR_GL_MITER_REVERT,
+        GR_GL_ROUND,
+        GR_GL_BEVEL
+    };
+    return gSkJoinsToGrGLJoins[join];
+    GR_STATIC_ASSERT(0 == SkPaint::kMiter_Join);
+    GR_STATIC_ASSERT(1 == SkPaint::kRound_Join);
+    GR_STATIC_ASSERT(2 == SkPaint::kBevel_Join);
+    GR_STATIC_ASSERT(GR_ARRAY_COUNT(gSkJoinsToGrGLJoins) == SkPaint::kJoinCount);
+}
+
+inline GrGLenum cap_to_gl_cap(SkPaint::Cap cap) {
+    static GrGLenum gSkCapsToGrGLCaps[] = {
+        GR_GL_FLAT,
+        GR_GL_ROUND,
+        GR_GL_SQUARE
+    };
+    return gSkCapsToGrGLCaps[cap];
+    GR_STATIC_ASSERT(0 == SkPaint::kButt_Cap);
+    GR_STATIC_ASSERT(1 == SkPaint::kRound_Cap);
+    GR_STATIC_ASSERT(2 == SkPaint::kSquare_Cap);
+    GR_STATIC_ASSERT(GR_ARRAY_COUNT(gSkCapsToGrGLCaps) == SkPaint::kCapCount);
+}
+
 }
 
 static const bool kIsWrapped = false; // The constructor creates the GL path object.
 
-GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu, kIsWrapped) {
+GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
+    : INHERITED(gpu, kIsWrapped, stroke) {
 #ifndef SK_SCALAR_IS_FLOAT
     GrCrash("Assumes scalar is float.");
 #endif
@@ -90,6 +119,19 @@
                          verbCnt, &pathCommands[0],
                          2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
     fBounds = path.getBounds();
+
+    if (stroke.needToApply()) {
+        GL_CALL(PathParameterf(fPathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));
+        GL_CALL(PathParameterf(fPathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter())));
+        GrGLenum join = join_to_gl_join(stroke.getJoin());
+        GL_CALL(PathParameteri(fPathID, GR_GL_PATH_JOIN_STYLE, join));
+        GrGLenum cap = cap_to_gl_cap(stroke.getCap());
+        GL_CALL(PathParameteri(fPathID, GR_GL_PATH_INITIAL_END_CAP, cap));
+        GL_CALL(PathParameteri(fPathID, GR_GL_PATH_TERMINAL_END_CAP, cap));
+
+        // FIXME: try to account for stroking, without rasterizing the stroke.
+        fBounds.outset(SkScalarToFloat(stroke.getWidth()), SkScalarToFloat(stroke.getWidth()));
+    }
 }
 
 GrGLPath::~GrGLPath() {
diff --git a/src/gpu/gl/GrGLPath.h b/src/gpu/gl/GrGLPath.h
index dfe3405..ef3aa56 100644
--- a/src/gpu/gl/GrGLPath.h
+++ b/src/gpu/gl/GrGLPath.h
@@ -14,6 +14,7 @@
 
 class GrGpuGL;
 class SkPath;
+class SkStrokeRec;
 
 /**
  * Currently this represents a path built using GL_NV_path_rendering. If we
@@ -23,7 +24,7 @@
 
 class GrGLPath : public GrPath {
 public:
-    GrGLPath(GrGpuGL* gpu, const SkPath& path);
+    GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke);
     virtual ~GrGLPath();
     GrGLuint pathID() const { return fPathID; }
     // TODO: Figure out how to get an approximate size of the path in Gpu
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 1138667..d313167 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -12,6 +12,7 @@
 #include "GrGLShaderBuilder.h"
 #include "GrTemplates.h"
 #include "GrTypes.h"
+#include "SkStrokeRec.h"
 #include "SkTemplates.h"
 
 static const GrGLuint GR_MAX_GLUINT = ~0U;
@@ -1276,9 +1277,9 @@
     }
 }
 
-GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) {
+GrPath* GrGpuGL::onCreatePath(const SkPath& inPath, const SkStrokeRec& stroke) {
     SkASSERT(this->caps()->pathRenderingSupport());
-    return SkNEW_ARGS(GrGLPath, (this, inPath));
+    return SkNEW_ARGS(GrGLPath, (this, inPath, stroke));
 }
 
 void GrGpuGL::flushScissor() {
@@ -1700,7 +1701,7 @@
     GL_CALL(StencilFillPath(id, fillMode, writeMask));
 }
 
-void GrGpuGL::onGpuFillPath(const GrPath* path, SkPath::FillType fill) {
+void GrGpuGL::onGpuDrawPath(const GrPath* path, SkPath::FillType fill) {
     SkASSERT(this->caps()->pathRenderingSupport());
 
     GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
@@ -1709,16 +1710,27 @@
     SkASSERT(!fCurrentProgram->hasVertexShader());
 
     flushPathStencilSettings(fill);
+    const SkStrokeRec& stroke = path->getStroke();
 
     SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(fill);
     SkASSERT(!fHWPathStencilSettings.isTwoSided());
     GrGLenum fillMode =
         gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
     GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
-    GL_CALL(StencilFillPath(id, fillMode, writeMask));
+
+    if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
+        GL_CALL(StencilFillPath(id, fillMode, writeMask));
+    }
+    if (stroke.needToApply()) {
+        GL_CALL(StencilStrokePath(id, 0xffff, writeMask));
+    }
 
     if (nonInvertedFill == fill) {
-        GL_CALL(CoverFillPath(id, GR_GL_BOUNDING_BOX));
+        if (stroke.needToApply()) {
+            GL_CALL(CoverStrokePath(id, GR_GL_BOUNDING_BOX));
+        } else {
+            GL_CALL(CoverFillPath(id, GR_GL_BOUNDING_BOX));
+        }
     } else {
         GrDrawState* drawState = this->drawState();
         GrDrawState::AutoViewMatrixRestore avmr;
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index a945e54..17699ef 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -121,7 +121,7 @@
                                                  bool dynamic) SK_OVERRIDE;
     virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
                                                bool dynamic) SK_OVERRIDE;
-    virtual GrPath* onCreatePath(const SkPath&) SK_OVERRIDE;
+    virtual GrPath* onCreatePath(const SkPath&, const SkStrokeRec&) SK_OVERRIDE;
     virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE;
     virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE;
     virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
@@ -152,7 +152,7 @@
     virtual void onGpuDraw(const DrawInfo&) SK_OVERRIDE;
 
     virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
-    virtual void onGpuFillPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
+    virtual void onGpuDrawPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
 
     virtual void clearStencil() SK_OVERRIDE;
     virtual void clearStencilClip(const SkIRect& rect,
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index f3b566c..4f9adb7 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -253,7 +253,7 @@
             return false;
         }
 
-        SkASSERT(kFillPath_DrawType != type || !fCurrentProgram->hasVertexShader());
+        SkASSERT(kDrawPath_DrawType != type || !fCurrentProgram->hasVertexShader());
 
         fCurrentProgram.get()->ref();