• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "GrAAHairLinePathRenderer.h"
10 
11 #include "GrContext.h"
12 #include "GrDrawState.h"
13 #include "GrGpu.h"
14 #include "GrIndexBuffer.h"
15 #include "GrPathUtils.h"
16 #include "SkGeometry.h"
17 #include "SkTemplates.h"
18 
19 namespace {
20 // quadratics are rendered as 5-sided polys in order to bound the
21 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
22 // bloat_quad.
23 static const int kVertsPerQuad = 5;
24 static const int kIdxsPerQuad = 9;
25 
26 static const int kVertsPerLineSeg = 4;
27 static const int kIdxsPerLineSeg = 6;
28 
29 static const int kNumQuadsInIdxBuffer = 256;
30 static const size_t kQuadIdxSBufize = kIdxsPerQuad *
31                                       sizeof(uint16_t) *
32                                       kNumQuadsInIdxBuffer;
33 
push_quad_index_data(GrIndexBuffer * qIdxBuffer)34 bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
35     uint16_t* data = (uint16_t*) qIdxBuffer->lock();
36     bool tempData = NULL == data;
37     if (tempData) {
38         data = new uint16_t[kNumQuadsInIdxBuffer * kIdxsPerQuad];
39     }
40     for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
41 
42         // Each quadratic is rendered as a five sided polygon. This poly bounds
43         // the quadratic's bounding triangle but has been expanded so that the
44         // 1-pixel wide area around the curve is inside the poly.
45         // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
46         // that is rendered would look like this:
47         //              b0
48         //              b
49         //
50         //     a0              c0
51         //      a            c
52         //       a1       c1
53         // Each is drawn as three triagnles specified by these 9 indices:
54         int baseIdx = i * kIdxsPerQuad;
55         uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
56         data[0 + baseIdx] = baseVert + 0; // a0
57         data[1 + baseIdx] = baseVert + 1; // a1
58         data[2 + baseIdx] = baseVert + 2; // b0
59         data[3 + baseIdx] = baseVert + 2; // b0
60         data[4 + baseIdx] = baseVert + 4; // c1
61         data[5 + baseIdx] = baseVert + 3; // c0
62         data[6 + baseIdx] = baseVert + 1; // a1
63         data[7 + baseIdx] = baseVert + 4; // c1
64         data[8 + baseIdx] = baseVert + 2; // b0
65     }
66     if (tempData) {
67         bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
68         delete[] data;
69         return ret;
70     } else {
71         qIdxBuffer->unlock();
72         return true;
73     }
74 }
75 }
76 
Create(GrContext * context)77 GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
78     const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
79     if (NULL == lIdxBuffer) {
80         return NULL;
81     }
82     GrGpu* gpu = context->getGpu();
83     GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
84     SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
85     if (NULL == qIdxBuf ||
86         !push_quad_index_data(qIdxBuf)) {
87         return NULL;
88     }
89     return new GrAAHairLinePathRenderer(context,
90                                         lIdxBuffer,
91                                         qIdxBuf);
92 }
93 
GrAAHairLinePathRenderer(const GrContext * context,const GrIndexBuffer * linesIndexBuffer,const GrIndexBuffer * quadsIndexBuffer)94 GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
95                                         const GrContext* context,
96                                         const GrIndexBuffer* linesIndexBuffer,
97                                         const GrIndexBuffer* quadsIndexBuffer) {
98     fLinesIndexBuffer = linesIndexBuffer;
99     linesIndexBuffer->ref();
100     fQuadsIndexBuffer = quadsIndexBuffer;
101     quadsIndexBuffer->ref();
102 }
103 
~GrAAHairLinePathRenderer()104 GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
105     fLinesIndexBuffer->unref();
106     fQuadsIndexBuffer->unref();
107 }
108 
109 namespace {
110 
111 typedef SkTArray<SkPoint, true> PtArray;
112 #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
113 typedef SkTArray<int, true> IntArray;
114 
115 // Takes 178th time of logf on Z600 / VC2010
get_float_exp(float x)116 int get_float_exp(float x) {
117     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
118 #if GR_DEBUG
119     static bool tested;
120     if (!tested) {
121         tested = true;
122         GrAssert(get_float_exp(0.25f) == -2);
123         GrAssert(get_float_exp(0.3f) == -2);
124         GrAssert(get_float_exp(0.5f) == -1);
125         GrAssert(get_float_exp(1.f) == 0);
126         GrAssert(get_float_exp(2.f) == 1);
127         GrAssert(get_float_exp(2.5f) == 1);
128         GrAssert(get_float_exp(8.f) == 3);
129         GrAssert(get_float_exp(100.f) == 6);
130         GrAssert(get_float_exp(1000.f) == 9);
131         GrAssert(get_float_exp(1024.f) == 10);
132         GrAssert(get_float_exp(3000000.f) == 21);
133     }
134 #endif
135     const int* iptr = (const int*)&x;
136     return (((*iptr) & 0x7f800000) >> 23) - 127;
137 }
138 
139 // we subdivide the quads to avoid huge overfill
140 // if it returns -1 then should be drawn as lines
num_quad_subdivs(const SkPoint p[3])141 int num_quad_subdivs(const SkPoint p[3]) {
142     static const SkScalar gDegenerateToLineTol = SK_Scalar1;
143     static const SkScalar gDegenerateToLineTolSqd =
144         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
145 
146     if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
147         p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
148         return -1;
149     }
150 
151     GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
152     if (dsqd < gDegenerateToLineTolSqd) {
153         return -1;
154     }
155 
156     if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
157         return -1;
158     }
159 
160     static const int kMaxSub = 4;
161     // tolerance of triangle height in pixels
162     // tuned on windows  Quadro FX 380 / Z600
163     // trade off of fill vs cpu time on verts
164     // maybe different when do this using gpu (geo or tess shaders)
165     static const SkScalar gSubdivTol = 175 * SK_Scalar1;
166 
167     if (dsqd <= gSubdivTol*gSubdivTol) {
168         return 0;
169     } else {
170         // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
171         // = log4(d*d/tol*tol)/2
172         // = log2(d*d/tol*tol)
173 
174 #ifdef SK_SCALAR_IS_FLOAT
175         // +1 since we're ignoring the mantissa contribution.
176         int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
177         log = GrMin(GrMax(0, log), kMaxSub);
178         return log;
179 #else
180         SkScalar log = SkScalarLog(SkScalarDiv(dsqd,gSubdivTol*gSubdivTol));
181         static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
182         log = SkScalarMul(log, conv);
183         return  GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
184 #endif
185     }
186 }
187 
188 /**
189  * Generates the lines and quads to be rendered. Lines are always recorded in
190  * device space. We will do a device space bloat to account for the 1pixel
191  * thickness.
192  * Quads are recorded in device space unless m contains
193  * perspective, then in they are in src space. We do this because we will
194  * subdivide large quads to reduce over-fill. This subdivision has to be
195  * performed before applying the perspective matrix.
196  */
generate_lines_and_quads(const SkPath & path,const SkMatrix & m,const SkVector & translate,GrIRect clip,PtArray * lines,PtArray * quads,IntArray * quadSubdivCnts)197 int generate_lines_and_quads(const SkPath& path,
198                              const SkMatrix& m,
199                              const SkVector& translate,
200                              GrIRect clip,
201                              PtArray* lines,
202                              PtArray* quads,
203                              IntArray* quadSubdivCnts) {
204     SkPath::Iter iter(path, false);
205 
206     int totalQuadCount = 0;
207     GrRect bounds;
208     GrIRect ibounds;
209 
210     bool persp = m.hasPerspective();
211 
212     for (;;) {
213         GrPoint pts[4];
214         GrPoint devPts[4];
215         GrPathCmd cmd = (GrPathCmd)iter.next(pts);
216         switch (cmd) {
217             case kMove_PathCmd:
218                 break;
219             case kLine_PathCmd:
220                 SkPoint::Offset(pts, 2, translate);
221                 m.mapPoints(devPts, pts, 2);
222                 bounds.setBounds(devPts, 2);
223                 bounds.outset(SK_Scalar1, SK_Scalar1);
224                 bounds.roundOut(&ibounds);
225                 if (SkIRect::Intersects(clip, ibounds)) {
226                     SkPoint* pts = lines->push_back_n(2);
227                     pts[0] = devPts[0];
228                     pts[1] = devPts[1];
229                 }
230                 break;
231             case kQuadratic_PathCmd:
232                 SkPoint::Offset(pts, 3, translate);
233                 m.mapPoints(devPts, pts, 3);
234                 bounds.setBounds(devPts, 3);
235                 bounds.outset(SK_Scalar1, SK_Scalar1);
236                 bounds.roundOut(&ibounds);
237                 if (SkIRect::Intersects(clip, ibounds)) {
238                     int subdiv = num_quad_subdivs(devPts);
239                     GrAssert(subdiv >= -1);
240                     if (-1 == subdiv) {
241                         SkPoint* pts = lines->push_back_n(4);
242                         pts[0] = devPts[0];
243                         pts[1] = devPts[1];
244                         pts[2] = devPts[1];
245                         pts[3] = devPts[2];
246                     } else {
247                         // when in perspective keep quads in src space
248                         SkPoint* qPts = persp ? pts : devPts;
249                         SkPoint* pts = quads->push_back_n(3);
250                         pts[0] = qPts[0];
251                         pts[1] = qPts[1];
252                         pts[2] = qPts[2];
253                         quadSubdivCnts->push_back() = subdiv;
254                         totalQuadCount += 1 << subdiv;
255                     }
256                 }
257             break;
258             case kCubic_PathCmd:
259                 SkPoint::Offset(pts, 4, translate);
260                 m.mapPoints(devPts, pts, 4);
261                 bounds.setBounds(devPts, 4);
262                 bounds.outset(SK_Scalar1, SK_Scalar1);
263                 bounds.roundOut(&ibounds);
264                 if (SkIRect::Intersects(clip, ibounds)) {
265                     PREALLOC_PTARRAY(32) q;
266                     // We convert cubics to quadratics (for now).
267                     // In perspective have to do conversion in src space.
268                     if (persp) {
269                         SkScalar tolScale =
270                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
271                                                              path.getBounds());
272                         GrPathUtils::convertCubicToQuads(pts, tolScale, &q);
273                     } else {
274                         GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, &q);
275                     }
276                     for (int i = 0; i < q.count(); i += 3) {
277                         SkPoint* qInDevSpace;
278                         // bounds has to be calculated in device space, but q is
279                         // in src space when there is perspective.
280                         if (persp) {
281                             m.mapPoints(devPts, &q[i], 3);
282                             bounds.setBounds(devPts, 3);
283                             qInDevSpace = devPts;
284                         } else {
285                             bounds.setBounds(&q[i], 3);
286                             qInDevSpace = &q[i];
287                         }
288                         bounds.outset(SK_Scalar1, SK_Scalar1);
289                         bounds.roundOut(&ibounds);
290                         if (SkIRect::Intersects(clip, ibounds)) {
291                             int subdiv = num_quad_subdivs(qInDevSpace);
292                             GrAssert(subdiv >= -1);
293                             if (-1 == subdiv) {
294                                 SkPoint* pts = lines->push_back_n(4);
295                                 // lines should always be in device coords
296                                 pts[0] = qInDevSpace[0];
297                                 pts[1] = qInDevSpace[1];
298                                 pts[2] = qInDevSpace[1];
299                                 pts[3] = qInDevSpace[2];
300                             } else {
301                                 SkPoint* pts = quads->push_back_n(3);
302                                 // q is already in src space when there is no
303                                 // perspective and dev coords otherwise.
304                                 pts[0] = q[0 + i];
305                                 pts[1] = q[1 + i];
306                                 pts[2] = q[2 + i];
307                                 quadSubdivCnts->push_back() = subdiv;
308                                 totalQuadCount += 1 << subdiv;
309                             }
310                         }
311                     }
312                 }
313             break;
314             case kClose_PathCmd:
315                 break;
316             case kEnd_PathCmd:
317                 return totalQuadCount;
318         }
319     }
320 }
321 
322 struct Vertex {
323     GrPoint fPos;
324     union {
325         struct {
326             GrScalar fA;
327             GrScalar fB;
328             GrScalar fC;
329         } fLine;
330         GrVec   fQuadCoord;
331         struct {
332             GrScalar fBogus[4];
333         };
334     };
335 };
336 GR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint));
337 
intersect_lines(const SkPoint & ptA,const SkVector & normA,const SkPoint & ptB,const SkVector & normB,SkPoint * result)338 void intersect_lines(const SkPoint& ptA, const SkVector& normA,
339                      const SkPoint& ptB, const SkVector& normB,
340                      SkPoint* result) {
341 
342     SkScalar lineAW = -normA.dot(ptA);
343     SkScalar lineBW = -normB.dot(ptB);
344 
345     SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
346                     SkScalarMul(normA.fY, normB.fX);
347     wInv = SkScalarInvert(wInv);
348 
349     result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
350     result->fX = SkScalarMul(result->fX, wInv);
351 
352     result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
353     result->fY = SkScalarMul(result->fY, wInv);
354 }
355 
bloat_quad(const SkPoint qpts[3],const GrMatrix * toDevice,const GrMatrix * toSrc,Vertex verts[kVertsPerQuad])356 void bloat_quad(const SkPoint qpts[3], const GrMatrix* toDevice,
357                 const GrMatrix* toSrc, Vertex verts[kVertsPerQuad]) {
358     GrAssert(!toDevice == !toSrc);
359     // original quad is specified by tri a,b,c
360     SkPoint a = qpts[0];
361     SkPoint b = qpts[1];
362     SkPoint c = qpts[2];
363 
364     // this should be in the src space, not dev coords, when we have perspective
365     SkMatrix DevToUV;
366     GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &DevToUV);
367 
368     if (toDevice) {
369         toDevice->mapPoints(&a, 1);
370         toDevice->mapPoints(&b, 1);
371         toDevice->mapPoints(&c, 1);
372     }
373     // make a new poly where we replace a and c by a 1-pixel wide edges orthog
374     // to edges ab and bc:
375     //
376     //   before       |        after
377     //                |              b0
378     //         b      |
379     //                |
380     //                |     a0            c0
381     // a         c    |        a1       c1
382     //
383     // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
384     // respectively.
385     Vertex& a0 = verts[0];
386     Vertex& a1 = verts[1];
387     Vertex& b0 = verts[2];
388     Vertex& c0 = verts[3];
389     Vertex& c1 = verts[4];
390 
391     SkVector ab = b;
392     ab -= a;
393     SkVector ac = c;
394     ac -= a;
395     SkVector cb = b;
396     cb -= c;
397 
398     // We should have already handled degenerates
399     GrAssert(ab.length() > 0 && cb.length() > 0);
400 
401     ab.normalize();
402     SkVector abN;
403     abN.setOrthog(ab, SkVector::kLeft_Side);
404     if (abN.dot(ac) > 0) {
405         abN.negate();
406     }
407 
408     cb.normalize();
409     SkVector cbN;
410     cbN.setOrthog(cb, SkVector::kLeft_Side);
411     if (cbN.dot(ac) < 0) {
412         cbN.negate();
413     }
414 
415     a0.fPos = a;
416     a0.fPos += abN;
417     a1.fPos = a;
418     a1.fPos -= abN;
419 
420     c0.fPos = c;
421     c0.fPos += cbN;
422     c1.fPos = c;
423     c1.fPos -= cbN;
424 
425     intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
426 
427     if (toSrc) {
428         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
429     }
430     DevToUV.mapPointsWithStride(&verts[0].fQuadCoord,
431                                 &verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
432 }
433 
add_quads(const SkPoint p[3],int subdiv,const GrMatrix * toDevice,const GrMatrix * toSrc,Vertex ** vert)434 void add_quads(const SkPoint p[3],
435                int subdiv,
436                const GrMatrix* toDevice,
437                const GrMatrix* toSrc,
438                Vertex** vert) {
439     GrAssert(subdiv >= 0);
440     if (subdiv) {
441         SkPoint newP[5];
442         SkChopQuadAtHalf(p, newP);
443         add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
444         add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
445     } else {
446         bloat_quad(p, toDevice, toSrc, *vert);
447         *vert += kVertsPerQuad;
448     }
449 }
450 
add_line(const SkPoint p[2],int rtHeight,const SkMatrix * toSrc,Vertex ** vert)451 void add_line(const SkPoint p[2],
452               int rtHeight,
453               const SkMatrix* toSrc,
454               Vertex** vert) {
455     const SkPoint& a = p[0];
456     const SkPoint& b = p[1];
457 
458     SkVector orthVec = b;
459     orthVec -= a;
460 
461     if (orthVec.setLength(SK_Scalar1)) {
462         orthVec.setOrthog(orthVec);
463 
464         // the values we pass down to the frag shader
465         // have to be in y-points-up space;
466         SkVector normal;
467         normal.fX = orthVec.fX;
468         normal.fY = -orthVec.fY;
469         SkPoint aYDown;
470         aYDown.fX = a.fX;
471         aYDown.fY = rtHeight - a.fY;
472 
473         SkScalar lineC = -(aYDown.dot(normal));
474         for (int i = 0; i < kVertsPerLineSeg; ++i) {
475             (*vert)[i].fPos = (i < 2) ? a : b;
476             if (0 == i || 3 == i) {
477                 (*vert)[i].fPos -= orthVec;
478             } else {
479                 (*vert)[i].fPos += orthVec;
480             }
481             (*vert)[i].fLine.fA = normal.fX;
482             (*vert)[i].fLine.fB = normal.fY;
483             (*vert)[i].fLine.fC = lineC;
484         }
485         if (NULL != toSrc) {
486             toSrc->mapPointsWithStride(&(*vert)->fPos,
487                                        sizeof(Vertex),
488                                        kVertsPerLineSeg);
489         }
490     } else {
491         // just make it degenerate and likely offscreen
492         (*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax);
493         (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
494         (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
495         (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
496     }
497 
498     *vert += kVertsPerLineSeg;
499 }
500 
501 }
502 
createGeom(const SkPath & path,const GrVec * translate,GrDrawTarget * target,GrDrawState::StageMask stageMask,int * lineCnt,int * quadCnt)503 bool GrAAHairLinePathRenderer::createGeom(const SkPath& path,
504                                           const GrVec* translate,
505                                           GrDrawTarget* target,
506                                           GrDrawState::StageMask stageMask,
507                                           int* lineCnt,
508                                           int* quadCnt) {
509     const GrDrawState& drawState = target->getDrawState();
510     int rtHeight = drawState.getRenderTarget()->height();
511 
512     GrIRect clip;
513     if (target->getClip().hasConservativeBounds()) {
514         GrRect clipRect =  target->getClip().getConservativeBounds();
515         clipRect.roundOut(&clip);
516     } else {
517         clip.setLargest();
518     }
519 
520 
521     GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
522     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
523         if ((1 << s) & stageMask) {
524             layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
525         }
526     }
527 
528     GrMatrix viewM = drawState.getViewMatrix();
529 
530     PREALLOC_PTARRAY(128) lines;
531     PREALLOC_PTARRAY(128) quads;
532     IntArray qSubdivs;
533     static const GrVec gZeroVec = {0, 0};
534     if (NULL == translate) {
535         translate = &gZeroVec;
536     }
537     *quadCnt = generate_lines_and_quads(path, viewM, *translate, clip,
538                                         &lines, &quads, &qSubdivs);
539 
540     *lineCnt = lines.count() / 2;
541     int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt;
542 
543     GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));
544 
545     Vertex* verts;
546     if (!target->reserveVertexSpace(layout, vertCnt, (void**)&verts)) {
547         return false;
548     }
549 
550     const GrMatrix* toDevice = NULL;
551     const GrMatrix* toSrc = NULL;
552     GrMatrix ivm;
553 
554     if (viewM.hasPerspective()) {
555         if (viewM.invert(&ivm)) {
556             toDevice = &viewM;
557             toSrc = &ivm;
558         }
559     }
560 
561     for (int i = 0; i < *lineCnt; ++i) {
562         add_line(&lines[2*i], rtHeight, toSrc, &verts);
563     }
564 
565     int unsubdivQuadCnt = quads.count() / 3;
566     for (int i = 0; i < unsubdivQuadCnt; ++i) {
567         GrAssert(qSubdivs[i] >= 0);
568         add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
569     }
570 
571     return true;
572 }
573 
canDrawPath(const SkPath & path,GrPathFill fill,const GrDrawTarget * target,bool antiAlias) const574 bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
575                                            GrPathFill fill,
576                                            const GrDrawTarget* target,
577                                            bool antiAlias) const {
578     if (fill != kHairLine_PathFill || !antiAlias) {
579         return false;
580     }
581 
582     static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask |
583                                           SkPath::kQuad_SegmentMask;
584     if (!target->getCaps().fShaderDerivativeSupport &&
585         (gReqDerivMask & path.getSegmentMasks())) {
586         return false;
587     }
588     return true;
589 }
590 
onDrawPath(const SkPath & path,GrPathFill fill,const GrVec * translate,GrDrawTarget * target,GrDrawState::StageMask stageMask,bool antiAlias)591 bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
592                                           GrPathFill fill,
593                                           const GrVec* translate,
594                                           GrDrawTarget* target,
595                                           GrDrawState::StageMask stageMask,
596                                           bool antiAlias) {
597 
598     int lineCnt;
599     int quadCnt;
600 
601     if (!this->createGeom(path,
602                           translate,
603                           target,
604                           stageMask,
605                           &lineCnt,
606                           &quadCnt)) {
607         return false;
608     }
609 
610     GrDrawState* drawState = target->drawState();
611 
612     GrDrawTarget::AutoStateRestore asr;
613     if (!drawState->getViewMatrix().hasPerspective()) {
614         asr.set(target);
615         GrMatrix ivm;
616         if (drawState->getViewInverse(&ivm)) {
617             drawState->preConcatSamplerMatrices(stageMask, ivm);
618         }
619         drawState->setViewMatrix(GrMatrix::I());
620     }
621 
622     // TODO: See whether rendering lines as degenerate quads improves perf
623     // when we have a mix
624     target->setIndexSourceToBuffer(fLinesIndexBuffer);
625     int lines = 0;
626     int nBufLines = fLinesIndexBuffer->maxQuads();
627     while (lines < lineCnt) {
628         int n = GrMin(lineCnt - lines, nBufLines);
629         drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType);
630         target->drawIndexed(kTriangles_PrimitiveType,
631                             kVertsPerLineSeg*lines,    // startV
632                             0,                         // startI
633                             kVertsPerLineSeg*n,        // vCount
634                             kIdxsPerLineSeg*n);        // iCount
635         lines += n;
636     }
637 
638     target->setIndexSourceToBuffer(fQuadsIndexBuffer);
639     int quads = 0;
640     while (quads < quadCnt) {
641         int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
642         drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
643         target->drawIndexed(kTriangles_PrimitiveType,
644                             4 * lineCnt + kVertsPerQuad*quads, // startV
645                             0,                                 // startI
646                             kVertsPerQuad*n,                   // vCount
647                             kIdxsPerQuad*n);                   // iCount
648         quads += n;
649     }
650     return true;
651 }
652 
653