Fix hairline pathrenderer for Nexus-10. Switches to using additional
geometry and passing in the coverage value instead.

BUG=
R=robertphillips@google.com, bsalomon@google.com

Author: jvanverth@google.com

Review URL: https://chromiumcodereview.appspot.com/22486003

git-svn-id: http://skia.googlecode.com/svn/trunk@10640 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index c1f951a..33c2ac3 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -29,15 +29,20 @@
 static const int kVertsPerQuad = 5;
 static const int kIdxsPerQuad = 9;
 
-static const int kVertsPerLineSeg = 4;
-static const int kIdxsPerLineSeg = 6;
+static const int kVertsPerLineSeg = 6;
+static const int kIdxsPerLineSeg = 12;
 
 static const int kNumQuadsInIdxBuffer = 256;
 static const size_t kQuadIdxSBufize = kIdxsPerQuad *
                                       sizeof(uint16_t) *
                                       kNumQuadsInIdxBuffer;
 
-bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
+static const int kNumLineSegsInIdxBuffer = 256;
+static const size_t kLineSegIdxSBufize = kIdxsPerLineSeg *
+                                         sizeof(uint16_t) *
+                                         kNumLineSegsInIdxBuffer;
+
+static bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
     uint16_t* data = (uint16_t*) qIdxBuffer->lock();
     bool tempData = NULL == data;
     if (tempData) {
@@ -78,22 +83,66 @@
         return true;
     }
 }
+
+static bool push_line_index_data(GrIndexBuffer* lIdxBuffer) {
+    uint16_t* data = (uint16_t*) lIdxBuffer->lock();
+    bool tempData = NULL == data;
+    if (tempData) {
+        data = SkNEW_ARRAY(uint16_t, kNumLineSegsInIdxBuffer * kIdxsPerLineSeg);
+    }
+    for (int i = 0; i < kNumLineSegsInIdxBuffer; ++i) {
+        // Each line segment is rendered as two quads, with alpha = 1 along the
+        // spine of the segment, and alpha = 0 along the outer edges, represented
+        // horizontally (i.e., the line equation is t*(p1-p0) + p0)
+        //
+        // p4                  p5
+        // p0                  p1
+        // p2                  p3
+        //
+        // Each is drawn as four triangles specified by these 12 indices:
+        int baseIdx = i * kIdxsPerLineSeg;
+        uint16_t baseVert = (uint16_t)(i * kVertsPerLineSeg);
+        data[0 + baseIdx] = baseVert + 0; // p0
+        data[1 + baseIdx] = baseVert + 1; // p1
+        data[2 + baseIdx] = baseVert + 2; // p2
+        
+        data[3 + baseIdx] = baseVert + 2; // p2
+        data[4 + baseIdx] = baseVert + 1; // p1
+        data[5 + baseIdx] = baseVert + 3; // p3
+        
+        data[6 + baseIdx] = baseVert + 0; // p0
+        data[7 + baseIdx] = baseVert + 5; // p5
+        data[8 + baseIdx] = baseVert + 1; // p1
+        
+        data[9 + baseIdx] = baseVert + 0; // p0
+        data[10+ baseIdx] = baseVert + 4; // p4
+        data[11+ baseIdx] = baseVert + 5; // p5
+    }
+    if (tempData) {
+        bool ret = lIdxBuffer->updateData(data, kLineSegIdxSBufize);
+        delete[] data;
+        return ret;
+    } else {
+        lIdxBuffer->unlock();
+        return true;
+    }
+}
 }
 
 GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
-    const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
-    if (NULL == lIdxBuffer) {
-        return NULL;
-    }
     GrGpu* gpu = context->getGpu();
     GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
     SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
