gfx/skia/trunk/src/gpu/GrAAHairLinePathRenderer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/GrAAHairLinePathRenderer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1042 @@
     1.4 +/*
     1.5 + * Copyright 2011 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "GrAAHairLinePathRenderer.h"
    1.12 +
    1.13 +#include "GrContext.h"
    1.14 +#include "GrDrawState.h"
    1.15 +#include "GrDrawTargetCaps.h"
    1.16 +#include "GrEffect.h"
    1.17 +#include "GrGpu.h"
    1.18 +#include "GrIndexBuffer.h"
    1.19 +#include "GrPathUtils.h"
    1.20 +#include "GrTBackendEffectFactory.h"
    1.21 +#include "SkGeometry.h"
    1.22 +#include "SkStroke.h"
    1.23 +#include "SkTemplates.h"
    1.24 +
    1.25 +#include "effects/GrBezierEffect.h"
    1.26 +
    1.27 +namespace {
    1.28 +// quadratics are rendered as 5-sided polys in order to bound the
    1.29 +// AA stroke around the center-curve. See comments in push_quad_index_buffer and
    1.30 +// bloat_quad. Quadratics and conics share an index buffer
    1.31 +static const int kVertsPerQuad = 5;
    1.32 +static const int kIdxsPerQuad = 9;
    1.33 +
    1.34 +// lines are rendered as:
    1.35 +//      *______________*
    1.36 +//      |\ -_______   /|
    1.37 +//      | \        \ / |
    1.38 +//      |  *--------*  |
    1.39 +//      | /  ______/ \ |
    1.40 +//      */_-__________\*
    1.41 +// For: 6 vertices and 18 indices (for 6 triangles)
    1.42 +static const int kVertsPerLineSeg = 6;
    1.43 +static const int kIdxsPerLineSeg = 18;
    1.44 +
    1.45 +static const int kNumQuadsInIdxBuffer = 256;
    1.46 +static const size_t kQuadIdxSBufize = kIdxsPerQuad *
    1.47 +                                      sizeof(uint16_t) *
    1.48 +                                      kNumQuadsInIdxBuffer;
    1.49 +
    1.50 +static const int kNumLineSegsInIdxBuffer = 256;
    1.51 +static const size_t kLineSegIdxSBufize = kIdxsPerLineSeg *
    1.52 +                                         sizeof(uint16_t) *
    1.53 +                                         kNumLineSegsInIdxBuffer;
    1.54 +
    1.55 +static bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
    1.56 +    uint16_t* data = (uint16_t*) qIdxBuffer->lock();
    1.57 +    bool tempData = NULL == data;
    1.58 +    if (tempData) {
    1.59 +        data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad);
    1.60 +    }
    1.61 +    for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
    1.62 +
    1.63 +        // Each quadratic is rendered as a five sided polygon. This poly bounds
    1.64 +        // the quadratic's bounding triangle but has been expanded so that the
    1.65 +        // 1-pixel wide area around the curve is inside the poly.
    1.66 +        // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
    1.67 +        // that is rendered would look like this:
    1.68 +        //              b0
    1.69 +        //              b
    1.70 +        //
    1.71 +        //     a0              c0
    1.72 +        //      a            c
    1.73 +        //       a1       c1
    1.74 +        // Each is drawn as three triangles specified by these 9 indices:
    1.75 +        int baseIdx = i * kIdxsPerQuad;
    1.76 +        uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
    1.77 +        data[0 + baseIdx] = baseVert + 0; // a0
    1.78 +        data[1 + baseIdx] = baseVert + 1; // a1
    1.79 +        data[2 + baseIdx] = baseVert + 2; // b0
    1.80 +        data[3 + baseIdx] = baseVert + 2; // b0
    1.81 +        data[4 + baseIdx] = baseVert + 4; // c1
    1.82 +        data[5 + baseIdx] = baseVert + 3; // c0
    1.83 +        data[6 + baseIdx] = baseVert + 1; // a1
    1.84 +        data[7 + baseIdx] = baseVert + 4; // c1
    1.85 +        data[8 + baseIdx] = baseVert + 2; // b0
    1.86 +    }
    1.87 +    if (tempData) {
    1.88 +        bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
    1.89 +        delete[] data;
    1.90 +        return ret;
    1.91 +    } else {
    1.92 +        qIdxBuffer->unlock();
    1.93 +        return true;
    1.94 +    }
    1.95 +}
    1.96 +
    1.97 +static bool push_line_index_data(GrIndexBuffer* lIdxBuffer) {
    1.98 +    uint16_t* data = (uint16_t*) lIdxBuffer->lock();
    1.99 +    bool tempData = NULL == data;
   1.100 +    if (tempData) {
   1.101 +        data = SkNEW_ARRAY(uint16_t, kNumLineSegsInIdxBuffer * kIdxsPerLineSeg);
   1.102 +    }
   1.103 +    for (int i = 0; i < kNumLineSegsInIdxBuffer; ++i) {
   1.104 +        // Each line segment is rendered as two quads and two triangles.
   1.105 +        // p0 and p1 have alpha = 1 while all other points have alpha = 0.
   1.106 +        // The four external points are offset 1 pixel perpendicular to the
   1.107 +        // line and half a pixel parallel to the line.
   1.108 +        //
   1.109 +        // p4                  p5
   1.110 +        //      p0         p1
   1.111 +        // p2                  p3
   1.112 +        //
   1.113 +        // Each is drawn as six triangles specified by these 18 indices:
   1.114 +        int baseIdx = i * kIdxsPerLineSeg;
   1.115 +        uint16_t baseVert = (uint16_t)(i * kVertsPerLineSeg);
   1.116 +        data[0 + baseIdx] = baseVert + 0;
   1.117 +        data[1 + baseIdx] = baseVert + 1;
   1.118 +        data[2 + baseIdx] = baseVert + 3;
   1.119 +
   1.120 +        data[3 + baseIdx] = baseVert + 0;
   1.121 +        data[4 + baseIdx] = baseVert + 3;
   1.122 +        data[5 + baseIdx] = baseVert + 2;
   1.123 +
   1.124 +        data[6 + baseIdx] = baseVert + 0;
   1.125 +        data[7 + baseIdx] = baseVert + 4;
   1.126 +        data[8 + baseIdx] = baseVert + 5;
   1.127 +
   1.128 +        data[9 + baseIdx] = baseVert + 0;
   1.129 +        data[10+ baseIdx] = baseVert + 5;
   1.130 +        data[11+ baseIdx] = baseVert + 1;
   1.131 +
   1.132 +        data[12 + baseIdx] = baseVert + 0;
   1.133 +        data[13 + baseIdx] = baseVert + 2;
   1.134 +        data[14 + baseIdx] = baseVert + 4;
   1.135 +
   1.136 +        data[15 + baseIdx] = baseVert + 1;
   1.137 +        data[16 + baseIdx] = baseVert + 5;
   1.138 +        data[17 + baseIdx] = baseVert + 3;
   1.139 +    }
   1.140 +    if (tempData) {
   1.141 +        bool ret = lIdxBuffer->updateData(data, kLineSegIdxSBufize);
   1.142 +        delete[] data;
   1.143 +        return ret;
   1.144 +    } else {
   1.145 +        lIdxBuffer->unlock();
   1.146 +        return true;
   1.147 +    }
   1.148 +}
   1.149 +}
   1.150 +
   1.151 +GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
   1.152 +    GrGpu* gpu = context->getGpu();
   1.153 +    GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
   1.154 +    SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
   1.155 +    if (NULL == qIdxBuf || !push_quad_index_data(qIdxBuf)) {
   1.156 +        return NULL;
   1.157 +    }
   1.158 +    GrIndexBuffer* lIdxBuf = gpu->createIndexBuffer(kLineSegIdxSBufize, false);
   1.159 +    SkAutoTUnref<GrIndexBuffer> lIdxBuffer(lIdxBuf);
   1.160 +    if (NULL == lIdxBuf || !push_line_index_data(lIdxBuf)) {
   1.161 +        return NULL;
   1.162 +    }
   1.163 +    return SkNEW_ARGS(GrAAHairLinePathRenderer,
   1.164 +                      (context, lIdxBuf, qIdxBuf));
   1.165 +}
   1.166 +
   1.167 +GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
   1.168 +                                        const GrContext* context,
   1.169 +                                        const GrIndexBuffer* linesIndexBuffer,
   1.170 +                                        const GrIndexBuffer* quadsIndexBuffer) {
   1.171 +    fLinesIndexBuffer = linesIndexBuffer;
   1.172 +    linesIndexBuffer->ref();
   1.173 +    fQuadsIndexBuffer = quadsIndexBuffer;
   1.174 +    quadsIndexBuffer->ref();
   1.175 +}
   1.176 +
   1.177 +GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
   1.178 +    fLinesIndexBuffer->unref();
   1.179 +    fQuadsIndexBuffer->unref();
   1.180 +}
   1.181 +
   1.182 +namespace {
   1.183 +
   1.184 +#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
   1.185 +
   1.186 +// Takes 178th time of logf on Z600 / VC2010
   1.187 +int get_float_exp(float x) {
   1.188 +    GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
   1.189 +#ifdef SK_DEBUG
   1.190 +    static bool tested;
   1.191 +    if (!tested) {
   1.192 +        tested = true;
   1.193 +        SkASSERT(get_float_exp(0.25f) == -2);
   1.194 +        SkASSERT(get_float_exp(0.3f) == -2);
   1.195 +        SkASSERT(get_float_exp(0.5f) == -1);
   1.196 +        SkASSERT(get_float_exp(1.f) == 0);
   1.197 +        SkASSERT(get_float_exp(2.f) == 1);
   1.198 +        SkASSERT(get_float_exp(2.5f) == 1);
   1.199 +        SkASSERT(get_float_exp(8.f) == 3);
   1.200 +        SkASSERT(get_float_exp(100.f) == 6);
   1.201 +        SkASSERT(get_float_exp(1000.f) == 9);
   1.202 +        SkASSERT(get_float_exp(1024.f) == 10);
   1.203 +        SkASSERT(get_float_exp(3000000.f) == 21);
   1.204 +    }
   1.205 +#endif
   1.206 +    const int* iptr = (const int*)&x;
   1.207 +    return (((*iptr) & 0x7f800000) >> 23) - 127;
   1.208 +}
   1.209 +
   1.210 +// Uses the max curvature function for quads to estimate
   1.211 +// where to chop the conic. If the max curvature is not
   1.212 +// found along the curve segment it will return 1 and
   1.213 +// dst[0] is the original conic. If it returns 2 the dst[0]
   1.214 +// and dst[1] are the two new conics.
   1.215 +int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
   1.216 +    SkScalar t = SkFindQuadMaxCurvature(src);
   1.217 +    if (t == 0) {
   1.218 +        if (dst) {
   1.219 +            dst[0].set(src, weight);
   1.220 +        }
   1.221 +        return 1;
   1.222 +    } else {
   1.223 +        if (dst) {
   1.224 +            SkConic conic;
   1.225 +            conic.set(src, weight);
   1.226 +            conic.chopAt(t, dst);
   1.227 +        }
   1.228 +        return 2;
   1.229 +    }
   1.230 +}
   1.231 +
   1.232 +// Calls split_conic on the entire conic and then once more on each subsection.
   1.233 +// Most cases will result in either 1 conic (chop point is not within t range)
   1.234 +// or 3 points (split once and then one subsection is split again).
   1.235 +int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
   1.236 +    SkConic dstTemp[2];
   1.237 +    int conicCnt = split_conic(src, dstTemp, weight);
   1.238 +    if (2 == conicCnt) {
   1.239 +        int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
   1.240 +        conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
   1.241 +    } else {
   1.242 +        dst[0] = dstTemp[0];
   1.243 +    }
   1.244 +    return conicCnt;
   1.245 +}
   1.246 +
   1.247 +// returns 0 if quad/conic is degen or close to it
   1.248 +// in this case approx the path with lines
   1.249 +// otherwise returns 1
   1.250 +int is_degen_quad_or_conic(const SkPoint p[3]) {
   1.251 +    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
   1.252 +    static const SkScalar gDegenerateToLineTolSqd =
   1.253 +        SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
   1.254 +
   1.255 +    if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
   1.256 +        p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
   1.257 +        return 1;
   1.258 +    }
   1.259 +
   1.260 +    SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
   1.261 +    if (dsqd < gDegenerateToLineTolSqd) {
   1.262 +        return 1;
   1.263 +    }
   1.264 +
   1.265 +    if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
   1.266 +        return 1;
   1.267 +    }
   1.268 +    return 0;
   1.269 +}
   1.270 +
   1.271 +// we subdivide the quads to avoid huge overfill
   1.272 +// if it returns -1 then should be drawn as lines
   1.273 +int num_quad_subdivs(const SkPoint p[3]) {
   1.274 +    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
   1.275 +    static const SkScalar gDegenerateToLineTolSqd =
   1.276 +        SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
   1.277 +
   1.278 +    if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
   1.279 +        p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
   1.280 +        return -1;
   1.281 +    }
   1.282 +
   1.283 +    SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
   1.284 +    if (dsqd < gDegenerateToLineTolSqd) {
   1.285 +        return -1;
   1.286 +    }
   1.287 +
   1.288 +    if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
   1.289 +        return -1;
   1.290 +    }
   1.291 +
   1.292 +    // tolerance of triangle height in pixels
   1.293 +    // tuned on windows  Quadro FX 380 / Z600
   1.294 +    // trade off of fill vs cpu time on verts
   1.295 +    // maybe different when do this using gpu (geo or tess shaders)
   1.296 +    static const SkScalar gSubdivTol = 175 * SK_Scalar1;
   1.297 +
   1.298 +    if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
   1.299 +        return 0;
   1.300 +    } else {
   1.301 +        static const int kMaxSub = 4;
   1.302 +        // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
   1.303 +        // = log4(d*d/tol*tol)/2
   1.304 +        // = log2(d*d/tol*tol)
   1.305 +
   1.306 +        // +1 since we're ignoring the mantissa contribution.
   1.307 +        int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
   1.308 +        log = GrMin(GrMax(0, log), kMaxSub);
   1.309 +        return log;
   1.310 +    }
   1.311 +}
   1.312 +
   1.313 +/**
   1.314 + * Generates the lines and quads to be rendered. Lines are always recorded in
   1.315 + * device space. We will do a device space bloat to account for the 1pixel
   1.316 + * thickness.
   1.317 + * Quads are recorded in device space unless m contains
   1.318 + * perspective, then in they are in src space. We do this because we will
   1.319 + * subdivide large quads to reduce over-fill. This subdivision has to be
   1.320 + * performed before applying the perspective matrix.
   1.321 + */
   1.322 +int generate_lines_and_quads(const SkPath& path,
   1.323 +                             const SkMatrix& m,
   1.324 +                             const SkIRect& devClipBounds,
   1.325 +                             GrAAHairLinePathRenderer::PtArray* lines,
   1.326 +                             GrAAHairLinePathRenderer::PtArray* quads,
   1.327 +                             GrAAHairLinePathRenderer::PtArray* conics,
   1.328 +                             GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
   1.329 +                             GrAAHairLinePathRenderer::FloatArray* conicWeights) {
   1.330 +    SkPath::Iter iter(path, false);
   1.331 +
   1.332 +    int totalQuadCount = 0;
   1.333 +    SkRect bounds;
   1.334 +    SkIRect ibounds;
   1.335 +
   1.336 +    bool persp = m.hasPerspective();
   1.337 +
   1.338 +    for (;;) {
   1.339 +        GrPoint pathPts[4];
   1.340 +        GrPoint devPts[4];
   1.341 +        SkPath::Verb verb = iter.next(pathPts);
   1.342 +        switch (verb) {
   1.343 +            case SkPath::kConic_Verb: {
   1.344 +                SkConic dst[4];
   1.345 +                // We chop the conics to create tighter clipping to hide error
   1.346 +                // that appears near max curvature of very thin conics. Thin
   1.347 +                // hyperbolas with high weight still show error.
   1.348 +                int conicCnt = chop_conic(pathPts, dst, iter.conicWeight());
   1.349 +                for (int i = 0; i < conicCnt; ++i) {
   1.350 +                    SkPoint* chopPnts = dst[i].fPts;
   1.351 +                    m.mapPoints(devPts, chopPnts, 3);
   1.352 +                    bounds.setBounds(devPts, 3);
   1.353 +                    bounds.outset(SK_Scalar1, SK_Scalar1);
   1.354 +                    bounds.roundOut(&ibounds);
   1.355 +                    if (SkIRect::Intersects(devClipBounds, ibounds)) {
   1.356 +                        if (is_degen_quad_or_conic(devPts)) {
   1.357 +                            SkPoint* pts = lines->push_back_n(4);
   1.358 +                            pts[0] = devPts[0];
   1.359 +                            pts[1] = devPts[1];
   1.360 +                            pts[2] = devPts[1];
   1.361 +                            pts[3] = devPts[2];
   1.362 +                        } else {
   1.363 +                            // when in perspective keep conics in src space
   1.364 +                            SkPoint* cPts = persp ? chopPnts : devPts;
   1.365 +                            SkPoint* pts = conics->push_back_n(3);
   1.366 +                            pts[0] = cPts[0];
   1.367 +                            pts[1] = cPts[1];
   1.368 +                            pts[2] = cPts[2];
   1.369 +                            conicWeights->push_back() = dst[i].fW;
   1.370 +                        }
   1.371 +                    }
   1.372 +                }
   1.373 +                break;
   1.374 +            }
   1.375 +            case SkPath::kMove_Verb:
   1.376 +                break;
   1.377 +            case SkPath::kLine_Verb:
   1.378 +                m.mapPoints(devPts, pathPts, 2);
   1.379 +                bounds.setBounds(devPts, 2);
   1.380 +                bounds.outset(SK_Scalar1, SK_Scalar1);
   1.381 +                bounds.roundOut(&ibounds);
   1.382 +                if (SkIRect::Intersects(devClipBounds, ibounds)) {
   1.383 +                    SkPoint* pts = lines->push_back_n(2);
   1.384 +                    pts[0] = devPts[0];
   1.385 +                    pts[1] = devPts[1];
   1.386 +                }
   1.387 +                break;
   1.388 +            case SkPath::kQuad_Verb: {
   1.389 +                SkPoint choppedPts[5];
   1.390 +                // Chopping the quad helps when the quad is either degenerate or nearly degenerate.
   1.391 +                // When it is degenerate it allows the approximation with lines to work since the
   1.392 +                // chop point (if there is one) will be at the parabola's vertex. In the nearly
   1.393 +                // degenerate the QuadUVMatrix computed for the points is almost singular which
   1.394 +                // can cause rendering artifacts.
   1.395 +                int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts);
   1.396 +                for (int i = 0; i < n; ++i) {
   1.397 +                    SkPoint* quadPts = choppedPts + i * 2;
   1.398 +                    m.mapPoints(devPts, quadPts, 3);
   1.399 +                    bounds.setBounds(devPts, 3);
   1.400 +                    bounds.outset(SK_Scalar1, SK_Scalar1);
   1.401 +                    bounds.roundOut(&ibounds);
   1.402 +
   1.403 +                    if (SkIRect::Intersects(devClipBounds, ibounds)) {
   1.404 +                        int subdiv = num_quad_subdivs(devPts);
   1.405 +                        SkASSERT(subdiv >= -1);
   1.406 +                        if (-1 == subdiv) {
   1.407 +                            SkPoint* pts = lines->push_back_n(4);
   1.408 +                            pts[0] = devPts[0];
   1.409 +                            pts[1] = devPts[1];
   1.410 +                            pts[2] = devPts[1];
   1.411 +                            pts[3] = devPts[2];
   1.412 +                        } else {
   1.413 +                            // when in perspective keep quads in src space
   1.414 +                            SkPoint* qPts = persp ? quadPts : devPts;
   1.415 +                            SkPoint* pts = quads->push_back_n(3);
   1.416 +                            pts[0] = qPts[0];
   1.417 +                            pts[1] = qPts[1];
   1.418 +                            pts[2] = qPts[2];
   1.419 +                            quadSubdivCnts->push_back() = subdiv;
   1.420 +                            totalQuadCount += 1 << subdiv;
   1.421 +                        }
   1.422 +                    }
   1.423 +                }
   1.424 +                break;
   1.425 +            }
   1.426 +            case SkPath::kCubic_Verb:
   1.427 +                m.mapPoints(devPts, pathPts, 4);
   1.428 +                bounds.setBounds(devPts, 4);
   1.429 +                bounds.outset(SK_Scalar1, SK_Scalar1);
   1.430 +                bounds.roundOut(&ibounds);
   1.431 +                if (SkIRect::Intersects(devClipBounds, ibounds)) {
   1.432 +                    PREALLOC_PTARRAY(32) q;
   1.433 +                    // we don't need a direction if we aren't constraining the subdivision
   1.434 +                    static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
   1.435 +                    // We convert cubics to quadratics (for now).
   1.436 +                    // In perspective have to do conversion in src space.
   1.437 +                    if (persp) {
   1.438 +                        SkScalar tolScale =
   1.439 +                            GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
   1.440 +                                                             path.getBounds());
   1.441 +                        GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q);
   1.442 +                    } else {
   1.443 +                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
   1.444 +                    }
   1.445 +                    for (int i = 0; i < q.count(); i += 3) {
   1.446 +                        SkPoint* qInDevSpace;
   1.447 +                        // bounds has to be calculated in device space, but q is
   1.448 +                        // in src space when there is perspective.
   1.449 +                        if (persp) {
   1.450 +                            m.mapPoints(devPts, &q[i], 3);
   1.451 +                            bounds.setBounds(devPts, 3);
   1.452 +                            qInDevSpace = devPts;
   1.453 +                        } else {
   1.454 +                            bounds.setBounds(&q[i], 3);
   1.455 +                            qInDevSpace = &q[i];
   1.456 +                        }
   1.457 +                        bounds.outset(SK_Scalar1, SK_Scalar1);
   1.458 +                        bounds.roundOut(&ibounds);
   1.459 +                        if (SkIRect::Intersects(devClipBounds, ibounds)) {
   1.460 +                            int subdiv = num_quad_subdivs(qInDevSpace);
   1.461 +                            SkASSERT(subdiv >= -1);
   1.462 +                            if (-1 == subdiv) {
   1.463 +                                SkPoint* pts = lines->push_back_n(4);
   1.464 +                                // lines should always be in device coords
   1.465 +                                pts[0] = qInDevSpace[0];
   1.466 +                                pts[1] = qInDevSpace[1];
   1.467 +                                pts[2] = qInDevSpace[1];
   1.468 +                                pts[3] = qInDevSpace[2];
   1.469 +                            } else {
   1.470 +                                SkPoint* pts = quads->push_back_n(3);
   1.471 +                                // q is already in src space when there is no
   1.472 +                                // perspective and dev coords otherwise.
   1.473 +                                pts[0] = q[0 + i];
   1.474 +                                pts[1] = q[1 + i];
   1.475 +                                pts[2] = q[2 + i];
   1.476 +                                quadSubdivCnts->push_back() = subdiv;
   1.477 +                                totalQuadCount += 1 << subdiv;
   1.478 +                            }
   1.479 +                        }
   1.480 +                    }
   1.481 +                }
   1.482 +                break;
   1.483 +            case SkPath::kClose_Verb:
   1.484 +                break;
   1.485 +            case SkPath::kDone_Verb:
   1.486 +                return totalQuadCount;
   1.487 +        }
   1.488 +    }
   1.489 +}
   1.490 +
   1.491 +struct LineVertex {
   1.492 +    GrPoint fPos;
   1.493 +    GrColor fCoverage;
   1.494 +};
   1.495 +
   1.496 +struct BezierVertex {
   1.497 +    GrPoint fPos;
   1.498 +    union {
   1.499 +        struct {
   1.500 +            SkScalar fK;
   1.501 +            SkScalar fL;
   1.502 +            SkScalar fM;
   1.503 +        } fConic;
   1.504 +        GrVec   fQuadCoord;
   1.505 +        struct {
   1.506 +            SkScalar fBogus[4];
   1.507 +        };
   1.508 +    };
   1.509 +};
   1.510 +
   1.511 +GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(GrPoint));
   1.512 +
   1.513 +void intersect_lines(const SkPoint& ptA, const SkVector& normA,
   1.514 +                     const SkPoint& ptB, const SkVector& normB,
   1.515 +                     SkPoint* result) {
   1.516 +
   1.517 +    SkScalar lineAW = -normA.dot(ptA);
   1.518 +    SkScalar lineBW = -normB.dot(ptB);
   1.519 +
   1.520 +    SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
   1.521 +        SkScalarMul(normA.fY, normB.fX);
   1.522 +    wInv = SkScalarInvert(wInv);
   1.523 +
   1.524 +    result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
   1.525 +    result->fX = SkScalarMul(result->fX, wInv);
   1.526 +
   1.527 +    result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
   1.528 +    result->fY = SkScalarMul(result->fY, wInv);
   1.529 +}
   1.530 +
   1.531 +void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kVertsPerQuad]) {
   1.532 +    // this should be in the src space, not dev coords, when we have perspective
   1.533 +    GrPathUtils::QuadUVMatrix DevToUV(qpts);
   1.534 +    DevToUV.apply<kVertsPerQuad, sizeof(BezierVertex), sizeof(GrPoint)>(verts);
   1.535 +}
   1.536 +
   1.537 +void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
   1.538 +                const SkMatrix* toSrc, BezierVertex verts[kVertsPerQuad],
   1.539 +                SkRect* devBounds) {
   1.540 +    SkASSERT(!toDevice == !toSrc);
   1.541 +    // original quad is specified by tri a,b,c
   1.542 +    SkPoint a = qpts[0];
   1.543 +    SkPoint b = qpts[1];
   1.544 +    SkPoint c = qpts[2];
   1.545 +
   1.546 +    if (toDevice) {
   1.547 +        toDevice->mapPoints(&a, 1);
   1.548 +        toDevice->mapPoints(&b, 1);
   1.549 +        toDevice->mapPoints(&c, 1);
   1.550 +    }
   1.551 +    // make a new poly where we replace a and c by a 1-pixel wide edges orthog
   1.552 +    // to edges ab and bc:
   1.553 +    //
   1.554 +    //   before       |        after
   1.555 +    //                |              b0
   1.556 +    //         b      |
   1.557 +    //                |
   1.558 +    //                |     a0            c0
   1.559 +    // a         c    |        a1       c1
   1.560 +    //
   1.561 +    // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
   1.562 +    // respectively.
   1.563 +    BezierVertex& a0 = verts[0];
   1.564 +    BezierVertex& a1 = verts[1];
   1.565 +    BezierVertex& b0 = verts[2];
   1.566 +    BezierVertex& c0 = verts[3];
   1.567 +    BezierVertex& c1 = verts[4];
   1.568 +
   1.569 +    SkVector ab = b;
   1.570 +    ab -= a;
   1.571 +    SkVector ac = c;
   1.572 +    ac -= a;
   1.573 +    SkVector cb = b;
   1.574 +    cb -= c;
   1.575 +
   1.576 +    // We should have already handled degenerates
   1.577 +    SkASSERT(ab.length() > 0 && cb.length() > 0);
   1.578 +
   1.579 +    ab.normalize();
   1.580 +    SkVector abN;
   1.581 +    abN.setOrthog(ab, SkVector::kLeft_Side);
   1.582 +    if (abN.dot(ac) > 0) {
   1.583 +        abN.negate();
   1.584 +    }
   1.585 +
   1.586 +    cb.normalize();
   1.587 +    SkVector cbN;
   1.588 +    cbN.setOrthog(cb, SkVector::kLeft_Side);
   1.589 +    if (cbN.dot(ac) < 0) {
   1.590 +        cbN.negate();
   1.591 +    }
   1.592 +
   1.593 +    a0.fPos = a;
   1.594 +    a0.fPos += abN;
   1.595 +    a1.fPos = a;
   1.596 +    a1.fPos -= abN;
   1.597 +
   1.598 +    c0.fPos = c;
   1.599 +    c0.fPos += cbN;
   1.600 +    c1.fPos = c;
   1.601 +    c1.fPos -= cbN;
   1.602 +
   1.603 +    intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
   1.604 +    devBounds->growToInclude(&verts[0].fPos, sizeof(BezierVertex), kVertsPerQuad);
   1.605 +
   1.606 +    if (toSrc) {
   1.607 +        toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kVertsPerQuad);
   1.608 +    }
   1.609 +}
   1.610 +
   1.611 +// Equations based off of Loop-Blinn Quadratic GPU Rendering
   1.612 +// Input Parametric:
   1.613 +// 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)
   1.614 +// Output Implicit:
   1.615 +// f(x, y, w) = f(P) = K^2 - LM
   1.616 +// K = dot(k, P), L = dot(l, P), M = dot(m, P)
   1.617 +// k, l, m are calculated in function GrPathUtils::getConicKLM
   1.618 +void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kVertsPerQuad],
   1.619 +                      const SkScalar weight) {
   1.620 +    SkScalar klm[9];
   1.621 +
   1.622 +    GrPathUtils::getConicKLM(p, weight, klm);
   1.623 +
   1.624 +    for (int i = 0; i < kVertsPerQuad; ++i) {
   1.625 +        const SkPoint pnt = verts[i].fPos;
   1.626 +        verts[i].fConic.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2];
   1.627 +        verts[i].fConic.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5];
   1.628 +        verts[i].fConic.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8];
   1.629 +    }
   1.630 +}
   1.631 +
   1.632 +void add_conics(const SkPoint p[3],
   1.633 +                const SkScalar weight,
   1.634 +                const SkMatrix* toDevice,
   1.635 +                const SkMatrix* toSrc,
   1.636 +                BezierVertex** vert,
   1.637 +                SkRect* devBounds) {
   1.638 +    bloat_quad(p, toDevice, toSrc, *vert, devBounds);
   1.639 +    set_conic_coeffs(p, *vert, weight);
   1.640 +    *vert += kVertsPerQuad;
   1.641 +}
   1.642 +
   1.643 +void add_quads(const SkPoint p[3],
   1.644 +               int subdiv,
   1.645 +               const SkMatrix* toDevice,
   1.646 +               const SkMatrix* toSrc,
   1.647 +               BezierVertex** vert,
   1.648 +               SkRect* devBounds) {
   1.649 +    SkASSERT(subdiv >= 0);
   1.650 +    if (subdiv) {
   1.651 +        SkPoint newP[5];
   1.652 +        SkChopQuadAtHalf(p, newP);
   1.653 +        add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert, devBounds);
   1.654 +        add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert, devBounds);
   1.655 +    } else {
   1.656 +        bloat_quad(p, toDevice, toSrc, *vert, devBounds);
   1.657 +        set_uv_quad(p, *vert);
   1.658 +        *vert += kVertsPerQuad;
   1.659 +    }
   1.660 +}
   1.661 +
   1.662 +void add_line(const SkPoint p[2],
   1.663 +              const SkMatrix* toSrc,
   1.664 +              GrColor coverage,
   1.665 +              LineVertex** vert) {
   1.666 +    const SkPoint& a = p[0];
   1.667 +    const SkPoint& b = p[1];
   1.668 +
   1.669 +    SkVector ortho, vec = b;
   1.670 +    vec -= a;
   1.671 +
   1.672 +    if (vec.setLength(SK_ScalarHalf)) {
   1.673 +        // Create a vector orthogonal to 'vec' and of unit length
   1.674 +        ortho.fX = 2.0f * vec.fY;
   1.675 +        ortho.fY = -2.0f * vec.fX;
   1.676 +
   1.677 +        (*vert)[0].fPos = a;
   1.678 +        (*vert)[0].fCoverage = coverage;
   1.679 +        (*vert)[1].fPos = b;
   1.680 +        (*vert)[1].fCoverage = coverage;
   1.681 +        (*vert)[2].fPos = a - vec + ortho;
   1.682 +        (*vert)[2].fCoverage = 0;
   1.683 +        (*vert)[3].fPos = b + vec + ortho;
   1.684 +        (*vert)[3].fCoverage = 0;
   1.685 +        (*vert)[4].fPos = a - vec - ortho;
   1.686 +        (*vert)[4].fCoverage = 0;
   1.687 +        (*vert)[5].fPos = b + vec - ortho;
   1.688 +        (*vert)[5].fCoverage = 0;
   1.689 +
   1.690 +        if (NULL != toSrc) {
   1.691 +            toSrc->mapPointsWithStride(&(*vert)->fPos,
   1.692 +                                       sizeof(LineVertex),
   1.693 +                                       kVertsPerLineSeg);
   1.694 +        }
   1.695 +    } else {
   1.696 +        // just make it degenerate and likely offscreen
   1.697 +        for (int i = 0; i < kVertsPerLineSeg; ++i) {
   1.698 +            (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax);
   1.699 +        }
   1.700 +    }
   1.701 +
   1.702 +    *vert += kVertsPerLineSeg;
   1.703 +}
   1.704 +
   1.705 +}
   1.706 +
   1.707 +///////////////////////////////////////////////////////////////////////////////
   1.708 +
   1.709 +namespace {
   1.710 +
   1.711 +// position + edge
   1.712 +extern const GrVertexAttrib gHairlineBezierAttribs[] = {
   1.713 +    {kVec2f_GrVertexAttribType, 0,                  kPosition_GrVertexAttribBinding},
   1.714 +    {kVec4f_GrVertexAttribType, sizeof(GrPoint),    kEffect_GrVertexAttribBinding}
   1.715 +};
   1.716 +
   1.717 +// position + coverage
   1.718 +extern const GrVertexAttrib gHairlineLineAttribs[] = {
   1.719 +    {kVec2f_GrVertexAttribType,  0,               kPosition_GrVertexAttribBinding},
   1.720 +    {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding},
   1.721 +};
   1.722 +
   1.723 +};
   1.724 +
   1.725 +bool GrAAHairLinePathRenderer::createLineGeom(const SkPath& path,
   1.726 +                                              GrDrawTarget* target,
   1.727 +                                              const PtArray& lines,
   1.728 +                                              int lineCnt,
   1.729 +                                              GrDrawTarget::AutoReleaseGeometry* arg,
   1.730 +                                              SkRect* devBounds) {
   1.731 +    GrDrawState* drawState = target->drawState();
   1.732 +
   1.733 +    const SkMatrix& viewM = drawState->getViewMatrix();
   1.734 +
   1.735 +    int vertCnt = kVertsPerLineSeg * lineCnt;
   1.736 +
   1.737 +    drawState->setVertexAttribs<gHairlineLineAttribs>(SK_ARRAY_COUNT(gHairlineLineAttribs));
   1.738 +    SkASSERT(sizeof(LineVertex) == drawState->getVertexSize());
   1.739 +
   1.740 +    if (!arg->set(target, vertCnt, 0)) {
   1.741 +        return false;
   1.742 +    }
   1.743 +
   1.744 +    LineVertex* verts = reinterpret_cast<LineVertex*>(arg->vertices());
   1.745 +
   1.746 +    const SkMatrix* toSrc = NULL;
   1.747 +    SkMatrix ivm;
   1.748 +
   1.749 +    if (viewM.hasPerspective()) {
   1.750 +        if (viewM.invert(&ivm)) {
   1.751 +            toSrc = &ivm;
   1.752 +        }
   1.753 +    }
   1.754 +    devBounds->set(lines.begin(), lines.count());
   1.755 +    for (int i = 0; i < lineCnt; ++i) {
   1.756 +        add_line(&lines[2*i], toSrc, drawState->getCoverageColor(), &verts);
   1.757 +    }
   1.758 +    // All the verts computed by add_line are within sqrt(1^2 + 0.5^2) of the end points.
   1.759 +    static const SkScalar kSqrtOfOneAndAQuarter = 1.118f;
   1.760 +    // Add a little extra to account for vector normalization precision.
   1.761 +    static const SkScalar kOutset = kSqrtOfOneAndAQuarter + SK_Scalar1 / 20;
   1.762 +    devBounds->outset(kOutset, kOutset);
   1.763 +
   1.764 +    return true;
   1.765 +}
   1.766 +
   1.767 +bool GrAAHairLinePathRenderer::createBezierGeom(
   1.768 +                                          const SkPath& path,
   1.769 +                                          GrDrawTarget* target,
   1.770 +                                          const PtArray& quads,
   1.771 +                                          int quadCnt,
   1.772 +                                          const PtArray& conics,
   1.773 +                                          int conicCnt,
   1.774 +                                          const IntArray& qSubdivs,
   1.775 +                                          const FloatArray& cWeights,
   1.776 +                                          GrDrawTarget::AutoReleaseGeometry* arg,
   1.777 +                                          SkRect* devBounds) {
   1.778 +    GrDrawState* drawState = target->drawState();
   1.779 +
   1.780 +    const SkMatrix& viewM = drawState->getViewMatrix();
   1.781 +
   1.782 +    int vertCnt = kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt;
   1.783 +
   1.784 +    target->drawState()->setVertexAttribs<gHairlineBezierAttribs>(SK_ARRAY_COUNT(gHairlineBezierAttribs));
   1.785 +    SkASSERT(sizeof(BezierVertex) == target->getDrawState().getVertexSize());
   1.786 +
   1.787 +    if (!arg->set(target, vertCnt, 0)) {
   1.788 +        return false;
   1.789 +    }
   1.790 +
   1.791 +    BezierVertex* verts = reinterpret_cast<BezierVertex*>(arg->vertices());
   1.792 +
   1.793 +    const SkMatrix* toDevice = NULL;
   1.794 +    const SkMatrix* toSrc = NULL;
   1.795 +    SkMatrix ivm;
   1.796 +
   1.797 +    if (viewM.hasPerspective()) {
   1.798 +        if (viewM.invert(&ivm)) {
   1.799 +            toDevice = &viewM;
   1.800 +            toSrc = &ivm;
   1.801 +        }
   1.802 +    }
   1.803 +
   1.804 +    // Seed the dev bounds with some pts known to be inside. Each quad and conic grows the bounding
   1.805 +    // box to include its vertices.
   1.806 +    SkPoint seedPts[2];
   1.807 +    if (quadCnt) {
   1.808 +        seedPts[0] = quads[0];
   1.809 +        seedPts[1] = quads[2];
   1.810 +    } else if (conicCnt) {
   1.811 +        seedPts[0] = conics[0];
   1.812 +        seedPts[1] = conics[2];
   1.813 +    }
   1.814 +    if (NULL != toDevice) {
   1.815 +        toDevice->mapPoints(seedPts, 2);
   1.816 +    }
   1.817 +    devBounds->set(seedPts[0], seedPts[1]);
   1.818 +
   1.819 +    int unsubdivQuadCnt = quads.count() / 3;
   1.820 +    for (int i = 0; i < unsubdivQuadCnt; ++i) {
   1.821 +        SkASSERT(qSubdivs[i] >= 0);
   1.822 +        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds);
   1.823 +    }
   1.824 +
   1.825 +    // Start Conics
   1.826 +    for (int i = 0; i < conicCnt; ++i) {
   1.827 +        add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts, devBounds);
   1.828 +    }
   1.829 +    return true;
   1.830 +}
   1.831 +
   1.832 +bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
   1.833 +                                           const SkStrokeRec& stroke,
   1.834 +                                           const GrDrawTarget* target,
   1.835 +                                           bool antiAlias) const {
   1.836 +    if (!antiAlias) {
   1.837 +        return false;
   1.838 +    }
   1.839 +
   1.840 +    if (!IsStrokeHairlineOrEquivalent(stroke,
   1.841 +                                      target->getDrawState().getViewMatrix(),
   1.842 +                                      NULL)) {
   1.843 +        return false;
   1.844 +    }
   1.845 +
   1.846 +    if (SkPath::kLine_SegmentMask == path.getSegmentMasks() ||
   1.847 +        target->caps()->shaderDerivativeSupport()) {
   1.848 +        return true;
   1.849 +    }
   1.850 +    return false;
   1.851 +}
   1.852 +
   1.853 +template <class VertexType>
   1.854 +bool check_bounds(GrDrawState* drawState, const SkRect& devBounds, void* vertices, int vCount)
   1.855 +{
   1.856 +    SkRect tolDevBounds = devBounds;
   1.857 +    // The bounds ought to be tight, but in perspective the below code runs the verts
   1.858 +    // through the view matrix to get back to dev coords, which can introduce imprecision.
   1.859 +    if (drawState->getViewMatrix().hasPerspective()) {
   1.860 +        tolDevBounds.outset(SK_Scalar1 / 1000, SK_Scalar1 / 1000);
   1.861 +    } else {
   1.862 +        // Non-persp matrices cause this path renderer to draw in device space.
   1.863 +        SkASSERT(drawState->getViewMatrix().isIdentity());
   1.864 +    }
   1.865 +    SkRect actualBounds;
   1.866 +
   1.867 +    VertexType* verts = reinterpret_cast<VertexType*>(vertices);
   1.868 +    bool first = true;
   1.869 +    for (int i = 0; i < vCount; ++i) {
   1.870 +        SkPoint pos = verts[i].fPos;
   1.871 +        // This is a hack to workaround the fact that we move some degenerate segments offscreen.
   1.872 +        if (SK_ScalarMax == pos.fX) {
   1.873 +            continue;
   1.874 +        }
   1.875 +        drawState->getViewMatrix().mapPoints(&pos, 1);
   1.876 +        if (first) {
   1.877 +            actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY);
   1.878 +            first = false;
   1.879 +        } else {
   1.880 +            actualBounds.growToInclude(pos.fX, pos.fY);
   1.881 +        }
   1.882 +    }
   1.883 +    if (!first) {
   1.884 +        return tolDevBounds.contains(actualBounds);
   1.885 +    }
   1.886 +
   1.887 +    return true;
   1.888 +}
   1.889 +
   1.890 +bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
   1.891 +                                          const SkStrokeRec& stroke,
   1.892 +                                          GrDrawTarget* target,
   1.893 +                                          bool antiAlias) {
   1.894 +    GrDrawState* drawState = target->drawState();
   1.895 +
   1.896 +    SkScalar hairlineCoverage;
   1.897 +    if (IsStrokeHairlineOrEquivalent(stroke,
   1.898 +                                     target->getDrawState().getViewMatrix(),
   1.899 +                                     &hairlineCoverage)) {
   1.900 +        uint8_t newCoverage = SkScalarRoundToInt(hairlineCoverage *
   1.901 +                                                 target->getDrawState().getCoverage());
   1.902 +        target->drawState()->setCoverage(newCoverage);
   1.903 +    }
   1.904 +
   1.905 +    SkIRect devClipBounds;
   1.906 +    target->getClip()->getConservativeBounds(drawState->getRenderTarget(), &devClipBounds);
   1.907 +
   1.908 +    int lineCnt;
   1.909 +    int quadCnt;
   1.910 +    int conicCnt;
   1.911 +    PREALLOC_PTARRAY(128) lines;
   1.912 +    PREALLOC_PTARRAY(128) quads;
   1.913 +    PREALLOC_PTARRAY(128) conics;
   1.914 +    IntArray qSubdivs;
   1.915 +    FloatArray cWeights;
   1.916 +    quadCnt = generate_lines_and_quads(path, drawState->getViewMatrix(), devClipBounds,
   1.917 +                                       &lines, &quads, &conics, &qSubdivs, &cWeights);
   1.918 +    lineCnt = lines.count() / 2;
   1.919 +    conicCnt = conics.count() / 3;
   1.920 +
   1.921 +    // do lines first
   1.922 +    if (lineCnt) {
   1.923 +        GrDrawTarget::AutoReleaseGeometry arg;
   1.924 +        SkRect devBounds;
   1.925 +
   1.926 +        if (!this->createLineGeom(path,
   1.927 +                                  target,
   1.928 +                                  lines,
   1.929 +                                  lineCnt,
   1.930 +                                  &arg,
   1.931 +                                  &devBounds)) {
   1.932 +            return false;
   1.933 +        }
   1.934 +
   1.935 +        GrDrawTarget::AutoStateRestore asr;
   1.936 +
   1.937 +        // createLineGeom transforms the geometry to device space when the matrix does not have
   1.938 +        // perspective.
   1.939 +        if (target->getDrawState().getViewMatrix().hasPerspective()) {
   1.940 +            asr.set(target, GrDrawTarget::kPreserve_ASRInit);
   1.941 +        } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
   1.942 +            return false;
   1.943 +        }
   1.944 +        GrDrawState* drawState = target->drawState();
   1.945 +
   1.946 +        // Check devBounds
   1.947 +        SkASSERT(check_bounds<LineVertex>(drawState, devBounds, arg.vertices(),
   1.948 +                                          kVertsPerLineSeg * lineCnt));
   1.949 +
   1.950 +        {
   1.951 +            GrDrawState::AutoRestoreEffects are(drawState);
   1.952 +            target->setIndexSourceToBuffer(fLinesIndexBuffer);
   1.953 +            int lines = 0;
   1.954 +            while (lines < lineCnt) {
   1.955 +                int n = GrMin(lineCnt - lines, kNumLineSegsInIdxBuffer);
   1.956 +                target->drawIndexed(kTriangles_GrPrimitiveType,
   1.957 +                                    kVertsPerLineSeg*lines,     // startV
   1.958 +                                    0,                          // startI
   1.959 +                                    kVertsPerLineSeg*n,         // vCount
   1.960 +                                    kIdxsPerLineSeg*n,          // iCount
   1.961 +                                    &devBounds);
   1.962 +                lines += n;
   1.963 +            }
   1.964 +        }
   1.965 +    }
   1.966 +
   1.967 +    // then quadratics/conics
   1.968 +    if (quadCnt || conicCnt) {
   1.969 +        GrDrawTarget::AutoReleaseGeometry arg;
   1.970 +        SkRect devBounds;
   1.971 +
   1.972 +        if (!this->createBezierGeom(path,
   1.973 +                                    target,
   1.974 +                                    quads,
   1.975 +                                    quadCnt,
   1.976 +                                    conics,
   1.977 +                                    conicCnt,
   1.978 +                                    qSubdivs,
   1.979 +                                    cWeights,
   1.980 +                                    &arg,
   1.981 +                                    &devBounds)) {
   1.982 +            return false;
   1.983 +        }
   1.984 +
   1.985 +        GrDrawTarget::AutoStateRestore asr;
   1.986 +
   1.987 +        // createGeom transforms the geometry to device space when the matrix does not have
   1.988 +        // perspective.
   1.989 +        if (target->getDrawState().getViewMatrix().hasPerspective()) {
   1.990 +            asr.set(target, GrDrawTarget::kPreserve_ASRInit);
   1.991 +        } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
   1.992 +            return false;
   1.993 +        }
   1.994 +        GrDrawState* drawState = target->drawState();
   1.995 +
   1.996 +        static const int kEdgeAttrIndex = 1;
   1.997 +
   1.998 +        // Check devBounds
   1.999 +        SkASSERT(check_bounds<BezierVertex>(drawState, devBounds, arg.vertices(),
  1.1000 +                                            kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt));
  1.1001 +
  1.1002 +        if (quadCnt > 0) {
  1.1003 +            GrEffectRef* hairQuadEffect = GrQuadEffect::Create(kHairlineAA_GrEffectEdgeType,
  1.1004 +                                                               *target->caps());
  1.1005 +            SkASSERT(NULL != hairQuadEffect);
  1.1006 +            GrDrawState::AutoRestoreEffects are(drawState);
  1.1007 +            target->setIndexSourceToBuffer(fQuadsIndexBuffer);
  1.1008 +            drawState->addCoverageEffect(hairQuadEffect, kEdgeAttrIndex)->unref();
  1.1009 +            int quads = 0;
  1.1010 +            while (quads < quadCnt) {
  1.1011 +                int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
  1.1012 +                target->drawIndexed(kTriangles_GrPrimitiveType,
  1.1013 +                                    kVertsPerQuad*quads,               // startV
  1.1014 +                                    0,                                 // startI
  1.1015 +                                    kVertsPerQuad*n,                   // vCount
  1.1016 +                                    kIdxsPerQuad*n,                    // iCount
  1.1017 +                                    &devBounds);
  1.1018 +                quads += n;
  1.1019 +            }
  1.1020 +        }
  1.1021 +
  1.1022 +        if (conicCnt > 0) {
  1.1023 +            GrDrawState::AutoRestoreEffects are(drawState);
  1.1024 +            GrEffectRef* hairConicEffect = GrConicEffect::Create(kHairlineAA_GrEffectEdgeType,
  1.1025 +                                                                 *target->caps());
  1.1026 +            SkASSERT(NULL != hairConicEffect);
  1.1027 +            drawState->addCoverageEffect(hairConicEffect, 1, 2)->unref();
  1.1028 +            int conics = 0;
  1.1029 +            while (conics < conicCnt) {
  1.1030 +                int n = GrMin(conicCnt - conics, kNumQuadsInIdxBuffer);
  1.1031 +                target->drawIndexed(kTriangles_GrPrimitiveType,
  1.1032 +                                    kVertsPerQuad*(quadCnt + conics),  // startV
  1.1033 +                                    0,                                 // startI
  1.1034 +                                    kVertsPerQuad*n,                   // vCount
  1.1035 +                                    kIdxsPerQuad*n,                    // iCount
  1.1036 +                                    &devBounds);
  1.1037 +                conics += n;
  1.1038 +            }
  1.1039 +        }
  1.1040 +    }
  1.1041 +
  1.1042 +    target->resetIndexSource();
  1.1043 +
  1.1044 +    return true;
  1.1045 +}

mercurial