diff options
-rw-r--r-- | gm/stroke_rect_shader.cpp | 61 | ||||
-rw-r--r-- | include/core/SkPaint.h | 6 | ||||
-rw-r--r-- | src/gpu/GrDrawContext.cpp | 65 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 19 | ||||
-rw-r--r-- | src/gpu/batches/GrAAStrokeRectBatch.cpp | 50 | ||||
-rw-r--r-- | src/gpu/batches/GrAAStrokeRectBatch.h | 8 | ||||
-rw-r--r-- | src/gpu/batches/GrNonAAStrokeRectBatch.cpp | 35 | ||||
-rw-r--r-- | src/gpu/batches/GrNonAAStrokeRectBatch.h | 11 | ||||
-rw-r--r-- | src/gpu/batches/GrRectBatchFactory.h | 4 | ||||
-rw-r--r-- | src/gpu/effects/GrDashingEffect.cpp | 2 |
10 files changed, 159 insertions, 102 deletions
diff --git a/gm/stroke_rect_shader.cpp b/gm/stroke_rect_shader.cpp new file mode 100644 index 0000000000..0eb09e9a6f --- /dev/null +++ b/gm/stroke_rect_shader.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkGradientShader.h" + +namespace skiagm { + +// Draw stroked rects (both AA and nonAA) with all the types of joins: +// bevel, miter, miter-limited-to-bevel, round +// and as a hairline. +DEF_SIMPLE_GM(stroke_rect_shader, canvas, 690, 300) { + static constexpr SkRect kRect {0, 0, 100, 100}; + static constexpr SkPoint kPts[] {{kRect.fLeft, kRect.fTop}, {kRect.fRight, kRect.fBottom}}; + static constexpr SkColor kColors[] {SK_ColorRED, SK_ColorBLUE}; + SkPaint paint; + sk_sp<SkShader> shader = SkGradientShader::MakeLinear(kPts, kColors, nullptr, 2, + SkShader::kClamp_TileMode); + paint.setShader(std::move(shader)); + paint.setStyle(SkPaint::kStroke_Style); + // Do a large initial translate so that local coords disagree with device coords significantly + // for the first rect drawn. + canvas->translate(kRect.centerX(), kRect.centerY()); + static constexpr SkScalar kPad = 20; + for (auto aa : {false, true}) { + paint.setAntiAlias(aa); + canvas->save(); + + static constexpr SkScalar kStrokeWidth = 10; + paint.setStrokeWidth(kStrokeWidth); + + paint.setStrokeJoin(SkPaint::kBevel_Join); + canvas->drawRect(kRect, paint); + canvas->translate(kRect.width() + kPad, 0); + + paint.setStrokeJoin(SkPaint::kMiter_Join); + canvas->drawRect(kRect, paint); + canvas->translate(kRect.width() + kPad, 0); + + // This miter limit should effectively produce a bevel join. + paint.setStrokeMiter(0.01f); + canvas->drawRect(kRect, paint); + canvas->translate(kRect.width() + kPad, 0); + + paint.setStrokeJoin(SkPaint::kRound_Join); + canvas->drawRect(kRect, paint); + canvas->translate(kRect.width() + kPad, 0); + + paint.setStrokeWidth(0); + canvas->drawRect(kRect, paint); + + canvas->restore(); + canvas->translate(0, kRect.height() + kPad); + } +} + +} diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 293ffedc12..c0f1f3eec8 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -409,9 +409,10 @@ public: kRound_Cap, //!< begin/end contours with a semi-circle extension kSquare_Cap, //!< begin/end contours with a half square extension - kCapCount, + kLast_Cap = kSquare_Cap, kDefault_Cap = kButt_Cap }; + static constexpr int kCapCount = kLast_Cap + 1; /** Join enum specifies the settings for the paint's strokejoin. This is the treatment that is applied to corners in paths and rectangles. @@ -421,9 +422,10 @@ public: kRound_Join, //!< connect path segments with a round join kBevel_Join, //!< connect path segments with a flat bevel join - kJoinCount, + kLast_Join = kBevel_Join, kDefault_Join = kMiter_Join }; + static constexpr int kJoinCount = kLast_Join + 1; /** Return the paint's stroke cap type, controlling how the start and end of stroked lines and paths are treated. diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp index d057984f9c..5474707191 100644 --- a/src/gpu/GrDrawContext.cpp +++ b/src/gpu/GrDrawContext.cpp @@ -311,11 +311,12 @@ void GrDrawContext::drawRect(const GrClip& clip, AutoCheckFlush acf(fDrawingManager); const SkStrokeRec& stroke = style->strokeRec(); - SkScalar width = stroke.getWidth(); - - // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking - // cases where the RT is fully inside a stroke. - if (width < 0) { + bool useHWAA; + bool snapToPixelCenters = false; + SkAutoTUnref<GrDrawBatch> batch; + if (stroke.getStyle() == SkStrokeRec::kFill_Style) { + // Check if this is a full RT draw and can be replaced with a clear. We don't bother + // checking cases where the RT is fully inside a stroke. SkRect rtRect; fRenderTarget->getBoundsRect(&rtRect); // Does the clip contain the entire RT? @@ -339,32 +340,54 @@ void GrDrawContext::drawRect(const GrClip& clip, } } } - } - - bool useHWAA; - bool snapToPixelCenters = false; - SkAutoTUnref<GrDrawBatch> batch; - if (width < 0) { batch.reset(this->getFillRectBatch(paint, viewMatrix, rect, &useHWAA)); - } else { + } else if (stroke.getStyle() == SkStrokeRec::kStroke_Style || + stroke.getStyle() == SkStrokeRec::kHairline_Style) { + if ((!rect.width() || !rect.height()) && + SkStrokeRec::kHairline_Style != stroke.getStyle()) { + SkScalar r = stroke.getWidth() / 2; + // TODO: Move these stroke->fill fallbacks to GrShape? + switch (stroke.getJoin()) { + case SkPaint::kMiter_Join: + this->drawRect(clip, paint, viewMatrix, + {rect.fLeft - r, rect.fTop - r, + rect.fRight + r, rect.fBottom + r}, + &GrStyle::SimpleFill()); + return; + case SkPaint::kRound_Join: + // Raster draws nothing when both dimensions are empty. + if (rect.width() || rect.height()){ + SkRRect rrect = SkRRect::MakeRectXY(rect.makeOutset(r, r), r, r); + this->drawRRect(clip, paint, viewMatrix, rrect, GrStyle::SimpleFill()); + return; + } + case SkPaint::kBevel_Join: + if (!rect.width()) { + this->drawRect(clip, paint, viewMatrix, + {rect.fLeft - r, rect.fTop, rect.fRight + r, rect.fBottom}, + &GrStyle::SimpleFill()); + } else { + this->drawRect(clip, paint, viewMatrix, + {rect.fLeft, rect.fTop - r, rect.fRight, rect.fBottom + r}, + &GrStyle::SimpleFill()); + } + return; + } + } GrColor color = paint.getColor(); - if (should_apply_coverage_aa(paint, fRenderTarget.get(), &useHWAA)) { // The stroke path needs the rect to remain axis aligned (no rotation or skew). if (viewMatrix.rectStaysRect()) { - batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect, - stroke)); + batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect, stroke)); } } else { - // Non-AA hairlines are snapped to pixel centers to make which pixels are hit - // deterministic - snapToPixelCenters = (0 == width && !fRenderTarget->isUnifiedMultisampled()); - batch.reset(GrRectBatchFactory::CreateNonAAStroke(color, viewMatrix, rect, - width, snapToPixelCenters)); - // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of // hairline rects. We jam all the vertices to pixel centers to avoid this, but not // when MSAA is enabled because it can cause ugly artifacts. + snapToPixelCenters = stroke.getStyle() == SkStrokeRec::kHairline_Style && + !fRenderTarget->isUnifiedMultisampled(); + batch.reset(GrRectBatchFactory::CreateNonAAStroke(color, viewMatrix, rect, + stroke, snapToPixelCenters)); } } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 2759a3e185..f6851f1812 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -516,24 +516,9 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext); CHECK_SHOULD_DRAW(draw); - bool doStroke = paint.getStyle() != SkPaint::kFill_Style; - SkScalar width = paint.getStrokeWidth(); - - /* - We have special code for hairline strokes, miter-strokes, bevel-stroke - and fills. Anything else we just call our path code. - */ - bool usePath = doStroke && width > 0 && - (paint.getStrokeJoin() == SkPaint::kRound_Join || - (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty())); - - // a few other reasons we might need to call drawPath... - if (paint.getMaskFilter() || paint.getPathEffect() || - paint.getStyle() == SkPaint::kStrokeAndFill_Style) { // we can't both stroke and fill rects - usePath = true; - } - if (usePath) { + // A couple reasons we might need to call drawPath. + if (paint.getMaskFilter() || paint.getPathEffect()) { SkPath path; path.setIsVolatile(true); path.addRect(rect); diff --git a/src/gpu/batches/GrAAStrokeRectBatch.cpp b/src/gpu/batches/GrAAStrokeRectBatch.cpp index 98b764aa5b..3e0893236f 100644 --- a/src/gpu/batches/GrAAStrokeRectBatch.cpp +++ b/src/gpu/batches/GrAAStrokeRectBatch.cpp @@ -516,14 +516,25 @@ void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices, } } -inline static bool is_miter(const SkStrokeRec& stroke) { +// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter +// limit makes a miter join effectively beveled. +inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) { + SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style || + stroke.getStyle() == SkStrokeRec::kHairline_Style); // For hairlines, make bevel and round joins appear the same as mitered ones. - // small miter limit means right angles show bevel... - if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join || - stroke.getMiter() < SK_ScalarSqrt2)) { - return false; + if (!stroke.getWidth()) { + *isMiter = true; + return true; } - return true; + if (stroke.getJoin() == SkPaint::kBevel_Join) { + *isMiter = false; + return true; + } + if (stroke.getJoin() == SkPaint::kMiter_Join) { + *isMiter = stroke.getMiter() >= SK_ScalarSqrt2; + return true; + } + return false; } static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside, @@ -596,7 +607,10 @@ GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke) { - bool isMiterStroke = is_miter(stroke); + bool isMiterStroke; + if (!allowed_stroke(stroke, &isMiterStroke)) { + return nullptr; + } AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke); SkRect devOutside, devOutsideAssist, devInside; @@ -609,28 +623,6 @@ GrDrawBatch* Create(GrColor color, return batch; } -bool Append(GrBatch* origBatch, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkStrokeRec& stroke) { - AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>(); - - // we can't batch across vm changes - bool isMiterStroke = is_miter(stroke); - if (!batch->canAppend(viewMatrix, isMiterStroke)) { - return false; - } - - SkRect devOutside, devOutsideAssist, devInside; - bool isDegenerate; - compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix, - rect, stroke.getWidth(), isMiterStroke); - - batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate); - return true; -} - }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/batches/GrAAStrokeRectBatch.h b/src/gpu/batches/GrAAStrokeRectBatch.h index e0069a1feb..964cc5b4b9 100644 --- a/src/gpu/batches/GrAAStrokeRectBatch.h +++ b/src/gpu/batches/GrAAStrokeRectBatch.h @@ -29,12 +29,6 @@ GrDrawBatch* Create(GrColor color, const SkRect& rect, const SkStrokeRec& stroke); -bool Append(GrBatch*, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - const SkStrokeRec& stroke); - -}; +} #endif diff --git a/src/gpu/batches/GrNonAAStrokeRectBatch.cpp b/src/gpu/batches/GrNonAAStrokeRectBatch.cpp index a38760c362..fb906081d4 100644 --- a/src/gpu/batches/GrNonAAStrokeRectBatch.cpp +++ b/src/gpu/batches/GrNonAAStrokeRectBatch.cpp @@ -211,29 +211,30 @@ private: typedef GrVertexBatch INHERITED; }; +// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners. +inline static bool allowed_stroke(const SkStrokeRec& stroke) { + SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style || + stroke.getStyle() == SkStrokeRec::kHairline_Style); + return !stroke.getWidth() || + (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2); +} + namespace GrNonAAStrokeRectBatch { GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, - SkScalar strokeWidth, + const SkStrokeRec& stroke, bool snapToPixelCenters) { + if (!allowed_stroke(stroke)) { + return nullptr; + } NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create(); - batch->append(color, viewMatrix, rect, strokeWidth); + batch->append(color, viewMatrix, rect, stroke.getWidth()); batch->init(snapToPixelCenters); return batch; } -void Append(GrBatch* origBatch, - GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - SkScalar strokeWidth, - bool snapToPixelCenters) { - NonAAStrokeRectBatch* batch = origBatch->cast<NonAAStrokeRectBatch>(); - batch->appendAndUpdateBounds(color, viewMatrix, rect, strokeWidth, snapToPixelCenters); -} - }; #ifdef GR_TEST_UTILS @@ -242,9 +243,13 @@ DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) { SkMatrix viewMatrix = GrTest::TestMatrix(random); GrColor color = GrRandomColor(random); SkRect rect = GrTest::TestRect(random); - SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f; - - return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool()); + SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f; + SkPaint paint; + paint.setStrokeWidth(strokeWidth); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeJoin(SkPaint::kMiter_Join); + SkStrokeRec strokeRec(paint); + return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeRec, random->nextBool()); } #endif diff --git a/src/gpu/batches/GrNonAAStrokeRectBatch.h b/src/gpu/batches/GrNonAAStrokeRectBatch.h index 3cee47dedd..4d94337cc0 100644 --- a/src/gpu/batches/GrNonAAStrokeRectBatch.h +++ b/src/gpu/batches/GrNonAAStrokeRectBatch.h @@ -14,6 +14,7 @@ class GrDrawBatch; struct SkRect; +class SkStrokeRec; class SkMatrix; namespace GrNonAAStrokeRectBatch { @@ -21,15 +22,9 @@ namespace GrNonAAStrokeRectBatch { GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, - SkScalar strokeWidth, + const SkStrokeRec&, bool snapToPixelCenters); -void Append(GrColor color, - const SkMatrix& viewMatrix, - const SkRect& rect, - SkScalar strokeWidth, - bool snapToPixelCenters); - -}; +} #endif diff --git a/src/gpu/batches/GrRectBatchFactory.h b/src/gpu/batches/GrRectBatchFactory.h index 16eeaee02c..4512f6a1f9 100644 --- a/src/gpu/batches/GrRectBatchFactory.h +++ b/src/gpu/batches/GrRectBatchFactory.h @@ -55,9 +55,9 @@ inline GrDrawBatch* CreateAAFill(GrColor color, inline GrDrawBatch* CreateNonAAStroke(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, - SkScalar strokeWidth, + const SkStrokeRec& strokeRec, bool snapToPixelCenters) { - return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, snapToPixelCenters); + return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeRec, snapToPixelCenters); } inline GrDrawBatch* CreateAAStroke(GrColor color, diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp index ae375409cb..4e82a62195 100644 --- a/src/gpu/effects/GrDashingEffect.cpp +++ b/src/gpu/effects/GrDashingEffect.cpp @@ -1240,7 +1240,7 @@ DRAW_BATCH_TEST_DEFINE(DashBatch) { } // pick random cap - SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::Cap::kCapCount)); + SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount)); SkScalar intervals[2]; |