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 "GrContext.h"
11#include "GrDrawState.h"
12#include "GrDrawTargetCaps.h"
13#include "GrEffect.h"
14#include "GrGpu.h"
15#include "GrIndexBuffer.h"
16#include "GrPathUtils.h"
17#include "GrTBackendEffectFactory.h"
18#include "SkGeometry.h"
19#include "SkStroke.h"
20#include "SkTemplates.h"
21
22#include "gl/GrGLEffect.h"
23#include "gl/GrGLSL.h"
24
25namespace {
26// quadratics are rendered as 5-sided polys in order to bound the
27// AA stroke around the center-curve. See comments in push_quad_index_buffer and
28// bloat_quad. Quadratics and conics share an index buffer
29static const int kVertsPerQuad = 5;
30static const int kIdxsPerQuad = 9;
31
32static const int kVertsPerLineSeg = 6;
33static const int kIdxsPerLineSeg = 12;
34
35static const int kNumQuadsInIdxBuffer = 256;
36static const size_t kQuadIdxSBufize = kIdxsPerQuad *
37                                      sizeof(uint16_t) *
38                                      kNumQuadsInIdxBuffer;
39
40static const int kNumLineSegsInIdxBuffer = 256;
41static const size_t kLineSegIdxSBufize = kIdxsPerLineSeg *
42                                         sizeof(uint16_t) *
43                                         kNumLineSegsInIdxBuffer;
44
45static bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
46    uint16_t* data = (uint16_t*) qIdxBuffer->lock();
47    bool tempData = NULL == data;
48    if (tempData) {
49        data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad);
50    }
51    for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
52
53        // Each quadratic is rendered as a five sided polygon. This poly bounds
54        // the quadratic's bounding triangle but has been expanded so that the
55        // 1-pixel wide area around the curve is inside the poly.
56        // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
57        // that is rendered would look like this:
58        //              b0
59        //              b
60        //
61        //     a0              c0
62        //      a            c
63        //       a1       c1
64        // Each is drawn as three triangles specified by these 9 indices:
65        int baseIdx = i * kIdxsPerQuad;
66        uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
67        data[0 + baseIdx] = baseVert + 0; // a0
68        data[1 + baseIdx] = baseVert + 1; // a1
69        data[2 + baseIdx] = baseVert + 2; // b0
70        data[3 + baseIdx] = baseVert + 2; // b0
71        data[4 + baseIdx] = baseVert + 4; // c1
72        data[5 + baseIdx] = baseVert + 3; // c0
73        data[6 + baseIdx] = baseVert + 1; // a1
74        data[7 + baseIdx] = baseVert + 4; // c1
75        data[8 + baseIdx] = baseVert + 2; // b0
76    }
77    if (tempData) {
78        bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
79        delete[] data;
80        return ret;
81    } else {
82        qIdxBuffer->unlock();
83        return true;
84    }
85}
86
87static bool push_line_index_data(GrIndexBuffer* lIdxBuffer) {
88    uint16_t* data = (uint16_t*) lIdxBuffer->lock();
89    bool tempData = NULL == data;
90    if (tempData) {
91        data = SkNEW_ARRAY(uint16_t, kNumLineSegsInIdxBuffer * kIdxsPerLineSeg);
92    }
93    for (int i = 0; i < kNumLineSegsInIdxBuffer; ++i) {
94        // Each line segment is rendered as two quads, with alpha = 1 along the
95        // spine of the segment, and alpha = 0 along the outer edges, represented
96        // horizontally (i.e., the line equation is t*(p1-p0) + p0)
97        //
98        // p4                  p5
99        // p0                  p1
100        // p2                  p3
101        //
102        // Each is drawn as four triangles specified by these 12 indices:
103        int baseIdx = i * kIdxsPerLineSeg;
104        uint16_t baseVert = (uint16_t)(i * kVertsPerLineSeg);
105        data[0 + baseIdx] = baseVert + 0; // p0
106        data[1 + baseIdx] = baseVert + 1; // p1
107        data[2 + baseIdx] = baseVert + 2; // p2
108
109        data[3 + baseIdx] = baseVert + 2; // p2
110        data[4 + baseIdx] = baseVert + 1; // p1
111        data[5 + baseIdx] = baseVert + 3; // p3
112
113        data[6 + baseIdx] = baseVert + 0; // p0
114        data[7 + baseIdx] = baseVert + 5; // p5
115        data[8 + baseIdx] = baseVert + 1; // p1
116
117        data[9 + baseIdx] = baseVert + 0; // p0
118        data[10+ baseIdx] = baseVert + 4; // p4
119        data[11+ baseIdx] = baseVert + 5; // p5
120    }
121    if (tempData) {
122        bool ret = lIdxBuffer->updateData(data, kLineSegIdxSBufize);
123        delete[] data;
124        return ret;
125    } else {
126        lIdxBuffer->unlock();
127        return true;
128    }
129}
130}
131
132GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
133    GrGpu* gpu = context->getGpu();
134    GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
135    SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
136    if (NULL == qIdxBuf || !push_quad_index_data(qIdxBuf)) {
137        return NULL;
138    }
139    GrIndexBuffer* lIdxBuf = gpu->createIndexBuffer(kLineSegIdxSBufize, false);
140    SkAutoTUnref<GrIndexBuffer> lIdxBuffer(lIdxBuf);
141    if (NULL == lIdxBuf || !push_line_index_data(lIdxBuf)) {
142        return NULL;
143    }
144    return SkNEW_ARGS(GrAAHairLinePathRenderer,
145                      (context, lIdxBuf, qIdxBuf));
146}
147
148GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
149                                        const GrContext* context,
150                                        const GrIndexBuffer* linesIndexBuffer,
151                                        const GrIndexBuffer* quadsIndexBuffer) {
152    fLinesIndexBuffer = linesIndexBuffer;
153    linesIndexBuffer->ref();
154    fQuadsIndexBuffer = quadsIndexBuffer;
155    quadsIndexBuffer->ref();
156}
157
158GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
159    fLinesIndexBuffer->unref();
160    fQuadsIndexBuffer->unref();
161}
162
163namespace {
164
165#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
166
167// Takes 178th time of logf on Z600 / VC2010
168int get_float_exp(float x) {
169    GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
170#if GR_DEBUG
171    static bool tested;
172    if (!tested) {
173        tested = true;
174        GrAssert(get_float_exp(0.25f) == -2);
175        GrAssert(get_float_exp(0.3f) == -2);
176        GrAssert(get_float_exp(0.5f) == -1);
177        GrAssert(get_float_exp(1.f) == 0);
178        GrAssert(get_float_exp(2.f) == 1);
179        GrAssert(get_float_exp(2.5f) == 1);
180        GrAssert(get_float_exp(8.f) == 3);
181        GrAssert(get_float_exp(100.f) == 6);
182        GrAssert(get_float_exp(1000.f) == 9);
183        GrAssert(get_float_exp(1024.f) == 10);
184        GrAssert(get_float_exp(3000000.f) == 21);
185    }
186#endif
187    const int* iptr = (const int*)&x;
188    return (((*iptr) & 0x7f800000) >> 23) - 127;
189}
190
191// Uses the max curvature function for quads to estimate
192// where to chop the conic. If the max curvature is not
193// found along the curve segment it will return 1 and
194// dst[0] is the original conic. If it returns 2 the dst[0]
195// and dst[1] are the two new conics.
196int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
197    SkScalar t = SkFindQuadMaxCurvature(src);
198    if (t == 0) {
199        if (dst) {
200            dst[0].set(src, weight);
201        }
202        return 1;
203    } else {
204        if (dst) {
205            SkConic conic;
206            conic.set(src, weight);
207            conic.chopAt(t, dst);
208        }
209        return 2;
210    }
211}
212
213// Calls split_conic on the entire conic and then once more on each subsection.
214// Most cases will result in either 1 conic (chop point is not within t range)
215// or 3 points (split once and then one subsection is split again).
216int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
217    SkConic dstTemp[2];
218    int conicCnt = split_conic(src, dstTemp, weight);
219    if (2 == conicCnt) {
220        int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
221        conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
222    } else {
223        dst[0] = dstTemp[0];
224    }
225    return conicCnt;
226}
227
228// returns 0 if quad/conic is degen or close to it
229// in this case approx the path with lines
230// otherwise returns 1
231int is_degen_quad_or_conic(const SkPoint p[3]) {
232    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
233    static const SkScalar gDegenerateToLineTolSqd =
234        SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
235
236    if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
237        p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
238        return 1;
239    }
240
241    SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
242    if (dsqd < gDegenerateToLineTolSqd) {
243        return 1;
244    }
245
246    if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
247        return 1;
248    }
249    return 0;
250}
251
252// we subdivide the quads to avoid huge overfill
253// if it returns -1 then should be drawn as lines
254int num_quad_subdivs(const SkPoint p[3]) {
255    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
256    static const SkScalar gDegenerateToLineTolSqd =
257        SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
258
259    if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
260        p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
261        return -1;
262    }
263
264    SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
265    if (dsqd < gDegenerateToLineTolSqd) {
266        return -1;
267    }
268
269    if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
270        return -1;
271    }
272
273    // tolerance of triangle height in pixels
274    // tuned on windows  Quadro FX 380 / Z600
275    // trade off of fill vs cpu time on verts
276    // maybe different when do this using gpu (geo or tess shaders)
277    static const SkScalar gSubdivTol = 175 * SK_Scalar1;
278
279    if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
280        return 0;
281    } else {
282        static const int kMaxSub = 4;
283        // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
284        // = log4(d*d/tol*tol)/2
285        // = log2(d*d/tol*tol)
286
287#ifdef SK_SCALAR_IS_FLOAT
288        // +1 since we're ignoring the mantissa contribution.
289        int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
290        log = GrMin(GrMax(0, log), kMaxSub);
291        return log;
292#else
293        SkScalar log = SkScalarLog(
294                          SkScalarDiv(dsqd,
295                                      SkScalarMul(gSubdivTol, gSubdivTol)));
296        static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
297        log = SkScalarMul(log, conv);
298        return  GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
299#endif
300    }
301}
302
303/**
304 * Generates the lines and quads to be rendered. Lines are always recorded in
305 * device space. We will do a device space bloat to account for the 1pixel
306 * thickness.
307 * Quads are recorded in device space unless m contains
308 * perspective, then in they are in src space. We do this because we will
309 * subdivide large quads to reduce over-fill. This subdivision has to be
310 * performed before applying the perspective matrix.
311 */
312int generate_lines_and_quads(const SkPath& path,
313                             const SkMatrix& m,
314                             const SkIRect& devClipBounds,
315                             GrAAHairLinePathRenderer::PtArray* lines,
316                             GrAAHairLinePathRenderer::PtArray* quads,
317                             GrAAHairLinePathRenderer::PtArray* conics,
318                             GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
319                             GrAAHairLinePathRenderer::FloatArray* conicWeights) {
320    SkPath::Iter iter(path, false);
321
322    int totalQuadCount = 0;
323    SkRect bounds;
324    SkIRect ibounds;
325
326    bool persp = m.hasPerspective();
327
328    for (;;) {
329        GrPoint pathPts[4];
330        GrPoint devPts[4];
331        SkPath::Verb verb = iter.next(pathPts);
332        switch (verb) {
333            case SkPath::kConic_Verb: {
334                SkConic dst[4];
335                // We chop the conics to create tighter clipping to hide error
336                // that appears near max curvature of very thin conics. Thin
337                // hyperbolas with high weight still show error.
338                int conicCnt = chop_conic(pathPts, dst, iter.conicWeight());
339                for (int i = 0; i < conicCnt; ++i) {
340                    SkPoint* chopPnts = dst[i].fPts;
341                    m.mapPoints(devPts, chopPnts, 3);
342                    bounds.setBounds(devPts, 3);
343                    bounds.outset(SK_Scalar1, SK_Scalar1);
344                    bounds.roundOut(&ibounds);
345                    if (SkIRect::Intersects(devClipBounds, ibounds)) {
346                        if (is_degen_quad_or_conic(devPts)) {
347                            SkPoint* pts = lines->push_back_n(4);
348                            pts[0] = devPts[0];
349                            pts[1] = devPts[1];
350                            pts[2] = devPts[1];
351                            pts[3] = devPts[2];
352                        } else {
353                            // when in perspective keep conics in src space
354                            SkPoint* cPts = persp ? chopPnts : devPts;
355                            SkPoint* pts = conics->push_back_n(3);
356                            pts[0] = cPts[0];
357                            pts[1] = cPts[1];
358                            pts[2] = cPts[2];
359                            conicWeights->push_back() = dst[i].fW;
360                        }
361                    }
362                }
363                break;
364            }
365            case SkPath::kMove_Verb:
366                break;
367            case SkPath::kLine_Verb:
368                m.mapPoints(devPts, pathPts, 2);
369                bounds.setBounds(devPts, 2);
370                bounds.outset(SK_Scalar1, SK_Scalar1);
371                bounds.roundOut(&ibounds);
372                if (SkIRect::Intersects(devClipBounds, ibounds)) {
373                    SkPoint* pts = lines->push_back_n(2);
374                    pts[0] = devPts[0];
375                    pts[1] = devPts[1];
376                }
377                break;
378            case SkPath::kQuad_Verb: {
379                SkPoint choppedPts[5];
380                // Chopping the quad helps when the quad is either degenerate or nearly degenerate.
381                // When it is degenerate it allows the approximation with lines to work since the
382                // chop point (if there is one) will be at the parabola's vertex. In the nearly
383                // degenerate the QuadUVMatrix computed for the points is almost singular which
384                // can cause rendering artifacts.
385                int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts);
386                for (int i = 0; i < n; ++i) {
387                    SkPoint* quadPts = choppedPts + i * 2;
388                    m.mapPoints(devPts, quadPts, 3);
389                    bounds.setBounds(devPts, 3);
390                    bounds.outset(SK_Scalar1, SK_Scalar1);
391                    bounds.roundOut(&ibounds);
392
393                    if (SkIRect::Intersects(devClipBounds, ibounds)) {
394                        int subdiv = num_quad_subdivs(devPts);
395                        GrAssert(subdiv >= -1);
396                        if (-1 == subdiv) {
397                            SkPoint* pts = lines->push_back_n(4);
398                            pts[0] = devPts[0];
399                            pts[1] = devPts[1];
400                            pts[2] = devPts[1];
401                            pts[3] = devPts[2];
402                        } else {
403                            // when in perspective keep quads in src space
404                            SkPoint* qPts = persp ? quadPts : devPts;
405                            SkPoint* pts = quads->push_back_n(3);
406                            pts[0] = qPts[0];
407                            pts[1] = qPts[1];
408                            pts[2] = qPts[2];
409                            quadSubdivCnts->push_back() = subdiv;
410                            totalQuadCount += 1 << subdiv;
411                        }
412                    }
413                }
414                break;
415            }
416            case SkPath::kCubic_Verb:
417                m.mapPoints(devPts, pathPts, 4);
418                bounds.setBounds(devPts, 4);
419                bounds.outset(SK_Scalar1, SK_Scalar1);
420                bounds.roundOut(&ibounds);
421                if (SkIRect::Intersects(devClipBounds, ibounds)) {
422                    PREALLOC_PTARRAY(32) q;
423                    // we don't need a direction if we aren't constraining the subdivision
424                    static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
425                    // We convert cubics to quadratics (for now).
426                    // In perspective have to do conversion in src space.
427                    if (persp) {
428                        SkScalar tolScale =
429                            GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
430                                                             path.getBounds());
431                        GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q);
432                    } else {
433                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
434                    }
435                    for (int i = 0; i < q.count(); i += 3) {
436                        SkPoint* qInDevSpace;
437                        // bounds has to be calculated in device space, but q is
438                        // in src space when there is perspective.
439                        if (persp) {
440                            m.mapPoints(devPts, &q[i], 3);
441                            bounds.setBounds(devPts, 3);
442                            qInDevSpace = devPts;
443                        } else {
444                            bounds.setBounds(&q[i], 3);
445                            qInDevSpace = &q[i];
446                        }
447                        bounds.outset(SK_Scalar1, SK_Scalar1);
448                        bounds.roundOut(&ibounds);
449                        if (SkIRect::Intersects(devClipBounds, ibounds)) {
450                            int subdiv = num_quad_subdivs(qInDevSpace);
451                            GrAssert(subdiv >= -1);
452                            if (-1 == subdiv) {
453                                SkPoint* pts = lines->push_back_n(4);
454                                // lines should always be in device coords
455                                pts[0] = qInDevSpace[0];
456                                pts[1] = qInDevSpace[1];
457                                pts[2] = qInDevSpace[1];
458                                pts[3] = qInDevSpace[2];
459                            } else {
460                                SkPoint* pts = quads->push_back_n(3);
461                                // q is already in src space when there is no
462                                // perspective and dev coords otherwise.
463                                pts[0] = q[0 + i];
464                                pts[1] = q[1 + i];
465                                pts[2] = q[2 + i];
466                                quadSubdivCnts->push_back() = subdiv;
467                                totalQuadCount += 1 << subdiv;
468                            }
469                        }
470                    }
471                }
472                break;
473            case SkPath::kClose_Verb:
474                break;
475            case SkPath::kDone_Verb:
476                return totalQuadCount;
477        }
478    }
479}
480
481struct LineVertex {
482    GrPoint fPos;
483    GrColor fCoverage;
484};
485
486struct BezierVertex {
487    GrPoint fPos;
488    union {
489        struct {
490            SkScalar fK;
491            SkScalar fL;
492            SkScalar fM;
493        } fConic;
494        GrVec   fQuadCoord;
495        struct {
496            SkScalar fBogus[4];
497        };
498    };
499};
500
501GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(GrPoint));
502
503void intersect_lines(const SkPoint& ptA, const SkVector& normA,
504                     const SkPoint& ptB, const SkVector& normB,
505                     SkPoint* result) {
506
507    SkScalar lineAW = -normA.dot(ptA);
508    SkScalar lineBW = -normB.dot(ptB);
509
510    SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
511        SkScalarMul(normA.fY, normB.fX);
512    wInv = SkScalarInvert(wInv);
513
514    result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
515    result->fX = SkScalarMul(result->fX, wInv);
516
517    result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
518    result->fY = SkScalarMul(result->fY, wInv);
519}
520
521void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kVertsPerQuad]) {
522    // this should be in the src space, not dev coords, when we have perspective
523    GrPathUtils::QuadUVMatrix DevToUV(qpts);
524    DevToUV.apply<kVertsPerQuad, sizeof(BezierVertex), sizeof(GrPoint)>(verts);
525}
526
527void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
528                const SkMatrix* toSrc, BezierVertex verts[kVertsPerQuad],
529                SkRect* devBounds) {
530    GrAssert(!toDevice == !toSrc);
531    // original quad is specified by tri a,b,c
532    SkPoint a = qpts[0];
533    SkPoint b = qpts[1];
534    SkPoint c = qpts[2];
535
536    if (toDevice) {
537        toDevice->mapPoints(&a, 1);
538        toDevice->mapPoints(&b, 1);
539        toDevice->mapPoints(&c, 1);
540    }
541    // make a new poly where we replace a and c by a 1-pixel wide edges orthog
542    // to edges ab and bc:
543    //
544    //   before       |        after
545    //                |              b0
546    //         b      |
547    //                |
548    //                |     a0            c0
549    // a         c    |        a1       c1
550    //
551    // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
552    // respectively.
553    BezierVertex& a0 = verts[0];
554    BezierVertex& a1 = verts[1];
555    BezierVertex& b0 = verts[2];
556    BezierVertex& c0 = verts[3];
557    BezierVertex& c1 = verts[4];
558
559    SkVector ab = b;
560    ab -= a;
561    SkVector ac = c;
562    ac -= a;
563    SkVector cb = b;
564    cb -= c;
565
566    // We should have already handled degenerates
567    GrAssert(ab.length() > 0 && cb.length() > 0);
568
569    ab.normalize();
570    SkVector abN;
571    abN.setOrthog(ab, SkVector::kLeft_Side);
572    if (abN.dot(ac) > 0) {
573        abN.negate();
574    }
575
576    cb.normalize();
577    SkVector cbN;
578    cbN.setOrthog(cb, SkVector::kLeft_Side);
579    if (cbN.dot(ac) < 0) {
580        cbN.negate();
581    }
582
583    a0.fPos = a;
584    a0.fPos += abN;
585    a1.fPos = a;
586    a1.fPos -= abN;
587
588    c0.fPos = c;
589    c0.fPos += cbN;
590    c1.fPos = c;
591    c1.fPos -= cbN;
592
593    // This point may not be within 1 pixel of a control point. We update the bounding box to
594    // include it.
595    intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
596    devBounds->growToInclude(b0.fPos.fX, b0.fPos.fY);
597
598    if (toSrc) {
599        toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kVertsPerQuad);
600    }
601}
602
603// Input:
604// Three control points: p[0], p[1], p[2] and weight: w
605// Output:
606// Let:
607// l = (2*w * (y1 - y0), 2*w * (x0 - x1), 2*w * (x1*y0 - x0*y1))
608// m = (2*w * (y2 - y1), 2*w * (x1 - x2), 2*w * (x2*y1 - x1*y2))
609// k = (y2 - y0, x0 - x2, (x2 - x0)*y0 - (y2 - y0)*x0 )
610void calc_conic_klm(const SkPoint p[3], const SkScalar weight,
611                       SkScalar k[3], SkScalar l[3], SkScalar m[3]) {
612    const SkScalar w2 = 2 * weight;
613    l[0] = w2 * (p[1].fY - p[0].fY);
614    l[1] = w2 * (p[0].fX - p[1].fX);
615    l[2] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY);
616
617    m[0] = w2 * (p[2].fY - p[1].fY);
618    m[1] = w2 * (p[1].fX - p[2].fX);
619    m[2] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY);
620
621    k[0] = p[2].fY - p[0].fY;
622    k[1] = p[0].fX - p[2].fX;
623    k[2] = (p[2].fX - p[0].fX) * p[0].fY - (p[2].fY - p[0].fY) * p[0].fX;
624
625    // scale the max absolute value of coeffs to 10
626    SkScalar scale = 0.0f;
627    for (int i = 0; i < 3; ++i) {
628       scale = SkMaxScalar(scale, SkScalarAbs(k[i]));
629       scale = SkMaxScalar(scale, SkScalarAbs(l[i]));
630       scale = SkMaxScalar(scale, SkScalarAbs(m[i]));
631    }
632    GrAssert(scale > 0);
633    scale /= 10.0f;
634    k[0] /= scale;
635    k[1] /= scale;
636    k[2] /= scale;
637    l[0] /= scale;
638    l[1] /= scale;
639    l[2] /= scale;
640    m[0] /= scale;
641    m[1] /= scale;
642    m[2] /= scale;
643}
644
645// Equations based off of Loop-Blinn Quadratic GPU Rendering
646// Input Parametric:
647// 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)
648// Output Implicit:
649// f(x, y, w) = f(P) = K^2 - LM
650// K = dot(k, P), L = dot(l, P), M = dot(m, P)
651// k, l, m are calculated in function calc_conic_klm
652void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kVertsPerQuad], const float weight) {
653    SkScalar k[3];
654    SkScalar l[3];
655    SkScalar m[3];
656
657    calc_conic_klm(p, weight, k, l, m);
658
659    for (int i = 0; i < kVertsPerQuad; ++i) {
660        const SkPoint pnt = verts[i].fPos;
661        verts[i].fConic.fK = pnt.fX * k[0] + pnt.fY * k[1] + k[2];
662        verts[i].fConic.fL = pnt.fX * l[0] + pnt.fY * l[1] + l[2];
663        verts[i].fConic.fM = pnt.fX * m[0] + pnt.fY * m[1] + m[2];
664    }
665}
666
667void add_conics(const SkPoint p[3],
668                float weight,
669                const SkMatrix* toDevice,
670                const SkMatrix* toSrc,
671                BezierVertex** vert,
672                SkRect* devBounds) {
673    bloat_quad(p, toDevice, toSrc, *vert, devBounds);
674    set_conic_coeffs(p, *vert, weight);
675    *vert += kVertsPerQuad;
676}
677
678void add_quads(const SkPoint p[3],
679               int subdiv,
680               const SkMatrix* toDevice,
681               const SkMatrix* toSrc,
682               BezierVertex** vert,
683               SkRect* devBounds) {
684    GrAssert(subdiv >= 0);
685    if (subdiv) {
686        SkPoint newP[5];
687        SkChopQuadAtHalf(p, newP);
688        add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert, devBounds);
689        add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert, devBounds);
690    } else {
691        bloat_quad(p, toDevice, toSrc, *vert, devBounds);
692        set_uv_quad(p, *vert);
693        *vert += kVertsPerQuad;
694    }
695}
696
697void add_line(const SkPoint p[2],
698              int rtHeight,
699              const SkMatrix* toSrc,
700              GrColor coverage,
701              LineVertex** vert) {
702    const SkPoint& a = p[0];
703    const SkPoint& b = p[1];
704
705    SkVector orthVec = b;
706    orthVec -= a;
707
708    if (orthVec.setLength(SK_Scalar1)) {
709        orthVec.setOrthog(orthVec);
710
711        for (int i = 0; i < kVertsPerLineSeg; ++i) {
712            (*vert)[i].fPos = (i & 0x1) ? b : a;
713            if (i & 0x2) {
714                (*vert)[i].fPos += orthVec;
715                (*vert)[i].fCoverage = 0;
716            } else if (i & 0x4) {
717                (*vert)[i].fPos -= orthVec;
718                (*vert)[i].fCoverage = 0;
719            } else {
720                (*vert)[i].fCoverage = coverage;
721            }
722        }
723        if (NULL != toSrc) {
724            toSrc->mapPointsWithStride(&(*vert)->fPos,
725                                       sizeof(LineVertex),
726                                       kVertsPerLineSeg);
727        }
728    } else {
729        // just make it degenerate and likely offscreen
730        for (int i = 0; i < kVertsPerLineSeg; ++i) {
731            (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax);
732        }
733    }
734
735    *vert += kVertsPerLineSeg;
736}
737
738}
739
740/**
741 * Shader is based off of Loop-Blinn Quadratic GPU Rendering
742 * The output of this effect is a hairline edge for conics.
743 * Conics specified by implicit equation K^2 - LM.
744 * K, L, and M, are the first three values of the vertex attribute,
745 * the fourth value is not used. Distance is calculated using a
746 * first order approximation from the taylor series.
747 * Coverage is max(0, 1-distance).
748 */
749
750/**
751 * Test were also run using a second order distance approximation.
752 * There were two versions of the second order approx. The first version
753 * is of roughly the form:
754 * f(q) = |f(p)| - ||f'(p)||*||q-p|| - ||f''(p)||*||q-p||^2.
755 * The second is similar:
756 * f(q) = |f(p)| + ||f'(p)||*||q-p|| + ||f''(p)||*||q-p||^2.
757 * The exact version of the equations can be found in the paper
758 * "Distance Approximations for Rasterizing Implicit Curves" by Gabriel Taubin
759 *
760 * In both versions we solve the quadratic for ||q-p||.
761 * Version 1:
762 * gFM is magnitude of first partials and gFM2 is magnitude of 2nd partials (as derived from paper)
763 * builder->fsCodeAppend("\t\tedgeAlpha = (sqrt(gFM*gFM+4.0*func*gF2M) - gFM)/(2.0*gF2M);\n");
764 * Version 2:
765 * builder->fsCodeAppend("\t\tedgeAlpha = (gFM - sqrt(gFM*gFM-4.0*func*gF2M))/(2.0*gF2M);\n");
766 *
767 * Also note that 2nd partials of k,l,m are zero
768 *
769 * When comparing the two second order approximations to the first order approximations,
770 * the following results were found. Version 1 tends to underestimate the distances, thus it
771 * basically increases all the error that we were already seeing in the first order
772 * approx. So this version is not the one to use. Version 2 has the opposite effect
773 * and tends to overestimate the distances. This is much closer to what we are
774 * looking for. It is able to render ellipses (even thin ones) without the need to chop.
775 * However, it can not handle thin hyperbolas well and thus would still rely on
776 * chopping to tighten the clipping. Another side effect of the overestimating is
777 * that the curves become much thinner and "ropey". If all that was ever rendered
778 * were "not too thin" curves and ellipses then 2nd order may have an advantage since
779 * only one geometry would need to be rendered. However no benches were run comparing
780 * chopped first order and non chopped 2nd order.
781 */
782class HairConicEdgeEffect : public GrEffect {
783public:
784    static GrEffectRef* Create() {
785        GR_CREATE_STATIC_EFFECT(gHairConicEdgeEffect, HairConicEdgeEffect, ());
786        gHairConicEdgeEffect->ref();
787        return gHairConicEdgeEffect;
788    }
789
790    virtual ~HairConicEdgeEffect() {}
791
792    static const char* Name() { return "HairConicEdge"; }
793
794    virtual void getConstantColorComponents(GrColor* color,
795                                            uint32_t* validFlags) const SK_OVERRIDE {
796        *validFlags = 0;
797    }
798
799    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
800        return GrTBackendEffectFactory<HairConicEdgeEffect>::getInstance();
801    }
802
803    class GLEffect : public GrGLEffect {
804    public:
805        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
806            : INHERITED (factory) {}
807
808        virtual void emitCode(GrGLShaderBuilder* builder,
809                              const GrDrawEffect& drawEffect,
810                              EffectKey key,
811                              const char* outputColor,
812                              const char* inputColor,
813                              const TextureSamplerArray& samplers) SK_OVERRIDE {
814            const char *vsName, *fsName;
815
816            SkAssertResult(builder->enableFeature(
817                    GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
818            builder->addVarying(kVec4f_GrSLType, "ConicCoeffs",
819                                &vsName, &fsName);
820            const SkString* attr0Name =
821                builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
822            builder->vsCodeAppendf("\t%s = %s;\n", vsName, attr0Name->c_str());
823
824            builder->fsCodeAppend("\t\tfloat edgeAlpha;\n");
825
826            builder->fsCodeAppendf("\t\tvec3 dklmdx = dFdx(%s.xyz);\n", fsName);
827            builder->fsCodeAppendf("\t\tvec3 dklmdy = dFdy(%s.xyz);\n", fsName);
828            builder->fsCodeAppendf("\t\tfloat dfdx =\n"
829                                   "\t\t\t2.0*%s.x*dklmdx.x - %s.y*dklmdx.z - %s.z*dklmdx.y;\n",
830                                   fsName, fsName, fsName);
831            builder->fsCodeAppendf("\t\tfloat dfdy =\n"
832                                   "\t\t\t2.0*%s.x*dklmdy.x - %s.y*dklmdy.z - %s.z*dklmdy.y;\n",
833                                   fsName, fsName, fsName);
834            builder->fsCodeAppend("\t\tvec2 gF = vec2(dfdx, dfdy);\n");
835            builder->fsCodeAppend("\t\tfloat gFM = sqrt(dot(gF, gF));\n");
836            builder->fsCodeAppendf("\t\tfloat func = abs(%s.x*%s.x - %s.y*%s.z);\n", fsName, fsName,
837                                   fsName, fsName);
838            builder->fsCodeAppend("\t\tedgeAlpha = func / gFM;\n");
839            builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
840            // Add line below for smooth cubic ramp
841            // builder->fsCodeAppend("\t\tedgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);\n");
842
843            SkString modulate;
844            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
845            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
846        }
847
848        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
849            return 0x0;
850        }
851
852        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
853
854    private:
855        typedef GrGLEffect INHERITED;
856    };
857
858private:
859    HairConicEdgeEffect() {
860        this->addVertexAttrib(kVec4f_GrSLType);
861    }
862
863    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
864        return true;
865    }
866
867    GR_DECLARE_EFFECT_TEST;
868
869    typedef GrEffect INHERITED;
870};
871
872GR_DEFINE_EFFECT_TEST(HairConicEdgeEffect);
873
874GrEffectRef* HairConicEdgeEffect::TestCreate(SkMWCRandom* random,
875                                             GrContext*,
876                                             const GrDrawTargetCaps& caps,
877                                             GrTexture*[]) {
878    return caps.shaderDerivativeSupport() ? HairConicEdgeEffect::Create() : NULL;
879}
880
881/**
882 * The output of this effect is a hairline edge for quadratics.
883 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
884 * two components of the vertex attribute. Uses unsigned distance.
885 * Coverage is min(0, 1-distance). 3rd & 4th component unused.
886 * Requires shader derivative instruction support.
887 */
888class HairQuadEdgeEffect : public GrEffect {
889public:
890
891    static GrEffectRef* Create() {
892        GR_CREATE_STATIC_EFFECT(gHairQuadEdgeEffect, HairQuadEdgeEffect, ());
893        gHairQuadEdgeEffect->ref();
894        return gHairQuadEdgeEffect;
895    }
896
897    virtual ~HairQuadEdgeEffect() {}
898
899    static const char* Name() { return "HairQuadEdge"; }
900
901    virtual void getConstantColorComponents(GrColor* color,
902                                            uint32_t* validFlags) const SK_OVERRIDE {
903        *validFlags = 0;
904    }
905
906    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
907        return GrTBackendEffectFactory<HairQuadEdgeEffect>::getInstance();
908    }
909
910    class GLEffect : public GrGLEffect {
911    public:
912        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
913            : INHERITED (factory) {}
914
915        virtual void emitCode(GrGLShaderBuilder* builder,
916                              const GrDrawEffect& drawEffect,
917                              EffectKey key,
918                              const char* outputColor,
919                              const char* inputColor,
920                              const TextureSamplerArray& samplers) SK_OVERRIDE {
921            const char *vsName, *fsName;
922            const SkString* attrName =
923                builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
924            builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n");
925
926            SkAssertResult(builder->enableFeature(
927                    GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
928            builder->addVarying(kVec4f_GrSLType, "HairQuadEdge", &vsName, &fsName);
929
930            builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
931            builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
932            builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
933                                   "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
934                                   fsName, fsName);
935            builder->fsCodeAppendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
936                                   fsName);
937            builder->fsCodeAppend("\t\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
938            builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
939
940            SkString modulate;
941            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
942            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
943
944            builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
945        }
946
947        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
948            return 0x0;
949        }
950
951        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
952
953    private:
954        typedef GrGLEffect INHERITED;
955    };
956
957private:
958    HairQuadEdgeEffect() {
959        this->addVertexAttrib(kVec4f_GrSLType);
960    }
961
962    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
963        return true;
964    }
965
966    GR_DECLARE_EFFECT_TEST;
967
968    typedef GrEffect INHERITED;
969};
970
971GR_DEFINE_EFFECT_TEST(HairQuadEdgeEffect);
972
973GrEffectRef* HairQuadEdgeEffect::TestCreate(SkMWCRandom* random,
974                                            GrContext*,
975                                            const GrDrawTargetCaps& caps,
976                                            GrTexture*[]) {
977    // Doesn't work without derivative instructions.
978    return caps.shaderDerivativeSupport() ? HairQuadEdgeEffect::Create() : NULL;
979}
980
981///////////////////////////////////////////////////////////////////////////////
982
983namespace {
984
985// position + edge
986extern const GrVertexAttrib gHairlineBezierAttribs[] = {
987    {kVec2f_GrVertexAttribType, 0,                  kPosition_GrVertexAttribBinding},
988    {kVec4f_GrVertexAttribType, sizeof(GrPoint),    kEffect_GrVertexAttribBinding}
989};
990
991// position + coverage
992extern const GrVertexAttrib gHairlineLineAttribs[] = {
993    {kVec2f_GrVertexAttribType,  0,               kPosition_GrVertexAttribBinding},
994    {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding},
995};
996
997};
998
999bool GrAAHairLinePathRenderer::createLineGeom(
1000            const SkPath& path,
1001            GrDrawTarget* target,
1002            const PtArray& lines,
1003            int lineCnt,
1004            GrDrawTarget::AutoReleaseGeometry* arg,
1005            SkRect* devBounds) {
1006    GrDrawState* drawState = target->drawState();
1007    int rtHeight = drawState->getRenderTarget()->height();
1008
1009    const SkMatrix& viewM = drawState->getViewMatrix();
1010
1011    *devBounds = path.getBounds();
1012    viewM.mapRect(devBounds);
1013    devBounds->outset(SK_Scalar1, SK_Scalar1);
1014
1015    int vertCnt = kVertsPerLineSeg * lineCnt;
1016
1017    target->drawState()->setVertexAttribs<gHairlineLineAttribs>(SK_ARRAY_COUNT(gHairlineLineAttribs));
1018    GrAssert(sizeof(LineVertex) == target->getDrawState().getVertexSize());
1019
1020    if (!arg->set(target, vertCnt, 0)) {
1021        return false;
1022    }
1023
1024    LineVertex* verts = reinterpret_cast<LineVertex*>(arg->vertices());
1025
1026    const SkMatrix* toSrc = NULL;
1027    SkMatrix ivm;
1028
1029    if (viewM.hasPerspective()) {
1030        if (viewM.invert(&ivm)) {
1031            toSrc = &ivm;
1032        }
1033    }
1034
1035    for (int i = 0; i < lineCnt; ++i) {
1036        add_line(&lines[2*i], rtHeight, toSrc, drawState->getCoverage(), &verts);
1037    }
1038
1039    return true;
1040}
1041
1042bool GrAAHairLinePathRenderer::createBezierGeom(
1043                                          const SkPath& path,
1044                                          GrDrawTarget* target,
1045                                          const PtArray& quads,
1046                                          int quadCnt,
1047                                          const PtArray& conics,
1048                                          int conicCnt,
1049                                          const IntArray& qSubdivs,
1050                                          const FloatArray& cWeights,
1051                                          GrDrawTarget::AutoReleaseGeometry* arg,
1052                                          SkRect* devBounds) {
1053    GrDrawState* drawState = target->drawState();
1054
1055    const SkMatrix& viewM = drawState->getViewMatrix();
1056
1057    // All the vertices that we compute are within 1 of path control points with the exception of
1058    // one of the bounding vertices for each quad. The add_quads() function will update the bounds
1059    // for each quad added.
1060    *devBounds = path.getBounds();
1061    viewM.mapRect(devBounds);
1062    devBounds->outset(SK_Scalar1, SK_Scalar1);
1063
1064    int vertCnt = kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt;
1065
1066    target->drawState()->setVertexAttribs<gHairlineBezierAttribs>(SK_ARRAY_COUNT(gHairlineBezierAttribs));
1067    GrAssert(sizeof(BezierVertex) == target->getDrawState().getVertexSize());
1068
1069    if (!arg->set(target, vertCnt, 0)) {
1070        return false;
1071    }
1072
1073    BezierVertex* verts = reinterpret_cast<BezierVertex*>(arg->vertices());
1074
1075    const SkMatrix* toDevice = NULL;
1076    const SkMatrix* toSrc = NULL;
1077    SkMatrix ivm;
1078
1079    if (viewM.hasPerspective()) {
1080        if (viewM.invert(&ivm)) {
1081            toDevice = &viewM;
1082            toSrc = &ivm;
1083        }
1084    }
1085
1086    int unsubdivQuadCnt = quads.count() / 3;
1087    for (int i = 0; i < unsubdivQuadCnt; ++i) {
1088        GrAssert(qSubdivs[i] >= 0);
1089        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds);
1090    }
1091
1092    // Start Conics
1093    for (int i = 0; i < conicCnt; ++i) {
1094        add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts, devBounds);
1095    }
1096    return true;
1097}
1098
1099bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
1100                                           const SkStrokeRec& stroke,
1101                                           const GrDrawTarget* target,
1102                                           bool antiAlias) const {
1103    if (!stroke.isHairlineStyle() || !antiAlias) {
1104        return false;
1105    }
1106
1107    if (SkPath::kLine_SegmentMask == path.getSegmentMasks() ||
1108        target->caps()->shaderDerivativeSupport()) {
1109        return true;
1110    }
1111    return false;
1112}
1113
1114template <class VertexType>
1115bool check_bounds(GrDrawState* drawState, const SkRect& devBounds, void* vertices, int vCount)
1116{
1117    SkRect tolDevBounds = devBounds;
1118    tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
1119    SkRect actualBounds;
1120
1121    VertexType* verts = reinterpret_cast<VertexType*>(vertices);
1122    bool first = true;
1123    for (int i = 0; i < vCount; ++i) {
1124        SkPoint pos = verts[i].fPos;
1125        // This is a hack to workaround the fact that we move some degenerate segments offscreen.
1126        if (SK_ScalarMax == pos.fX) {
1127            continue;
1128        }
1129        drawState->getViewMatrix().mapPoints(&pos, 1);
1130        if (first) {
1131            actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY);
1132            first = false;
1133        } else {
1134            actualBounds.growToInclude(pos.fX, pos.fY);
1135        }
1136    }
1137    if (!first) {
1138        return tolDevBounds.contains(actualBounds);
1139    }
1140
1141    return true;
1142}
1143
1144bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
1145                                          const SkStrokeRec&,
1146                                          GrDrawTarget* target,
1147                                          bool antiAlias) {
1148
1149    GrDrawState* drawState = target->drawState();
1150
1151    SkIRect devClipBounds;
1152    target->getClip()->getConservativeBounds(drawState->getRenderTarget(), &devClipBounds);
1153
1154    int lineCnt;
1155    int quadCnt;
1156    int conicCnt;
1157    PREALLOC_PTARRAY(128) lines;
1158    PREALLOC_PTARRAY(128) quads;
1159    PREALLOC_PTARRAY(128) conics;
1160    IntArray qSubdivs;
1161    FloatArray cWeights;
1162    quadCnt = generate_lines_and_quads(path, drawState->getViewMatrix(), devClipBounds,
1163                                       &lines, &quads, &conics, &qSubdivs, &cWeights);
1164    lineCnt = lines.count() / 2;
1165    conicCnt = conics.count() / 3;
1166
1167    // do lines first
1168    {
1169        GrDrawTarget::AutoReleaseGeometry arg;
1170        SkRect devBounds;
1171
1172        if (!this->createLineGeom(path,
1173                                  target,
1174                                  lines,
1175                                  lineCnt,
1176                                  &arg,
1177                                  &devBounds)) {
1178            return false;
1179        }
1180
1181        GrDrawTarget::AutoStateRestore asr;
1182
1183        // createGeom transforms the geometry to device space when the matrix does not have
1184        // perspective.
1185        if (target->getDrawState().getViewMatrix().hasPerspective()) {
1186            asr.set(target, GrDrawTarget::kPreserve_ASRInit);
1187        } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
1188            return false;
1189        }
1190        GrDrawState* drawState = target->drawState();
1191
1192        // Check devBounds
1193        SkASSERT(check_bounds<LineVertex>(drawState, devBounds, arg.vertices(),
1194                                          kVertsPerLineSeg * lineCnt));
1195
1196        {
1197            GrDrawState::AutoRestoreEffects are(drawState);
1198            target->setIndexSourceToBuffer(fLinesIndexBuffer);
1199            int lines = 0;
1200            while (lines < lineCnt) {
1201                int n = GrMin(lineCnt - lines, kNumLineSegsInIdxBuffer);
1202                target->drawIndexed(kTriangles_GrPrimitiveType,
1203                                    kVertsPerLineSeg*lines,     // startV
1204                                    0,                          // startI
1205                                    kVertsPerLineSeg*n,         // vCount
1206                                    kIdxsPerLineSeg*n,
1207                                    &devBounds);                // iCount
1208                lines += n;
1209            }
1210        }
1211    }
1212
1213    // then quadratics/conics
1214    {
1215        GrDrawTarget::AutoReleaseGeometry arg;
1216        SkRect devBounds;
1217
1218        if (!this->createBezierGeom(path,
1219                                    target,
1220                                    quads,
1221                                    quadCnt,
1222                                    conics,
1223                                    conicCnt,
1224                                    qSubdivs,
1225                                    cWeights,
1226                                    &arg,
1227                                    &devBounds)) {
1228            return false;
1229        }
1230
1231        GrDrawTarget::AutoStateRestore asr;
1232
1233        // createGeom transforms the geometry to device space when the matrix does not have
1234        // perspective.
1235        if (target->getDrawState().getViewMatrix().hasPerspective()) {
1236            asr.set(target, GrDrawTarget::kPreserve_ASRInit);
1237        } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
1238            return false;
1239        }
1240        GrDrawState* drawState = target->drawState();
1241
1242        static const int kEdgeAttrIndex = 1;
1243
1244        GrEffectRef* hairQuadEffect = HairQuadEdgeEffect::Create();
1245        GrEffectRef* hairConicEffect = HairConicEdgeEffect::Create();
1246
1247        // Check devBounds
1248        SkASSERT(check_bounds<BezierVertex>(drawState, devBounds, arg.vertices(),
1249                                            kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt));
1250
1251        {
1252            GrDrawState::AutoRestoreEffects are(drawState);
1253            target->setIndexSourceToBuffer(fQuadsIndexBuffer);
1254            int quads = 0;
1255            drawState->addCoverageEffect(hairQuadEffect, kEdgeAttrIndex)->unref();
1256            while (quads < quadCnt) {
1257                int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
1258                target->drawIndexed(kTriangles_GrPrimitiveType,
1259                                    kVertsPerQuad*quads,               // startV
1260                                    0,                                 // startI
1261                                    kVertsPerQuad*n,                   // vCount
1262                                    kIdxsPerQuad*n,                    // iCount
1263                                    &devBounds);
1264                quads += n;
1265            }
1266        }
1267
1268        {
1269            GrDrawState::AutoRestoreEffects are(drawState);
1270            int conics = 0;
1271            drawState->addCoverageEffect(hairConicEffect, 1, 2)->unref();
1272            while (conics < conicCnt) {
1273                int n = GrMin(conicCnt - conics, kNumQuadsInIdxBuffer);
1274                target->drawIndexed(kTriangles_GrPrimitiveType,
1275                                    kVertsPerQuad*(quadCnt + conics),  // startV
1276                                    0,                                 // startI
1277                                    kVertsPerQuad*n,                   // vCount
1278                                    kIdxsPerQuad*n,                    // iCount
1279                                    &devBounds);
1280                conics += n;
1281            }
1282        }
1283    }
1284
1285    target->resetIndexSource();
1286
1287    return true;
1288}
1289