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