static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
                                const SkRect& bounds) {
    REPORTER_ASSERT(reporter, p.isConvex());
    REPORTER_ASSERT(reporter, p.getBounds() == bounds);

    SkPath p2(p);
    REPORTER_ASSERT(reporter, p2.isConvex());
    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);

    SkPath other;
    other.swap(p2);
    REPORTER_ASSERT(reporter, other.isConvex());
    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
}
Example #2
0
    virtual void onDraw(int loops, SkCanvas* canvas) {

        SkPaint paint;
        this->setupPaint(&paint);

        for (int i = 0; i < loops; ++i) {
            // jostle the clip regions each time to prevent caching
            fClipRect.offset((i % 2) == 0 ? SkIntToScalar(10) : SkIntToScalar(-10), 0);
            fClipPath.reset();
            fClipPath.addRoundRect(fClipRect,
                                   SkIntToScalar(5), SkIntToScalar(5));
            SkASSERT(fClipPath.isConvex());

            canvas->save();
#if 1
            if (fDoPath) {
                canvas->clipPath(fClipPath, kReplace_SkClipOp, fDoAA);
            } else {
                canvas->clipRect(fClipRect, kReplace_SkClipOp, fDoAA);
            }

            canvas->drawRect(fDrawRect, paint);
#else
            // this path tests out directly draw the clip primitive
            // use it to comparing just drawing the clip vs. drawing using
            // the clip
            if (fDoPath) {
                canvas->drawPath(fClipPath, paint);
            } else {
                canvas->drawRect(fClipRect, paint);
            }
#endif
            canvas->restore();
        }
    }
bool GrAndroidPathRenderer::canDrawPath(const SkPath& path,
                                        const SkStrokeRec& stroke,
                                        const GrDrawTarget* target,
                                        bool antiAlias) const {
    return ((stroke.isFillStyle() || stroke.getStyle() == SkStrokeRec::kStroke_Style)
             && !path.isInverseFillType() && path.isConvex());
}
bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
                                         const GrPipelineBuilder*,
                                         const SkMatrix& viewMatrix,
                                         const SkPath& path,
                                         const GrStrokeInfo& stroke,
                                         bool antiAlias) const {
    return (target->caps()->shaderCaps()->shaderDerivativeSupport() && antiAlias &&
            stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
}
static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
#if STENCIL_OFF
    return true;
#else
    if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
        return path.isConvex();
    }
    return false;
#endif
}
Example #6
0
static inline bool single_pass_path(const SkPath& path, GrPathFill fill) {
#if STENCIL_OFF
    return true;
#else
    if (kEvenOdd_GrPathFill == fill || kWinding_GrPathFill == fill) {
        return path.isConvex();
    }
    return false;
#endif
}
Example #7
0
bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
                                         GrPathFill fill,
                                         const GrDrawTarget* target,
                                         bool antiAlias) const {
    if (!target->getCaps().shaderDerivativeSupport() || !antiAlias ||
        kHairLine_GrPathFill == fill || GrIsFillInverted(fill) ||
        !path.isConvex()) {
        return false;
    }  else {
        return true;
    }
}
Example #8
0
void draw(SkCanvas* canvas) {
    SkPaint paint;
    paint.setAntiAlias(true);
    for (auto xradius : { 0, 7, 13, 20 } ) {
        for (auto yradius : { 0, 9, 18, 40 } ) {
            SkPath path;
            path.addRoundRect({10, 10, 36, 46}, xradius, yradius);
            paint.setColor(path.isRect(nullptr) ? SK_ColorRED : path.isOval(nullptr) ?
                           SK_ColorBLUE : path.isConvex() ? SK_ColorGRAY : SK_ColorGREEN);
            canvas->drawPath(path, paint);
            canvas->translate(64, 0);
        }
        canvas->translate(-256, 64);
    }
}
Example #9
0
    AAClipBench(bool doPath, bool doAA)
        : fDoPath(doPath)
        , fDoAA(doAA) {

        fName.printf("aaclip_%s_%s",
                     doPath ? "path" : "rect",
                     doAA ? "AA" : "BW");

        fClipRect.set(10.5f, 10.5f,
                      50.5f, 50.5f);
        fClipPath.addRoundRect(fClipRect, SkIntToScalar(10), SkIntToScalar(10));
        fDrawRect.set(SkIntToScalar(0), SkIntToScalar(0),
                      SkIntToScalar(100), SkIntToScalar(100));

        SkASSERT(fClipPath.isConvex());
    }