-    if (NULL == qIdxBuf ||
-        !push_quad_index_data(qIdxBuf)) {
+    if (NULL == qIdxBuf || !push_quad_index_data(qIdxBuf)) {
+        return NULL;
+    }
+    GrIndexBuffer* lIdxBuf = gpu->createIndexBuffer(kLineSegIdxSBufize, false);
+    SkAutoTUnref<GrIndexBuffer> lIdxBuffer(lIdxBuf);
+    if (NULL == lIdxBuf || !push_line_index_data(lIdxBuf)) {
         return NULL;
     }
     return SkNEW_ARGS(GrAAHairLinePathRenderer,
-                      (context, lIdxBuffer, qIdxBuf));
+                      (context, lIdxBuf, qIdxBuf));
 }
 
 GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
@@ -436,9 +485,7 @@
     GrPoint fPos;
     union {
         struct {
-            SkScalar fA;
-            SkScalar fB;
-            SkScalar fC;
+            SkScalar fCoverage;
         } fLine;
         struct {
             SkScalar fK;
@@ -661,17 +708,17 @@
     if (orthVec.setLength(SK_Scalar1)) {
         orthVec.setOrthog(orthVec);
 
-        SkScalar lineC = -(a.dot(orthVec));
         for (int i = 0; i < kVertsPerLineSeg; ++i) {
-            (*vert)[i].fPos = (i < 2) ? a : b;
-            if (0 == i || 3 == i) {
-                (*vert)[i].fPos -= orthVec;
-            } else {
+            (*vert)[i].fPos = (i & 0x1) ? b : a;
+            if (i & 0x2) {
                 (*vert)[i].fPos += orthVec;
+                (*vert)[i].fLine.fCoverage = 0;
+            } else if (i & 0x4) {
+                (*vert)[i].fPos -= orthVec;
+                (*vert)[i].fLine.fCoverage = 0;
+            } else {
+                (*vert)[i].fLine.fCoverage = SK_Scalar1;
             }
-            (*vert)[i].fLine.fA = orthVec.fX;
-            (*vert)[i].fLine.fB = orthVec.fY;
-            (*vert)[i].fLine.fC = lineC;
         }
         if (NULL != toSrc) {
             toSrc->mapPointsWithStride(&(*vert)->fPos,
@@ -684,6 +731,8 @@
         (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
         (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
         (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
+        (*vert)[4].fPos.set(SK_ScalarMax, SK_ScalarMax);
+        (*vert)[5].fPos.set(SK_ScalarMax, SK_ScalarMax);
     }
 
     *vert += kVertsPerLineSeg;
@@ -936,7 +985,7 @@
 
 /**
  * The output of this effect is a 1-pixel wide line.
- * Input is 2D implicit device coord line eq (a*x + b*y +c = 0). 4th component unused.
+ * Input is coverage relative to the line.
  */
 class HairLineEdgeEffect : public GrEffect {
 public:
@@ -978,9 +1027,7 @@
 
             builder->addVarying(kVec4f_GrSLType, "HairLineEdge", &vsName, &fsName);
 
-            builder->fsCodeAppendf("\t\tedgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n",
-                                   builder->fragmentPosition(), fsName);
-            builder->fsCodeAppendf("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            builder->fsCodeAppendf("\t\tedgeAlpha = %s.x;\n", fsName);
 
             SkString modulate;
             GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
@@ -1191,13 +1238,15 @@
 #endif
 
     {
+        // the fact we're using an effect to pass per-vertex coverage is kind of dumb,
+        // but necessary due to the shared vertex buffer
+        // TODO: refactor so that we render lines with a separate vertex buffer
         GrDrawState::AutoRestoreEffects are(drawState);
         target->setIndexSourceToBuffer(fLinesIndexBuffer);
         int lines = 0;
-        int nBufLines = fLinesIndexBuffer->maxQuads();
         drawState->addCoverageEffect(hairLineEffect, kEdgeAttrIndex)->unref();
         while (lines < lineCnt) {
-            int n = GrMin(lineCnt - lines, nBufLines);
+            int n = GrMin(lineCnt - lines, kNumLineSegsInIdxBuffer);
             target->drawIndexed(kTriangles_GrPrimitiveType,
                                 kVertsPerLineSeg*lines,     // startV
                                 0,                          // startI