Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrAAHairLinePathRenderer.h"
      9 
     10 #include "GrBatch.h"
     11 #include "GrBatchTarget.h"
     12 #include "GrBatchTest.h"
     13 #include "GrContext.h"
     14 #include "GrDefaultGeoProcFactory.h"
     15 #include "GrDrawTargetCaps.h"
     16 #include "GrIndexBuffer.h"
     17 #include "GrPathUtils.h"
     18 #include "GrPipelineBuilder.h"
     19 #include "GrProcessor.h"
     20 #include "GrResourceProvider.h"
     21 #include "GrVertexBuffer.h"
     22 #include "SkGeometry.h"
     23 #include "SkStroke.h"
     24 #include "SkTemplates.h"
     25 
     26 #include "effects/GrBezierEffect.h"
     27 
     28 #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
     29 
     30 // quadratics are rendered as 5-sided polys in order to bound the
     31 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
     32 // bloat_quad. Quadratics and conics share an index buffer
     33 
     34 // lines are rendered as:
     35 //      *______________*
     36 //      |\ -_______   /|
     37 //      | \        \ / |
     38 //      |  *--------*  |
     39 //      | /  ______/ \ |
     40 //      */_-__________\*
     41 // For: 6 vertices and 18 indices (for 6 triangles)
     42 
     43 // Each quadratic is rendered as a five sided polygon. This poly bounds
     44 // the quadratic's bounding triangle but has been expanded so that the
     45 // 1-pixel wide area around the curve is inside the poly.
     46 // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
     47 // that is rendered would look like this:
     48 //              b0
     49 //              b
     50 //
     51 //     a0              c0
     52 //      a            c
     53 //       a1       c1
     54 // Each is drawn as three triangles ((a0,a1,b0), (b0,c1,c0), (a1,c1,b0))
     55 // specified by these 9 indices:
     56 static const uint16_t kQuadIdxBufPattern[] = {
     57     0, 1, 2,
     58     2, 4, 3,
     59     1, 4, 2
     60 };
     61 
     62 static const int kIdxsPerQuad = SK_ARRAY_COUNT(kQuadIdxBufPattern);
     63 static const int kQuadNumVertices = 5;
     64 static const int kQuadsNumInIdxBuffer = 256;
     65 GR_DECLARE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
     66 
     67 static const GrIndexBuffer* ref_quads_index_buffer(GrResourceProvider* resourceProvider) {
     68     GR_DEFINE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
     69     return resourceProvider->refOrCreateInstancedIndexBuffer(
     70         kQuadIdxBufPattern, kIdxsPerQuad, kQuadsNumInIdxBuffer, kQuadNumVertices,
     71         gQuadsIndexBufferKey);
     72 }
     73 
     74 
     75 // Each line segment is rendered as two quads and two triangles.
     76 // p0 and p1 have alpha = 1 while all other points have alpha = 0.
     77 // The four external points are offset 1 pixel perpendicular to the
     78 // line and half a pixel parallel to the line.
     79 //
     80 // p4                  p5
     81 //      p0         p1
     82 // p2                  p3
     83 //
     84 // Each is drawn as six triangles specified by these 18 indices:
     85 
     86 static const uint16_t kLineSegIdxBufPattern[] = {
     87     0, 1, 3,
     88     0, 3, 2,
     89     0, 4, 5,
     90     0, 5, 1,
     91     0, 2, 4,
     92     1, 5, 3
     93 };
     94 
     95 static const int kIdxsPerLineSeg = SK_ARRAY_COUNT(kLineSegIdxBufPattern);
     96 static const int kLineSegNumVertices = 6;
     97 static const int kLineSegsNumInIdxBuffer = 256;
     98 
     99 GR_DECLARE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
    100 
    101 static const GrIndexBuffer* ref_lines_index_buffer(GrResourceProvider* resourceProvider) {
    102     GR_DEFINE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
    103     return resourceProvider->refOrCreateInstancedIndexBuffer(
    104         kLineSegIdxBufPattern, kIdxsPerLineSeg,  kLineSegsNumInIdxBuffer, kLineSegNumVertices,
    105         gLinesIndexBufferKey);
    106 }
    107 
    108 // Takes 178th time of logf on Z600 / VC2010
    109 static int get_float_exp(float x) {
    110     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
    111 #ifdef SK_DEBUG
    112     static bool tested;
    113     if (!tested) {
    114         tested = true;
    115         SkASSERT(get_float_exp(0.25f) == -2);
    116         SkASSERT(get_float_exp(0.3f) == -2);
    117         SkASSERT(get_float_exp(0.5f) == -1);
    118         SkASSERT(get_float_exp(1.f) == 0);
    119         SkASSERT(get_float_exp(2.f) == 1);
    120         SkASSERT(get_float_exp(2.5f) == 1);
    121         SkASSERT(get_float_exp(8.f) == 3);
    122         SkASSERT(get_float_exp(100.f) == 6);
    123         SkASSERT(get_float_exp(1000.f) == 9);
    124         SkASSERT(get_float_exp(1024.f) == 10);
    125         SkASSERT(get_float_exp(3000000.f) == 21);
    126     }
    127 #endif
    128     const int* iptr = (const int*)&x;
    129     return (((*iptr) & 0x7f800000) >> 23) - 127;
    130 }
    131 
    132 // Uses the max curvature function for quads to estimate
    133 // where to chop the conic. If the max curvature is not
    134 // found along the curve segment it will return 1 and
    135 // dst[0] is the original conic. If it returns 2 the dst[0]
    136 // and dst[1] are the two new conics.
    137 static int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
    138     SkScalar t = SkFindQuadMaxCurvature(src);
    139     if (t == 0) {
    140         if (dst) {
    141             dst[0].set(src, weight);
    142         }
    143         return 1;
    144     } else {
    145         if (dst) {
    146             SkConic conic;
    147             conic.set(src, weight);
    148             conic.chopAt(t, dst);
    149         }
    150         return 2;
    151     }
    152 }
    153 
    154 // Calls split_conic on the entire conic and then once more on each subsection.
    155 // Most cases will result in either 1 conic (chop point is not within t range)
    156 // or 3 points (split once and then one subsection is split again).
    157 static int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
    158     SkConic dstTemp[2];
    159     int conicCnt = split_conic(src, dstTemp, weight);
    160     if (2 == conicCnt) {
    161         int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
    162         conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
    163     } else {
    164         dst[0] = dstTemp[0];
    165     }
    166     return conicCnt;
    167 }
    168 
    169 // returns 0 if quad/conic is degen or close to it
    170 // in this case approx the path with lines
    171 // otherwise returns 1
    172 static int is_degen_quad_or_conic(const SkPoint p[3], SkScalar* dsqd) {
    173     static const SkScalar gDegenerateToLineTol = GrPathUtils::kDefaultTolerance;
    174     static const SkScalar gDegenerateToLineTolSqd =
    175         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
    176 
    177     if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
    178         p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
    179         return 1;
    180     }
    181 
    182     *dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
    183     if (*dsqd < gDegenerateToLineTolSqd) {
    184         return 1;
    185     }
    186 
    187     if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
    188         return 1;
    189     }
    190     return 0;
    191 }
    192 
    193 static int is_degen_quad_or_conic(const SkPoint p[3]) {
    194     SkScalar dsqd;
    195     return is_degen_quad_or_conic(p, &dsqd);
    196 }
    197 
    198 // we subdivide the quads to avoid huge overfill
    199 // if it returns -1 then should be drawn as lines
    200 static int num_quad_subdivs(const SkPoint p[3]) {
    201     SkScalar dsqd;
    202     if (is_degen_quad_or_conic(p, &dsqd)) {
    203         return -1;
    204     }
    205 
    206     // tolerance of triangle height in pixels
    207     // tuned on windows  Quadro FX 380 / Z600
    208     // trade off of fill vs cpu time on verts
    209     // maybe different when do this using gpu (geo or tess shaders)
    210     static const SkScalar gSubdivTol = 175 * SK_Scalar1;
    211 
    212     if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
    213         return 0;
    214     } else {
    215         static const int kMaxSub = 4;
    216         // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
    217         // = log4(d*d/tol*tol)/2
    218         // = log2(d*d/tol*tol)
    219 
    220         // +1 since we're ignoring the mantissa contribution.
    221         int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
    222         log = SkTMin(SkTMax(0, log), kMaxSub);
    223         return log;
    224     }
    225 }
    226 
    227 /**
    228  * Generates the lines and quads to be rendered. Lines are always recorded in
    229  * device space. We will do a device space bloat to account for the 1pixel
    230  * thickness.
    231  * Quads are recorded in device space unless m contains
    232  * perspective, then in they are in src space. We do this because we will
    233  * subdivide large quads to reduce over-fill. This subdivision has to be
    234  * performed before applying the perspective matrix.
    235  */
    236 static int gather_lines_and_quads(const SkPath& path,
    237                                   const SkMatrix& m,
    238                                   const SkIRect& devClipBounds,
    239                                   GrAAHairLinePathRenderer::PtArray* lines,
    240                                   GrAAHairLinePathRenderer::PtArray* quads,
    241                                   GrAAHairLinePathRenderer::PtArray* conics,
    242                                   GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
    243                                   GrAAHairLinePathRenderer::FloatArray* conicWeights) {
    244     SkPath::Iter iter(path, false);
    245 
    246     int totalQuadCount = 0;
    247     SkRect bounds;
    248     SkIRect ibounds;
    249 
    250     bool persp = m.hasPerspective();
    251 
    252     for (;;) {
    253         SkPoint pathPts[4];
    254         SkPoint devPts[4];
    255         SkPath::Verb verb = iter.next(pathPts);
    256         switch (verb) {
    257             case SkPath::kConic_Verb: {
    258                 SkConic dst[4];
    259                 // We chop the conics to create tighter clipping to hide error
    260                 // that appears near max curvature of very thin conics. Thin
    261                 // hyperbolas with high weight still show error.
    262                 int conicCnt = chop_conic(pathPts, dst, iter.conicWeight());
    263                 for (int i = 0; i < conicCnt; ++i) {
    264                     SkPoint* chopPnts = dst[i].fPts;
    265                     m.mapPoints(devPts, chopPnts, 3);
    266                     bounds.setBounds(devPts, 3);
    267                     bounds.outset(SK_Scalar1, SK_Scalar1);
    268                     bounds.roundOut(&ibounds);
    269                     if (SkIRect::Intersects(devClipBounds, ibounds)) {
    270                         if (is_degen_quad_or_conic(devPts)) {
    271                             SkPoint* pts = lines->push_back_n(4);
    272                             pts[0] = devPts[0];
    273                             pts[1] = devPts[1];
    274                             pts[2] = devPts[1];
    275                             pts[3] = devPts[2];
    276                         } else {
    277                             // when in perspective keep conics in src space
    278                             SkPoint* cPts = persp ? chopPnts : devPts;
    279                             SkPoint* pts = conics->push_back_n(3);
    280                             pts[0] = cPts[0];
    281                             pts[1] = cPts[1];
    282                             pts[2] = cPts[2];
    283                             conicWeights->push_back() = dst[i].fW;
    284                         }
    285                     }
    286                 }
    287                 break;
    288             }
    289             case SkPath::kMove_Verb:
    290                 break;
    291             case SkPath::kLine_Verb:
    292                 m.mapPoints(devPts, pathPts, 2);
    293                 bounds.setBounds(devPts, 2);
    294                 bounds.outset(SK_Scalar1, SK_Scalar1);
    295                 bounds.roundOut(&ibounds);
    296                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
    297                     SkPoint* pts = lines->push_back_n(2);
    298                     pts[0] = devPts[0];
    299                     pts[1] = devPts[1];
    300                 }
    301                 break;
    302             case SkPath::kQuad_Verb: {
    303                 SkPoint choppedPts[5];
    304                 // Chopping the quad helps when the quad is either degenerate or nearly degenerate.
    305                 // When it is degenerate it allows the approximation with lines to work since the
    306                 // chop point (if there is one) will be at the parabola's vertex. In the nearly
    307                 // degenerate the QuadUVMatrix computed for the points is almost singular which
    308                 // can cause rendering artifacts.
    309                 int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts);
    310                 for (int i = 0; i < n; ++i) {
    311                     SkPoint* quadPts = choppedPts + i * 2;
    312                     m.mapPoints(devPts, quadPts, 3);
    313                     bounds.setBounds(devPts, 3);
    314                     bounds.outset(SK_Scalar1, SK_Scalar1);
    315                     bounds.roundOut(&ibounds);
    316 
    317                     if (SkIRect::Intersects(devClipBounds, ibounds)) {
    318                         int subdiv = num_quad_subdivs(devPts);
    319                         SkASSERT(subdiv >= -1);
    320                         if (-1 == subdiv) {
    321                             SkPoint* pts = lines->push_back_n(4);
    322                             pts[0] = devPts[0];
    323                             pts[1] = devPts[1];
    324                             pts[2] = devPts[1];
    325                             pts[3] = devPts[2];
    326                         } else {
    327                             // when in perspective keep quads in src space
    328                             SkPoint* qPts = persp ? quadPts : devPts;
    329                             SkPoint* pts = quads->push_back_n(3);
    330                             pts[0] = qPts[0];
    331                             pts[1] = qPts[1];
    332                             pts[2] = qPts[2];
    333                             quadSubdivCnts->push_back() = subdiv;
    334                             totalQuadCount += 1 << subdiv;
    335                         }
    336                     }
    337                 }
    338                 break;
    339             }
    340             case SkPath::kCubic_Verb:
    341                 m.mapPoints(devPts, pathPts, 4);
    342                 bounds.setBounds(devPts, 4);
    343                 bounds.outset(SK_Scalar1, SK_Scalar1);
    344                 bounds.roundOut(&ibounds);
    345                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
    346                     PREALLOC_PTARRAY(32) q;
    347                     // we don't need a direction if we aren't constraining the subdivision
    348                     static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
    349                     // We convert cubics to quadratics (for now).
    350                     // In perspective have to do conversion in src space.
    351                     if (persp) {
    352                         SkScalar tolScale =
    353                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
    354                                                              path.getBounds());
    355                         GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q);
    356                     } else {
    357                         GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
    358                     }
    359                     for (int i = 0; i < q.count(); i += 3) {
    360                         SkPoint* qInDevSpace;
    361                         // bounds has to be calculated in device space, but q is
    362                         // in src space when there is perspective.
    363                         if (persp) {
    364                             m.mapPoints(devPts, &q[i], 3);
    365                             bounds.setBounds(devPts, 3);
    366                             qInDevSpace = devPts;
    367                         } else {
    368                             bounds.setBounds(&q[i], 3);
    369                             qInDevSpace = &q[i];
    370                         }
    371                         bounds.outset(SK_Scalar1, SK_Scalar1);
    372                         bounds.roundOut(&ibounds);
    373                         if (SkIRect::Intersects(devClipBounds, ibounds)) {
    374                             int subdiv = num_quad_subdivs(qInDevSpace);
    375                             SkASSERT(subdiv >= -1);
    376                             if (-1 == subdiv) {
    377                                 SkPoint* pts = lines->push_back_n(4);
    378                                 // lines should always be in device coords
    379                                 pts[0] = qInDevSpace[0];
    380                                 pts[1] = qInDevSpace[1];
    381                                 pts[2] = qInDevSpace[1];
    382                                 pts[3] = qInDevSpace[2];
    383                             } else {
    384                                 SkPoint* pts = quads->push_back_n(3);
    385                                 // q is already in src space when there is no
    386                                 // perspective and dev coords otherwise.
    387                                 pts[0] = q[0 + i];
    388                                 pts[1] = q[1 + i];
    389                                 pts[2] = q[2 + i];
    390                                 quadSubdivCnts->push_back() = subdiv;
    391                                 totalQuadCount += 1 << subdiv;
    392                             }
    393                         }
    394                     }
    395                 }
    396                 break;
    397             case SkPath::kClose_Verb:
    398                 break;
    399             case SkPath::kDone_Verb:
    400                 return totalQuadCount;
    401         }
    402     }
    403 }
    404 
    405 struct LineVertex {
    406     SkPoint fPos;
    407     float fCoverage;
    408 };
    409 
    410 struct BezierVertex {
    411     SkPoint fPos;
    412     union {
    413         struct {
    414             SkScalar fK;
    415             SkScalar fL;
    416             SkScalar fM;
    417         } fConic;
    418         SkVector   fQuadCoord;
    419         struct {
    420             SkScalar fBogus[4];
    421         };
    422     };
    423 };
    424 
    425 GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(SkPoint));
    426 
    427 static void intersect_lines(const SkPoint& ptA, const SkVector& normA,
    428                             const SkPoint& ptB, const SkVector& normB,
    429                             SkPoint* result) {
    430 
    431     SkScalar lineAW = -normA.dot(ptA);
    432     SkScalar lineBW = -normB.dot(ptB);
    433 
    434     SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
    435         SkScalarMul(normA.fY, normB.fX);
    436     wInv = SkScalarInvert(wInv);
    437 
    438     result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
    439     result->fX = SkScalarMul(result->fX, wInv);
    440 
    441     result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
    442     result->fY = SkScalarMul(result->fY, wInv);
    443 }
    444 
    445 static void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) {
    446     // this should be in the src space, not dev coords, when we have perspective
    447     GrPathUtils::QuadUVMatrix DevToUV(qpts);
    448     DevToUV.apply<kQuadNumVertices, sizeof(BezierVertex), sizeof(SkPoint)>(verts);
    449 }
    450 
    451 static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
    452                        const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) {
    453     SkASSERT(!toDevice == !toSrc);
    454     // original quad is specified by tri a,b,c
    455     SkPoint a = qpts[0];
    456     SkPoint b = qpts[1];
    457     SkPoint c = qpts[2];
    458 
    459     if (toDevice) {
    460         toDevice->mapPoints(&a, 1);
    461         toDevice->mapPoints(&b, 1);
    462         toDevice->mapPoints(&c, 1);
    463     }
    464     // make a new poly where we replace a and c by a 1-pixel wide edges orthog
    465     // to edges ab and bc:
    466     //
    467     //   before       |        after
    468     //                |              b0
    469     //         b      |
    470     //                |
    471     //                |     a0            c0
    472     // a         c    |        a1       c1
    473     //
    474     // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
    475     // respectively.
    476     BezierVertex& a0 = verts[0];
    477     BezierVertex& a1 = verts[1];
    478     BezierVertex& b0 = verts[2];
    479     BezierVertex& c0 = verts[3];
    480     BezierVertex& c1 = verts[4];
    481 
    482     SkVector ab = b;
    483     ab -= a;
    484     SkVector ac = c;
    485     ac -= a;
    486     SkVector cb = b;
    487     cb -= c;
    488 
    489     // We should have already handled degenerates
    490     SkASSERT(ab.length() > 0 && cb.length() > 0);
    491 
    492     ab.normalize();
    493     SkVector abN;
    494     abN.setOrthog(ab, SkVector::kLeft_Side);
    495     if (abN.dot(ac) > 0) {
    496         abN.negate();
    497     }
    498 
    499     cb.normalize();
    500     SkVector cbN;
    501     cbN.setOrthog(cb, SkVector::kLeft_Side);
    502     if (cbN.dot(ac) < 0) {
    503         cbN.negate();
    504     }
    505 
    506     a0.fPos = a;
    507     a0.fPos += abN;
    508     a1.fPos = a;
    509     a1.fPos -= abN;
    510 
    511     c0.fPos = c;
    512     c0.fPos += cbN;
    513     c1.fPos = c;
    514     c1.fPos -= cbN;
    515 
    516     intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
    517 
    518     if (toSrc) {
    519         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVertices);
    520     }
    521 }
    522 
    523 // Equations based off of Loop-Blinn Quadratic GPU Rendering
    524 // Input Parametric:
    525 // P(t) = (P0*(1-t)^2 + 2*w*P1*t*(1-t) + P2*t^2) / (1-t)^2 + 2*w*t*(1-t) + t^2)
    526 // Output Implicit:
    527 // f(x, y, w) = f(P) = K^2 - LM
    528 // K = dot(k, P), L = dot(l, P), M = dot(m, P)
    529 // k, l, m are calculated in function GrPathUtils::getConicKLM
    530 static void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kQuadNumVertices],
    531                              const SkScalar weight) {
    532     SkScalar klm[9];
    533 
    534     GrPathUtils::getConicKLM(p, weight, klm);
    535 
    536     for (int i = 0; i < kQuadNumVertices; ++i) {
    537         const SkPoint pnt = verts[i].fPos;
    538         verts[i].fConic.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2];
    539         verts[i].fConic.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5];
    540         verts[i].fConic.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8];
    541     }
    542 }
    543 
    544 static void add_conics(const SkPoint p[3],
    545                        const SkScalar weight,
    546                        const SkMatrix* toDevice,
    547                        const SkMatrix* toSrc,
    548                        BezierVertex** vert) {
    549     bloat_quad(p, toDevice, toSrc, *vert);
    550     set_conic_coeffs(p, *vert, weight);
    551     *vert += kQuadNumVertices;
    552 }
    553 
    554 static void add_quads(const SkPoint p[3],
    555                       int subdiv,
    556                       const SkMatrix* toDevice,
    557                       const SkMatrix* toSrc,
    558                       BezierVertex** vert) {
    559     SkASSERT(subdiv >= 0);
    560     if (subdiv) {
    561         SkPoint newP[5];
    562         SkChopQuadAtHalf(p, newP);
    563         add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
    564         add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
    565     } else {
    566         bloat_quad(p, toDevice, toSrc, *vert);
    567         set_uv_quad(p, *vert);
    568         *vert += kQuadNumVertices;
    569     }
    570 }
    571 
    572 static void add_line(const SkPoint p[2],
    573                      const SkMatrix* toSrc,
    574                      uint8_t coverage,
    575                      LineVertex** vert) {
    576     const SkPoint& a = p[0];
    577     const SkPoint& b = p[1];
    578 
    579     SkVector ortho, vec = b;
    580     vec -= a;
    581 
    582     if (vec.setLength(SK_ScalarHalf)) {
    583         // Create a vector orthogonal to 'vec' and of unit length
    584         ortho.fX = 2.0f * vec.fY;
    585         ortho.fY = -2.0f * vec.fX;
    586 
    587         float floatCoverage = GrNormalizeByteToFloat(coverage);
    588 
    589         (*vert)[0].fPos = a;
    590         (*vert)[0].fCoverage = floatCoverage;
    591         (*vert)[1].fPos = b;
    592         (*vert)[1].fCoverage = floatCoverage;
    593         (*vert)[2].fPos = a - vec + ortho;
    594         (*vert)[2].fCoverage = 0;
    595         (*vert)[3].fPos = b + vec + ortho;
    596         (*vert)[3].fCoverage = 0;
    597         (*vert)[4].fPos = a - vec - ortho;
    598         (*vert)[4].fCoverage = 0;
    599         (*vert)[5].fPos = b + vec - ortho;
    600         (*vert)[5].fCoverage = 0;
    601 
    602         if (toSrc) {
    603             toSrc->mapPointsWithStride(&(*vert)->fPos,
    604                                        sizeof(LineVertex),
    605                                        kLineSegNumVertices);
    606         }
    607     } else {
    608         // just make it degenerate and likely offscreen
    609         for (int i = 0; i < kLineSegNumVertices; ++i) {
    610             (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax);
    611         }
    612     }
    613 
    614     *vert += kLineSegNumVertices;
    615 }
    616 
    617 ///////////////////////////////////////////////////////////////////////////////
    618 
    619 bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target,
    620                                            const GrPipelineBuilder* pipelineBuilder,
    621                                            const SkMatrix& viewMatrix,
    622                                            const SkPath& path,
    623                                            const GrStrokeInfo& stroke,
    624                                            bool antiAlias) const {
    625     if (!antiAlias) {
    626         return false;
    627     }
    628 
    629     if (!IsStrokeHairlineOrEquivalent(stroke, viewMatrix, NULL)) {
    630         return false;
    631     }
    632 
    633     if (SkPath::kLine_SegmentMask == path.getSegmentMasks() ||
    634         target->caps()->shaderCaps()->shaderDerivativeSupport()) {
    635         return true;
    636     }
    637     return false;
    638 }
    639 
    640 template <class VertexType>
    641 bool check_bounds(const SkMatrix& viewMatrix, const SkRect& devBounds, void* vertices, int vCount)
    642 {
    643     SkRect tolDevBounds = devBounds;
    644     // The bounds ought to be tight, but in perspective the below code runs the verts
    645     // through the view matrix to get back to dev coords, which can introduce imprecision.
    646     if (viewMatrix.hasPerspective()) {
    647         tolDevBounds.outset(SK_Scalar1 / 1000, SK_Scalar1 / 1000);
    648     } else {
    649         // Non-persp matrices cause this path renderer to draw in device space.
    650         SkASSERT(viewMatrix.isIdentity());
    651     }
    652     SkRect actualBounds;
    653 
    654     VertexType* verts = reinterpret_cast<VertexType*>(vertices);
    655     bool first = true;
    656     for (int i = 0; i < vCount; ++i) {
    657         SkPoint pos = verts[i].fPos;
    658         // This is a hack to workaround the fact that we move some degenerate segments offscreen.
    659         if (SK_ScalarMax == pos.fX) {
    660             continue;
    661         }
    662         viewMatrix.mapPoints(&pos, 1);
    663         if (first) {
    664             actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY);
    665             first = false;
    666         } else {
    667             actualBounds.growToInclude(pos.fX, pos.fY);
    668         }
    669     }
    670     if (!first) {
    671         return tolDevBounds.contains(actualBounds);
    672     }
    673 
    674     return true;
    675 }
    676 
    677 class AAHairlineBatch : public GrBatch {
    678 public:
    679     struct Geometry {
    680         GrColor fColor;
    681         uint8_t fCoverage;
    682         SkMatrix fViewMatrix;
    683         SkPath fPath;
    684         SkIRect fDevClipBounds;
    685     };
    686 
    687     static GrBatch* Create(const Geometry& geometry) {
    688         return SkNEW_ARGS(AAHairlineBatch, (geometry));
    689     }
    690 
    691     const char* name() const override { return "AAHairlineBatch"; }
    692 
    693     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
    694         // When this is called on a batch, there is only one geometry bundle
    695         out->setKnownFourComponents(fGeoData[0].fColor);
    696     }
    697     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
    698         out->setUnknownSingleComponent();
    699     }
    700 
    701     void initBatchTracker(const GrPipelineInfo& init) override {
    702         // Handle any color overrides
    703         if (init.fColorIgnored) {
    704             fGeoData[0].fColor = GrColor_ILLEGAL;
    705         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
    706             fGeoData[0].fColor = init.fOverrideColor;
    707         }
    708 
    709         // setup batch properties
    710         fBatch.fColorIgnored = init.fColorIgnored;
    711         fBatch.fColor = fGeoData[0].fColor;
    712         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
    713         fBatch.fCoverageIgnored = init.fCoverageIgnored;
    714         fBatch.fCoverage = fGeoData[0].fCoverage;
    715     }
    716 
    717     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override;
    718 
    719     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    720 
    721 private:
    722     typedef SkTArray<SkPoint, true> PtArray;
    723     typedef SkTArray<int, true> IntArray;
    724     typedef SkTArray<float, true> FloatArray;
    725 
    726     AAHairlineBatch(const Geometry& geometry) {
    727         this->initClassID<AAHairlineBatch>();
    728         fGeoData.push_back(geometry);
    729 
    730         // compute bounds
    731         fBounds = geometry.fPath.getBounds();
    732         geometry.fViewMatrix.mapRect(&fBounds);
    733 
    734         // This is b.c. hairlines are notionally infinitely thin so without expansion
    735         // two overlapping lines could be reordered even though they hit the same pixels.
    736         fBounds.outset(0.5f, 0.5f);
    737     }
    738 
    739     bool onCombineIfPossible(GrBatch* t) override {
    740         AAHairlineBatch* that = t->cast<AAHairlineBatch>();
    741 
    742         if (this->viewMatrix().hasPerspective() != that->viewMatrix().hasPerspective()) {
    743             return false;
    744         }
    745 
    746         // We go to identity if we don't have perspective
    747         if (this->viewMatrix().hasPerspective() &&
    748             !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    749             return false;
    750         }
    751 
    752         // TODO we can actually batch hairlines if they are the same color in a kind of bulk method
    753         // but we haven't implemented this yet
    754         // TODO investigate going to vertex color and coverage?
    755         if (this->coverage() != that->coverage()) {
    756             return false;
    757         }
    758 
    759         if (this->color() != that->color()) {
    760             return false;
    761         }
    762 
    763         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    764         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    765             return false;
    766         }
    767 
    768         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    769         this->joinBounds(that->bounds());
    770         return true;
    771     }
    772 
    773     GrColor color() const { return fBatch.fColor; }
    774     uint8_t coverage() const { return fBatch.fCoverage; }
    775     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    776     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    777 
    778     struct BatchTracker {
    779         GrColor fColor;
    780         uint8_t fCoverage;
    781         SkRect fDevBounds;
    782         bool fUsesLocalCoords;
    783         bool fColorIgnored;
    784         bool fCoverageIgnored;
    785     };
    786 
    787     BatchTracker fBatch;
    788     SkSTArray<1, Geometry, true> fGeoData;
    789 };
    790 
    791 void AAHairlineBatch::generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
    792     // Setup the viewmatrix and localmatrix for the GrGeometryProcessor.
    793     SkMatrix invert;
    794     if (!this->viewMatrix().invert(&invert)) {
    795         return;
    796     }
    797 
    798     // we will transform to identity space if the viewmatrix does not have perspective
    799     bool hasPerspective = this->viewMatrix().hasPerspective();
    800     const SkMatrix* geometryProcessorViewM = &SkMatrix::I();
    801     const SkMatrix* geometryProcessorLocalM = &invert;
    802     const SkMatrix* toDevice = NULL;
    803     const SkMatrix* toSrc = NULL;
    804     if (hasPerspective) {
    805         geometryProcessorViewM = &this->viewMatrix();
    806         geometryProcessorLocalM = &SkMatrix::I();
    807         toDevice = &this->viewMatrix();
    808         toSrc = &invert;
    809     }
    810 
    811     // Setup geometry processors for worst case
    812     uint32_t gpFlags = GrDefaultGeoProcFactory::kPosition_GPType |
    813                        GrDefaultGeoProcFactory::kCoverage_GPType;
    814 
    815     SkAutoTUnref<const GrGeometryProcessor> lineGP(
    816             GrDefaultGeoProcFactory::Create(gpFlags,
    817                                             this->color(),
    818                                             *geometryProcessorViewM,
    819                                             *geometryProcessorLocalM,
    820                                             this->coverage()));
    821 
    822     SkAutoTUnref<const GrGeometryProcessor> quadGP(
    823             GrQuadEffect::Create(this->color(),
    824                                  *geometryProcessorViewM,
    825                                  kHairlineAA_GrProcessorEdgeType,
    826                                  batchTarget->caps(),
    827                                  *geometryProcessorLocalM,
    828                                  this->coverage()));
    829 
    830     SkAutoTUnref<const GrGeometryProcessor> conicGP(
    831             GrConicEffect::Create(this->color(),
    832                                   *geometryProcessorViewM,
    833                                   kHairlineAA_GrProcessorEdgeType,
    834                                   batchTarget->caps(),
    835                                   *geometryProcessorLocalM,
    836                                   this->coverage()));
    837 
    838     // This is hand inlined for maximum performance.
    839     PREALLOC_PTARRAY(128) lines;
    840     PREALLOC_PTARRAY(128) quads;
    841     PREALLOC_PTARRAY(128) conics;
    842     IntArray qSubdivs;
    843     FloatArray cWeights;
    844     int quadCount = 0;
    845 
    846     int instanceCount = fGeoData.count();
    847     for (int i = 0; i < instanceCount; i++) {
    848         const Geometry& args = fGeoData[i];
    849         quadCount += gather_lines_and_quads(args.fPath, args.fViewMatrix, args.fDevClipBounds,
    850                                             &lines, &quads, &conics, &qSubdivs, &cWeights);
    851     }
    852 
    853     int lineCount = lines.count() / 2;
    854     int conicCount = conics.count() / 3;
    855 
    856     // do lines first
    857     if (lineCount) {
    858         SkAutoTUnref<const GrIndexBuffer> linesIndexBuffer(
    859             ref_lines_index_buffer(batchTarget->resourceProvider()));
    860         batchTarget->initDraw(lineGP, pipeline);
    861 
    862         // TODO remove this when batch is everywhere
    863         GrPipelineInfo init;
    864         init.fColorIgnored = fBatch.fColorIgnored;
    865         init.fOverrideColor = GrColor_ILLEGAL;
    866         init.fCoverageIgnored = fBatch.fCoverageIgnored;
    867         init.fUsesLocalCoords = this->usesLocalCoords();
    868         lineGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
    869 
    870         const GrVertexBuffer* vertexBuffer;
    871         int firstVertex;
    872 
    873         size_t vertexStride = lineGP->getVertexStride();
    874         int vertexCount = kLineSegNumVertices * lineCount;
    875         LineVertex* verts = reinterpret_cast<LineVertex*>(
    876             batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex));
    877 
    878         if (!verts|| !linesIndexBuffer) {
    879             SkDebugf("Could not allocate vertices\n");
    880             return;
    881         }
    882 
    883         SkASSERT(lineGP->getVertexStride() == sizeof(LineVertex));
    884 
    885         for (int i = 0; i < lineCount; ++i) {
    886             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
    887         }
    888 
    889         {
    890             GrVertices vertices;
    891             vertices.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, linesIndexBuffer,
    892                                    firstVertex, kLineSegNumVertices, kIdxsPerLineSeg, lineCount,
    893                                    kLineSegsNumInIdxBuffer);
    894             batchTarget->draw(vertices);
    895         }
    896     }
    897 
    898     if (quadCount || conicCount) {
    899         const GrVertexBuffer* vertexBuffer;
    900         int firstVertex;
    901 
    902         SkAutoTUnref<const GrIndexBuffer> quadsIndexBuffer(
    903             ref_quads_index_buffer(batchTarget->resourceProvider()));
    904 
    905         size_t vertexStride = sizeof(BezierVertex);
    906         int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount;
    907         void *vertices = batchTarget->makeVertSpace(vertexStride, vertexCount,
    908                                                     &vertexBuffer, &firstVertex);
    909 
    910         if (!vertices || !quadsIndexBuffer) {
    911             SkDebugf("Could not allocate vertices\n");
    912             return;
    913         }
    914 
    915         // Setup vertices
    916         BezierVertex* verts = reinterpret_cast<BezierVertex*>(vertices);
    917 
    918         int unsubdivQuadCnt = quads.count() / 3;
    919         for (int i = 0; i < unsubdivQuadCnt; ++i) {
    920             SkASSERT(qSubdivs[i] >= 0);
    921             add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
    922         }
    923 
    924         // Start Conics
    925         for (int i = 0; i < conicCount; ++i) {
    926             add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts);
    927         }
    928 
    929         if (quadCount > 0) {
    930             batchTarget->initDraw(quadGP, pipeline);
    931 
    932             // TODO remove this when batch is everywhere
    933             GrPipelineInfo init;
    934             init.fColorIgnored = fBatch.fColorIgnored;
    935             init.fOverrideColor = GrColor_ILLEGAL;
    936             init.fCoverageIgnored = fBatch.fCoverageIgnored;
    937             init.fUsesLocalCoords = this->usesLocalCoords();
    938             quadGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
    939 
    940             {
    941                 GrVertices verts;
    942                 verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
    943                                     firstVertex, kQuadNumVertices, kIdxsPerQuad, quadCount,
    944                                     kQuadsNumInIdxBuffer);
    945                 batchTarget->draw(verts);
    946                 firstVertex += quadCount * kQuadNumVertices;
    947            }
    948         }
    949 
    950         if (conicCount > 0) {
    951             batchTarget->initDraw(conicGP, pipeline);
    952 
    953             // TODO remove this when batch is everywhere
    954             GrPipelineInfo init;
    955             init.fColorIgnored = fBatch.fColorIgnored;
    956             init.fOverrideColor = GrColor_ILLEGAL;
    957             init.fCoverageIgnored = fBatch.fCoverageIgnored;
    958             init.fUsesLocalCoords = this->usesLocalCoords();
    959             conicGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
    960 
    961             {
    962                 GrVertices verts;
    963                 verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
    964                                     firstVertex, kQuadNumVertices, kIdxsPerQuad, conicCount,
    965                                     kQuadsNumInIdxBuffer);
    966                 batchTarget->draw(verts);
    967             }
    968         }
    969     }
    970 }
    971 
    972 static GrBatch* create_hairline_batch(GrColor color,
    973                                       const SkMatrix& viewMatrix,
    974                                       const SkPath& path,
    975                                       const GrStrokeInfo& stroke,
    976                                       const SkIRect& devClipBounds) {
    977     SkScalar hairlineCoverage;
    978     uint8_t newCoverage = 0xff;
    979     if (GrPathRenderer::IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) {
    980         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
    981     }
    982 
    983     AAHairlineBatch::Geometry geometry;
    984     geometry.fColor = color;
    985     geometry.fCoverage = newCoverage;
    986     geometry.fViewMatrix = viewMatrix;
    987     geometry.fPath = path;
    988     geometry.fDevClipBounds = devClipBounds;
    989 
    990     return AAHairlineBatch::Create(geometry);
    991 }
    992 
    993 bool GrAAHairLinePathRenderer::onDrawPath(GrDrawTarget* target,
    994                                           GrPipelineBuilder* pipelineBuilder,
    995                                           GrColor color,
    996                                           const SkMatrix& viewMatrix,
    997                                           const SkPath& path,
    998                                           const GrStrokeInfo& stroke,
    999                                           bool) {
   1000     SkIRect devClipBounds;
   1001     pipelineBuilder->clip().getConservativeBounds(pipelineBuilder->getRenderTarget(),
   1002                                                   &devClipBounds);
   1003 
   1004     SkAutoTUnref<GrBatch> batch(create_hairline_batch(color, viewMatrix, path, stroke,
   1005                                                       devClipBounds));
   1006     target->drawBatch(pipelineBuilder, batch);
   1007 
   1008     return true;
   1009 }
   1010 
   1011 ///////////////////////////////////////////////////////////////////////////////////////////////////
   1012 
   1013 #ifdef GR_TEST_UTILS
   1014 
   1015 BATCH_TEST_DEFINE(AAHairlineBatch) {
   1016     GrColor color = GrRandomColor(random);
   1017     SkMatrix viewMatrix = GrTest::TestMatrix(random);
   1018     GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
   1019     SkPath path = GrTest::TestPath(random);
   1020     SkIRect devClipBounds;
   1021     devClipBounds.setEmpty();
   1022     return create_hairline_batch(color, viewMatrix, path, stroke, devClipBounds);
   1023 }
   1024 
   1025 #endif
   1026