Example #10
0
// Creates a star type shape using a SkPath
static SkPath create_star() {
    static const int kNumPoints = 5;
    SkPath concavePath;
    SkPoint points[kNumPoints] = {{0, SkIntToScalar(-50)} };
    SkMatrix rot;
    rot.setRotate(SkIntToScalar(360) / kNumPoints);
    for (int i = 1; i < kNumPoints; ++i) {
        rot.mapPoints(points + i, points + i - 1, 1);
    }
    concavePath.moveTo(points[0]);
    for (int i = 0; i < kNumPoints; ++i) {
        concavePath.lineTo(points[(2 * i) % kNumPoints]);
    }
    concavePath.setFillType(SkPath::kEvenOdd_FillType);
    SkASSERT(!concavePath.isConvex());
    concavePath.close();
    return concavePath;
}
Example #11
0
    void recurse(SkCanvas* canvas,
                 int depth,
                 const SkPoint& offset) {

            canvas->save();

            SkRect temp = SkRect::MakeLTRB(0, 0,
                                           fSizes[depth].fX, fSizes[depth].fY);
            temp.offset(offset);

            SkPath path;
            path.addRoundRect(temp, SkIntToScalar(3), SkIntToScalar(3));
            SkASSERT(path.isConvex());

            canvas->clipPath(path,
                             0 == depth ? SkRegion::kReplace_Op :
                                          SkRegion::kIntersect_Op,
                             fDoAA);

            if (kNestingDepth == depth) {
                // we only draw the draw rect at the lowest nesting level
                SkPaint paint;
                paint.setColor(0xff000000 | fRandom.nextU());
                canvas->drawRect(fDrawRect, paint);
            } else {
                SkPoint childOffset = offset;
                this->recurse(canvas, depth+1, childOffset);

                childOffset += fSizes[depth+1];
                this->recurse(canvas, depth+1, childOffset);

                childOffset.fX = offset.fX + fSizes[depth+1].fX;
                childOffset.fY = offset.fY;
                this->recurse(canvas, depth+1, childOffset);

                childOffset.fX = offset.fX;
                childOffset.fY = offset.fY + fSizes[depth+1].fY;
                this->recurse(canvas, depth+1, childOffset);
            }

            canvas->restore();
    }
