aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/stroke_rect_shader.cpp61
-rw-r--r--include/core/SkPaint.h6
-rw-r--r--src/gpu/GrDrawContext.cpp65
-rw-r--r--src/gpu/SkGpuDevice.cpp19
-rw-r--r--src/gpu/batches/GrAAStrokeRectBatch.cpp50
-rw-r--r--src/gpu/batches/GrAAStrokeRectBatch.h8
-rw-r--r--src/gpu/batches/GrNonAAStrokeRectBatch.cpp35
-rw-r--r--src/gpu/batches/GrNonAAStrokeRectBatch.h11
-rw-r--r--src/gpu/batches/GrRectBatchFactory.h4
-rw-r--r--src/gpu/effects/GrDashingEffect.cpp2
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];