diff --git a/include/gpu/GrTestUtils.h b/include/gpu/GrTestUtils.h index 6fed7e1761..91f36ea2d9 100644 --- a/include/gpu/GrTestUtils.h +++ b/include/gpu/GrTestUtils.h @@ -16,6 +16,7 @@ #include "SkRandom.h" #include "SkStrokeRec.h" +class GrStrokeInfo; class SkMatrix; class SkPath; class SkRRect; @@ -35,6 +36,7 @@ const SkRRect& TestRRectSimple(SkRandom*); const SkPath& TestPath(SkRandom*); const SkPath& TestPathConvex(SkRandom*); SkStrokeRec TestStrokeRec(SkRandom*); +GrStrokeInfo TestStrokeInfo(SkRandom*); } diff --git a/src/gpu/GrTessellatingPathRenderer.cpp b/src/gpu/GrTessellatingPathRenderer.cpp index 282efe2516..5bbc4757ff 100644 --- a/src/gpu/GrTessellatingPathRenderer.cpp +++ b/src/gpu/GrTessellatingPathRenderer.cpp @@ -1384,9 +1384,10 @@ private: } // namespace bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { - // This path renderer can draw all fill styles, but does not do antialiasing. It can do convex - // and concave paths, but we'll leave the convex ones to simpler algorithms. - return args.fStroke->isFillStyle() && !args.fAntiAlias && !args.fPath->isConvex(); + // This path renderer can draw all fill styles, all stroke styles except hairlines, but does + // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to + // simpler algorithms. + return !args.fStroke->isHairlineStyle() && !args.fAntiAlias && !args.fPath->isConvex(); } class TessellatingPathBatch : public GrBatch { @@ -1394,9 +1395,10 @@ public: static GrBatch* Create(const GrColor& color, const SkPath& path, + const GrStrokeInfo& stroke, const SkMatrix& viewMatrix, SkRect clipBounds) { - return SkNEW_ARGS(TessellatingPathBatch, (color, path, viewMatrix, clipBounds)); + return SkNEW_ARGS(TessellatingPathBatch, (color, path, stroke, viewMatrix, clipBounds)); } const char* name() const override { return "TessellatingPathBatch"; } @@ -1421,7 +1423,23 @@ public: int tessellate(GrUniqueKey* key, GrResourceProvider* resourceProvider, SkAutoTUnref& vertexBuffer) { - SkRect pathBounds = fPath.getBounds(); + SkPath path; + GrStrokeInfo stroke(fStroke); + if (stroke.isDashed()) { + if (!stroke.applyDashToPath(&path, &stroke, fPath)) { + return 0; + } + } else { + path = fPath; + } + if (!stroke.isFillStyle()) { + stroke.setResScale(SkScalarAbs(fViewMatrix.getMaxScale())); + if (!stroke.applyToPath(&path, path)) { + return 0; + } + stroke.setFillStyle(); + } + SkRect pathBounds = path.getBounds(); Comparator c; if (pathBounds.width() > pathBounds.height()) { c.sweep_lt = sweep_lt_horiz; @@ -1433,7 +1451,7 @@ public: SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, pathBounds); int contourCnt; - int maxPts = GrPathUtils::worstCasePointCount(fPath, &contourCnt, tol); + int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, tol); if (maxPts <= 0) { return 0; } @@ -1441,7 +1459,7 @@ public: SkDebugf("Path not rendered, too many verts (%d)\n", maxPts); return 0; } - SkPath::FillType fillType = fPath.getFillType(); + SkPath::FillType fillType = path.getFillType(); if (SkPath::IsInverseFillType(fillType)) { contourCnt++; } @@ -1455,7 +1473,7 @@ public: // connectivity of one Edge per Vertex (will grow for intersections). SkChunkAlloc alloc(maxPts * (3 * sizeof(Vertex) + sizeof(Edge))); bool isLinear; - path_to_contours(fPath, tol, fClipBounds, contours.get(), alloc, &isLinear); + path_to_contours(path, tol, fClipBounds, contours.get(), alloc, &isLinear); Poly* polys; polys = contours_to_polys(contours.get(), contourCnt, c, alloc); int count = 0; @@ -1503,13 +1521,15 @@ public: GrUniqueKey key; int clipBoundsSize32 = fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0; - GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32); + int strokeDataSize32 = fStroke.computeUniqueKeyFragmentData32Cnt(); + GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32 + strokeDataSize32); builder[0] = fPath.getGenerationID(); builder[1] = fPath.getFillType(); // For inverse fills, the tessellation is dependent on clip bounds. if (fPath.isInverseFillType()) { memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds)); } + fStroke.asUniqueKeyFragment(&builder[2 + clipBoundsSize32]); builder.finish(); GrResourceProvider* rp = batchTarget->resourceProvider(); SkAutoTUnref vertexBuffer(rp->findAndRefTByUniqueKey(key)); @@ -1561,10 +1581,12 @@ public: private: TessellatingPathBatch(const GrColor& color, const SkPath& path, + const GrStrokeInfo& stroke, const SkMatrix& viewMatrix, const SkRect& clipBounds) : fColor(color) , fPath(path) + , fStroke(stroke) , fViewMatrix(viewMatrix) , fClipBounds(clipBounds) { this->initClassID(); @@ -1575,6 +1597,7 @@ private: GrColor fColor; SkPath fPath; + GrStrokeInfo fStroke; SkMatrix fViewMatrix; SkRect fClipBounds; // in source space GrPipelineInfo fPipelineInfo; @@ -1596,7 +1619,8 @@ bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { } vmi.mapRect(&clipBounds); SkAutoTUnref batch(TessellatingPathBatch::Create(args.fColor, *args.fPath, - *args.fViewMatrix, clipBounds)); + *args.fStroke, *args.fViewMatrix, + clipBounds)); args.fTarget->drawBatch(*args.fPipelineBuilder, batch); return true; @@ -1617,7 +1641,8 @@ BATCH_TEST_DEFINE(TesselatingPathBatch) { SkFAIL("Cannot invert matrix\n"); } vmi.mapRect(&clipBounds); - return TessellatingPathBatch::Create(color, path, viewMatrix, clipBounds); + GrStrokeInfo strokeInfo = GrTest::TestStrokeInfo(random); + return TessellatingPathBatch::Create(color, path, strokeInfo, viewMatrix, clipBounds); } #endif diff --git a/src/gpu/GrTestUtils.cpp b/src/gpu/GrTestUtils.cpp index b690d7e3cd..9e1dbce691 100644 --- a/src/gpu/GrTestUtils.cpp +++ b/src/gpu/GrTestUtils.cpp @@ -5,8 +5,10 @@ * found in the LICENSE file. */ +#include "GrStrokeInfo.h" #include "GrTestUtils.h" #include "SkMatrix.h" +#include "SkPathEffect.h" #include "SkPath.h" #include "SkRRect.h" @@ -216,21 +218,44 @@ const SkPath& TestPathConvex(SkRandom* random) { return gPath[random->nextULessThan(static_cast(SK_ARRAY_COUNT(gPath)))]; } -SkStrokeRec TestStrokeRec(SkRandom* random) { - SkStrokeRec::InitStyle style = - SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1)); - SkStrokeRec rec(style); +static void randomize_stroke_rec(SkStrokeRec* rec, SkRandom* random) { bool strokeAndFill = random->nextBool(); SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f; - rec.setStrokeStyle(strokeWidth, strokeAndFill); + rec->setStrokeStyle(strokeWidth, strokeAndFill); SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount)); SkPaint::Join join = SkPaint::Join(random->nextULessThan(SkPaint::kJoinCount)); SkScalar miterLimit = random->nextRangeScalar(1.f, 5.f); - rec.setStrokeParams(cap, join, miterLimit); + rec->setStrokeParams(cap, join, miterLimit); +} + +SkStrokeRec TestStrokeRec(SkRandom* random) { + SkStrokeRec::InitStyle style = + SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1)); + SkStrokeRec rec(style); + randomize_stroke_rec(&rec, random); return rec; } +GrStrokeInfo TestStrokeInfo(SkRandom* random) { + SkStrokeRec::InitStyle style = + SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1)); + GrStrokeInfo strokeInfo(style); + randomize_stroke_rec(&strokeInfo, random); + SkPathEffect::DashInfo dashInfo; + dashInfo.fCount = random->nextRangeU(1, 100); + dashInfo.fIntervals = SkNEW_ARRAY(SkScalar, dashInfo.fCount); + SkScalar sum = 0; + for (int i = 0; i < dashInfo.fCount; i++) { + dashInfo.fIntervals[i] = random->nextRangeScalar(SkDoubleToScalar(0.01), + SkDoubleToScalar(10.0)); + sum += dashInfo.fIntervals[i]; + } + dashInfo.fPhase = random->nextRangeScalar(0, sum); + strokeInfo.setDashInfo(dashInfo); + return strokeInfo; +} + }; #endif