bool GrAALinearizingConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
                                                    const GrPipelineBuilder*,
                                                    const SkMatrix& viewMatrix,
                                                    const SkPath& path,
                                                    const GrStrokeInfo& stroke,
                                                    bool antiAlias) const {
    if (!antiAlias) {
        return false;
    }
    if (path.isInverseFillType()) {
        return false;
    }
    if (!path.isConvex()) {
        return false;
    }
    if (stroke.getStyle() == SkStrokeRec::kStroke_Style) {
        return viewMatrix.isSimilarity() && stroke.getWidth() >= 1.0f && 
                stroke.getWidth() <= kMaxStrokeWidth && !stroke.isDashed() && 
                SkPathPriv::LastVerbIsClose(path) && stroke.getJoin() != SkPaint::Join::kRound_Join;
    }
    return stroke.getStyle() == SkStrokeRec::kFill_Style;
}
Example #13
0
static GrConvexHint getConvexHint(const SkPath& path) {
    return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
}
Example #14
0
// clipRect has not been shifted up
void sk_fill_path(const SkPath& path, const SkIRect& clipRect, SkBlitter* blitter,
                  int start_y, int stop_y, int shiftEdgesUp, bool pathContainedInClip) {
    SkASSERT(blitter);

    SkIRect shiftedClip = clipRect;
    shiftedClip.fLeft = SkLeftShift(shiftedClip.fLeft, shiftEdgesUp);
    shiftedClip.fRight = SkLeftShift(shiftedClip.fRight, shiftEdgesUp);
    shiftedClip.fTop = SkLeftShift(shiftedClip.fTop, shiftEdgesUp);
    shiftedClip.fBottom = SkLeftShift(shiftedClip.fBottom, shiftEdgesUp);

    SkEdgeBuilder builder;
    int count = builder.build_edges(path, &shiftedClip, shiftEdgesUp, pathContainedInClip);
    SkEdge** list = builder.edgeList();

    if (0 == count) {
        if (path.isInverseFillType()) {
            /*
             *  Since we are in inverse-fill, our caller has already drawn above
             *  our top (start_y) and will draw below our bottom (stop_y). Thus
             *  we need to restrict our drawing to the intersection of the clip
             *  and those two limits.
             */
            SkIRect rect = clipRect;
            if (rect.fTop < start_y) {
                rect.fTop = start_y;
            }
            if (rect.fBottom > stop_y) {
                rect.fBottom = stop_y;
            }
            if (!rect.isEmpty()) {
                blitter->blitRect(rect.fLeft << shiftEdgesUp,
                                  rect.fTop << shiftEdgesUp,
                                  rect.width() << shiftEdgesUp,
                                  rect.height() << shiftEdgesUp);
            }
        }
        return;
    }

    SkEdge headEdge, tailEdge, *last;
    // this returns the first and last edge after they're sorted into a dlink list
    SkEdge* edge = sort_edges(list, count, &last);

    headEdge.fPrev = nullptr;
    headEdge.fNext = edge;
    headEdge.fFirstY = kEDGE_HEAD_Y;
    headEdge.fX = SK_MinS32;
    edge->fPrev = &headEdge;

    tailEdge.fPrev = last;
    tailEdge.fNext = nullptr;
    tailEdge.fFirstY = kEDGE_TAIL_Y;
    last->fNext = &tailEdge;

    // now edge is the head of the sorted linklist

    start_y = SkLeftShift(start_y, shiftEdgesUp);
    stop_y = SkLeftShift(stop_y, shiftEdgesUp);
    if (!pathContainedInClip && start_y < shiftedClip.fTop) {
        start_y = shiftedClip.fTop;
    }
    if (!pathContainedInClip && stop_y > shiftedClip.fBottom) {
        stop_y = shiftedClip.fBottom;
    }

    InverseBlitter  ib;
    PrePostProc     proc = nullptr;

    if (path.isInverseFillType()) {
        ib.setBlitter(blitter, clipRect, shiftEdgesUp);
        blitter = &ib;
        proc = PrePostInverseBlitterProc;
    }

    // count >= 2 is required as the convex walker does not handle missing right edges
    if (path.isConvex() && (nullptr == proc) && count >= 2) {
        walk_simple_edges(&headEdge, blitter, start_y, stop_y);
    } else {
        walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc,
                shiftedClip.right());
    }
}
Example #15
0
/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
   paths with union ops could be locally resolved and still improve over doing the
   ops one at a time. */
bool SkOpBuilder::resolve(SkPath* result) {
    SkPath original = *result;
    int count = fOps.count();
    bool allUnion = true;
    SkPathPriv::FirstDirection firstDir = SkPathPriv::kUnknown_FirstDirection;
    for (int index = 0; index < count; ++index) {
        SkPath* test = &fPathRefs[index];
        if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
            allUnion = false;
            break;
        }
        // If all paths are convex, track direction, reversing as needed.
        if (test->isConvex()) {
            SkPathPriv::FirstDirection dir;
            if (!SkPathPriv::CheapComputeFirstDirection(*test, &dir)) {
                allUnion = false;
                break;
            }
            if (firstDir == SkPathPriv::kUnknown_FirstDirection) {
                firstDir = dir;
            } else if (firstDir != dir) {
                SkPath temp;
                temp.reverseAddPath(*test);
                *test = temp;
            }
            continue;
        }
        // If the path is not convex but its bounds do not intersect the others, simplify is enough.
        const SkRect& testBounds = test->getBounds();
        for (int inner = 0; inner < index; ++inner) {
            // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
            if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
                allUnion = false;
                break;
            }
        }
    }
    if (!allUnion) {
        *result = fPathRefs[0];
        for (int index = 1; index < count; ++index) {
            if (!Op(*result, fPathRefs[index], fOps[index], result)) {
                reset();
                *result = original;
                return false;
            }
        }
        reset();
        return true;
    }
    SkPath sum;
    for (int index = 0; index < count; ++index) {
        if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
            reset();
            *result = original;
            return false;
        }
        if (!fPathRefs[index].isEmpty()) {
            // convert the even odd result back to winding form before accumulating it
            if (!FixWinding(&fPathRefs[index])) {
                *result = original;
                return false;
            }
            sum.addPath(fPathRefs[index]);
        }
    }
    reset();
    bool success = Simplify(sum, result);
    if (!success) {
        *result = original;
    }
    return success;
}
bool GrTesselatedPathRenderer::onDrawPath(const SkPath& path,
                                          GrPathFill fill,
                                          const GrVec* translate,
                                          GrDrawTarget* target,
                                          GrDrawState::StageMask stageMask,
                                          bool antiAlias) {

    GrDrawTarget::AutoStateRestore asr(target);
    GrDrawState* drawState = target->drawState();
    // face culling doesn't make sense here
    GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());

    GrMatrix viewM = drawState->getViewMatrix();

    GrScalar tol = GR_Scalar1;
    tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
    GrScalar tolSqd = GrMul(tol, tol);

    int subpathCnt;
    int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);

    GrVertexLayout layout = 0;
    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
        if ((1 << s) & stageMask) {
            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
        }
    }

    bool inverted = GrIsFillInverted(fill);
    if (inverted) {
        maxPts += 4;
        subpathCnt++;
    }
    if (maxPts > USHRT_MAX) {
        return false;
    }
    SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
    GrPoint* base = baseMem;
    GrPoint* vert = base;
    GrPoint* subpathBase = base;

    SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);

    GrPoint pts[4];
    SkPath::Iter iter(path, false);

    bool first = true;
    int subpath = 0;

    for (;;) {
        switch (iter.next(pts)) {
            case kMove_PathCmd:
                if (!first) {
                    subpathVertCount[subpath] = vert-subpathBase;
                    subpathBase = vert;
                    ++subpath;
                }
                *vert = pts[0];
                vert++;
                break;
            case kLine_PathCmd:
                *vert = pts[1];
                vert++;
                break;
            case kQuadratic_PathCmd: {
                GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
                                                     tolSqd, &vert,
                                                     GrPathUtils::quadraticPointCount(pts, tol));
                break;
            }
            case kCubic_PathCmd: {
                GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
                                                 tolSqd, &vert,
                                                 GrPathUtils::cubicPointCount(pts, tol));
                break;
            }
            case kClose_PathCmd:
                break;
            case kEnd_PathCmd:
                subpathVertCount[subpath] = vert-subpathBase;
                ++subpath; // this could be only in debug
                goto FINISHED;
        }
        first = false;
    }
FINISHED:
    if (NULL != translate && 0 != translate->fX && 0 != translate->fY) {
        for (int i = 0; i < vert - base; i++) {
            base[i].offset(translate->fX, translate->fY);
        }
    }

    if (inverted) {
        GrRect bounds;
        GrAssert(NULL != drawState->getRenderTarget());
        bounds.setLTRB(0, 0,
                       GrIntToScalar(drawState->getRenderTarget()->width()),
                       GrIntToScalar(drawState->getRenderTarget()->height()));
        GrMatrix vmi;
        if (drawState->getViewInverse(&vmi)) {
            vmi.mapRect(&bounds);
        }
        *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
        *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
        *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
        *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
        subpathVertCount[subpath++] = 4;
    }

    GrAssert(subpath == subpathCnt);
    GrAssert((vert - base) <= maxPts);

    size_t count = vert - base;

    if (count < 3) {
        return true;
    }

    if (subpathCnt == 1 && !inverted && path.isConvex()) {
        if (antiAlias) {
            GrEdgeArray edges;
            GrMatrix inverse, matrix = drawState->getViewMatrix();
            drawState->getViewInverse(&inverse);

            count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
            size_t maxEdges = target->getMaxEdges();
            if (count == 0) {
                return true;
            }
            if (count <= maxEdges) {
                // All edges fit; upload all edges and draw all verts as a fan
                target->setVertexSourceToArray(layout, base, count);
                drawState->setEdgeAAData(&edges[0], count);
                target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
            } else {
                // Upload "maxEdges" edges and verts at a time, and draw as
                // separate fans
                for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
                    edges[i] = edges[0];
                    base[i] = base[0];
                    int size = GR_CT_MIN(count - i, maxEdges);
                    target->setVertexSourceToArray(layout, &base[i], size);
                    drawState->setEdgeAAData(&edges[i], size);
                    target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
                }
            }
            drawState->setEdgeAAData(NULL, 0);
        } else {
            target->setVertexSourceToArray(layout, base, count);
            target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
        }
        return true;
    }

    if (antiAlias) {
        // Run the tesselator once to get the boundaries.
        GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
        btess.addVertices(base, subpathVertCount, subpathCnt);

        GrMatrix inverse, matrix = drawState->getViewMatrix();
        if (!drawState->getViewInverse(&inverse)) {
            return false;
        }

        if (btess.vertices().count() > USHRT_MAX) {
            return false;
        }

        // Inflate the boundary, and run the tesselator again to generate
        // interior polys.
        const GrPointArray& contourPoints = btess.contourPoints();
        const GrIndexArray& contours = btess.contours();
        GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);

        size_t i = 0;
        Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
        for (int contour = 0; contour < contours.count(); ++contour) {
            int count = contours[contour];
            GrEdgeArray edges;
            int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
            Sk_gluTessBeginContour(ptess.tess());
            for (int j = 0; j < newCount; j++) {
                ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
            }
            i += count;
            Sk_gluTessEndContour(ptess.tess());
        }

        Sk_gluTessEndPolygon(ptess.tess());

        if (ptess.vertices().count() > USHRT_MAX) {
            return false;
        }

        // Draw the resulting polys and upload their edge data.
        drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit);
        const GrPointArray& vertices = ptess.vertices();
        const GrIndexArray& indices = ptess.indices();
        const GrDrawState::Edge* edges = ptess.edges();
        GR_DEBUGASSERT(indices.count() % 3 == 0);
        for (int i = 0; i < indices.count(); i += 3) {
            GrPoint tri_verts[3];
            int index0 = indices[i];
            int index1 = indices[i + 1];
            int index2 = indices[i + 2];
            tri_verts[0] = vertices[index0];
            tri_verts[1] = vertices[index1];
            tri_verts[2] = vertices[index2];
            GrDrawState::Edge tri_edges[6];
            int t = 0;
            const GrDrawState::Edge& edge0 = edges[index0 * 2];
            const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
            const GrDrawState::Edge& edge2 = edges[index1 * 2];
            const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
            const GrDrawState::Edge& edge4 = edges[index2 * 2];
            const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
            if (validEdge(edge0) && validEdge(edge1)) {
                tri_edges[t++] = edge0;
                tri_edges[t++] = edge1;
            }
            if (validEdge(edge2) && validEdge(edge3)) {
                tri_edges[t++] = edge2;
                tri_edges[t++] = edge3;
            }
            if (validEdge(edge4) && validEdge(edge5)) {
                tri_edges[t++] = edge4;
                tri_edges[t++] = edge5;
            }
            drawState->setEdgeAAData(&tri_edges[0], t);
            target->setVertexSourceToArray(layout, &tri_verts[0], 3);
            target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
        }
        drawState->setEdgeAAData(NULL, 0);
        drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit);
        return true;
    }

    GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
    ptess.addVertices(base, subpathVertCount, subpathCnt);
    const GrPointArray& vertices = ptess.vertices();
    const GrIndexArray& indices = ptess.indices();
    if (indices.count() > 0) {
        target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
        target->setIndexSourceToArray(indices.begin(), indices.count());
        target->drawIndexed(kTriangles_PrimitiveType,
                            0,
                            0,
                            vertices.count(),
                            indices.count());
    }
    return true;
}
Example #17
0
static void test_gpu_veto(skiatest::Reporter* reporter) {
    SkPictureRecorder recorder;

    SkCanvas* canvas = recorder.beginRecording(100, 100);
    {
        SkPath path;
        path.moveTo(0, 0);
        path.lineTo(50, 50);

        SkScalar intervals[] = { 1.0f, 1.0f };
        sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));

        SkPaint paint;
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setPathEffect(dash);

        for (int i = 0; i < 50; ++i) {
            canvas->drawPath(path, paint);
        }
    }
    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
    // path effects currently render an SkPicture undesireable for GPU rendering

    const char *reason = nullptr;
    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr, &reason));
    REPORTER_ASSERT(reporter, reason);

    canvas = recorder.beginRecording(100, 100);
    {
        SkPath path;

        path.moveTo(0, 0);
        path.lineTo(0, 50);
        path.lineTo(25, 25);
        path.lineTo(50, 50);
        path.lineTo(50, 0);
        path.close();
        REPORTER_ASSERT(reporter, !path.isConvex());

        SkPaint paint;
        paint.setAntiAlias(true);
        for (int i = 0; i < 50; ++i) {
            canvas->drawPath(path, paint);
        }
    }
    picture = recorder.finishRecordingAsPicture();
    // A lot of small AA concave paths should be fine for GPU rendering
    REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));

    canvas = recorder.beginRecording(100, 100);
    {
        SkPath path;

        path.moveTo(0, 0);
        path.lineTo(0, 100);
        path.lineTo(50, 50);
        path.lineTo(100, 100);
        path.lineTo(100, 0);
        path.close();
        REPORTER_ASSERT(reporter, !path.isConvex());

        SkPaint paint;
        paint.setAntiAlias(true);
        for (int i = 0; i < 50; ++i) {
            canvas->drawPath(path, paint);
        }
    }
    picture = recorder.finishRecordingAsPicture();
    // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));

    canvas = recorder.beginRecording(100, 100);
    {
        SkPath path;

        path.moveTo(0, 0);
        path.lineTo(0, 50);
        path.lineTo(25, 25);
        path.lineTo(50, 50);
        path.lineTo(50, 0);
        path.close();
        REPORTER_ASSERT(reporter, !path.isConvex());

        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(0);
        for (int i = 0; i < 50; ++i) {
            canvas->drawPath(path, paint);
        }
    }
    picture = recorder.finishRecordingAsPicture();
    // hairline stroked AA concave paths are fine for GPU rendering
    REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));

    canvas = recorder.beginRecording(100, 100);
    {
        SkPaint paint;
        SkScalar intervals [] = { 10, 20 };
        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));

        SkPoint points [2] = { { 0, 0 }, { 100, 0 } };

        for (int i = 0; i < 50; ++i) {
            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
        }
    }
    picture = recorder.finishRecordingAsPicture();
    // fast-path dashed effects are fine for GPU rendering ...
    REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));

    canvas = recorder.beginRecording(100, 100);
    {
        SkPaint paint;
        SkScalar intervals [] = { 10, 20 };
        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));

        for (int i = 0; i < 50; ++i) {
            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
        }
    }
    picture = recorder.finishRecordingAsPicture();
    // ... but only when applied to drawPoint() calls
    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));

    // Nest the previous picture inside a new one.
    canvas = recorder.beginRecording(100, 100);
    {
        canvas->drawPicture(picture.get());
    }
    picture = recorder.finishRecordingAsPicture();
    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
}
Example #18
0
static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
    if (!path.isInverseFillType()) {
        return path.isConvex();
    }
    return false;
}
bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
                                         GrPathFill fill,
                                         const GrDrawTarget* target,
                                         bool antiAlias) const {
    return staticCanDrawPath(path.isConvex(), fill, target, antiAlias);
}