Add the ability for gpu to render dotted lines (dashed line, 0 on interval, round caps)
BUG=skia:
R=bsalomon@google.com
Author: egdaniel@google.com
Review URL: https://codereview.chromium.org/345113003
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index a01f052..2354eab 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -36,6 +36,10 @@
# reed: bitmapfilters changed (labels) with hide_config CL, just need rebaselines
bitmapfilters
+# egdaniel dashing4 gm will now correctly contain dotted lines for gpu (previous missing)
+# /usr/lib/x86_64-linux-gnu/libfontconfig
+dashing4
+
# humper:
# Needs rebaselining after faster GPU blur patch lands
megalooper_0x0
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index 8d9c08f..49169f1 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -54,7 +54,7 @@
SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
// Current we do don't handle Round or Square cap dashes
- if (SkPaint::kRound_Cap == cap) {
+ if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) {
return false;
}
@@ -204,7 +204,7 @@
SkScalar perpScale;
calc_dash_scaling(¶llelScale, &perpScale, vm, ptsRot);
- bool hasCap = SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth;
+ bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth;
// We always want to at least stroke out half a pixel on each side in device space
// so 0.5f / perpScale gives us this min in src space
@@ -341,8 +341,11 @@
devInfo.fIntervals = devIntervals;
GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType :
kFillBW_GrEffectEdgeType;
+ bool isRoundCap = SkPaint::kRound_Cap == cap;
+ GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap :
+ GrDashingEffect::kNonRound_DashCap;
drawState->addCoverageEffect(
- GrDashingEffect::Create(edgeType, devInfo, strokeWidth), 1)->unref();
+ GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType), 1)->unref();
}
// Set up the vertex data for the line and start/end dashes
@@ -364,6 +367,11 @@
int curVIdx = 0;
+ if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) {
+ // need to adjust this for round caps to correctly set the dashPos attrib on vertices
+ startOffset -= halfDevStroke;
+ }
+
// Draw interior part of dashed line
if (!lineDone) {
SkPoint devicePts[2];
@@ -404,19 +412,230 @@
//////////////////////////////////////////////////////////////////////////////
+class GLDashingCircleEffect;
+/*
+ * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
+ * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
+ * Both of the previous two parameters are in device space. This effect also requires the setting of
+ * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
+ * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
+ * transform the line to be horizontal, with the start of line at the origin then shifted to the
+ * right by half the off interval. The line then goes in the positive x direction.
+ */
+class DashingCircleEffect : public GrVertexEffect {
+public:
+ typedef SkPathEffect::DashInfo DashInfo;
+
+ static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info,
+ SkScalar radius);
+
+ virtual ~DashingCircleEffect();
+
+ static const char* Name() { return "DashingCircleEffect"; }
+
+ GrEffectEdgeType getEdgeType() const { return fEdgeType; }
+
+ SkScalar getRadius() const { return fRadius; }
+
+ SkScalar getCenterX() const { return fCenterX; }
+
+ SkScalar getIntervalLength() const { return fIntervalLength; }
+
+ typedef GLDashingCircleEffect GLEffect;
+
+ virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+ virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+ DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar radius);
+
+ virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
+
+ GrEffectEdgeType fEdgeType;
+ SkScalar fIntervalLength;
+ SkScalar fRadius;
+ SkScalar fCenterX;
+
+ GR_DECLARE_EFFECT_TEST;
+
+ typedef GrVertexEffect INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GLDashingCircleEffect : public GrGLVertexEffect {
+public:
+ GLDashingCircleEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
+
+ virtual void emitCode(GrGLFullShaderBuilder* builder,
+ const GrDrawEffect& drawEffect,
+ EffectKey key,
+ const char* outputColor,
+ const char* inputColor,
+ const TransformedCoordsArray&,
+ const TextureSamplerArray&) SK_OVERRIDE;
+
+ static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
+
+ virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+ GrGLUniformManager::UniformHandle fParamUniform;
+ SkScalar fPrevRadius;
+ SkScalar fPrevCenterX;
+ SkScalar fPrevIntervalLength;
+ typedef GrGLVertexEffect INHERITED;
+};
+
+GLDashingCircleEffect::GLDashingCircleEffect(const GrBackendEffectFactory& factory,
+ const GrDrawEffect& drawEffect)
+ : INHERITED (factory) {
+ fPrevRadius = SK_ScalarMin;
+ fPrevCenterX = SK_ScalarMin;
+ fPrevIntervalLength = SK_ScalarMax;
+}
+
+void GLDashingCircleEffect::emitCode(GrGLFullShaderBuilder* builder,
+ const GrDrawEffect& drawEffect,
+ EffectKey key,
+ const char* outputColor,
+ const char* inputColor,
+ const TransformedCoordsArray&,
+ const TextureSamplerArray& samplers) {
+ const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
+ const char *paramName;
+ // The param uniforms, xyz, refer to circle radius - 0.5, cicles center x coord, and
+ // the total interval length of the dash.
+ fParamUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+ kVec3f_GrSLType,
+ "params",
+ ¶mName);
+
+ const char *vsCoordName, *fsCoordName;
+ builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
+ const SkString* attr0Name =
+ builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
+ builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
+
+ // transforms all points so that we can compare them to our test circle
+ builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n",
+ fsCoordName, fsCoordName, paramName, paramName);
+ builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
+ builder->fsCodeAppendf("\t\tvec2 center = vec2(%s.y, 0.0);\n", paramName);
+ builder->fsCodeAppend("\t\tfloat dist = length(center - fragPosShifted);\n");
+ if (GrEffectEdgeTypeIsAA(dce.getEdgeType())) {
+ builder->fsCodeAppendf("\t\tfloat diff = dist - %s.x;\n", paramName);
+ builder->fsCodeAppend("\t\tdiff = 1 - diff;\n");
+ builder->fsCodeAppend("\t\tfloat alpha = clamp(diff, 0.0, 1.0);\n");
+ } else {
+ builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
+ builder->fsCodeAppendf("\t\talpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;\n", paramName);
+ }
+ builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
+ (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
+}
+
+void GLDashingCircleEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
+ const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
+ SkScalar radius = dce.getRadius();
+ SkScalar centerX = dce.getCenterX();
+ SkScalar intervalLength = dce.getIntervalLength();
+ if (radius != fPrevRadius || centerX != fPrevCenterX || intervalLength != fPrevIntervalLength) {
+ uman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength);
+ fPrevRadius = radius;
+ fPrevCenterX = centerX;
+ fPrevIntervalLength = intervalLength;
+ }
+}
+
+GrGLEffect::EffectKey GLDashingCircleEffect::GenKey(const GrDrawEffect& drawEffect,
+ const GrGLCaps&) {
+ const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
+ return dce.getEdgeType();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* DashingCircleEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
+ SkScalar radius) {
+ if (info.fCount != 2 || info.fIntervals[0] != 0) {
+ return NULL;
+ }
+
+ return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingCircleEffect,
+ (edgeType, info, radius))));
+}
+
+DashingCircleEffect::~DashingCircleEffect() {}
+
+void DashingCircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+ *validFlags = 0;
+}
+
+const GrBackendEffectFactory& DashingCircleEffect::getFactory() const {
+ return GrTBackendEffectFactory<DashingCircleEffect>::getInstance();
+}
+
+DashingCircleEffect::DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info,
+ SkScalar radius)
+ : fEdgeType(edgeType) {
+ SkScalar onLen = info.fIntervals[0];
+ SkScalar offLen = info.fIntervals[1];
+ fIntervalLength = onLen + offLen;
+ fRadius = radius;
+ fCenterX = SkScalarHalf(offLen);
+
+ this->addVertexAttrib(kVec2f_GrSLType);
+}
+
+bool DashingCircleEffect::onIsEqual(const GrEffect& other) const {
+ const DashingCircleEffect& dce = CastEffect<DashingCircleEffect>(other);
+ return (fEdgeType == dce.fEdgeType &&
+ fIntervalLength == dce.fIntervalLength &&
+ fRadius == dce.fRadius &&
+ fCenterX == dce.fCenterX);
+}
+
+GR_DEFINE_EFFECT_TEST(DashingCircleEffect);
+
+GrEffectRef* DashingCircleEffect::TestCreate(SkRandom* random,
+ GrContext*,
+ const GrDrawTargetCaps& caps,
+ GrTexture*[]) {
+ GrEffectRef* effect;
+ GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
+ kGrEffectEdgeTypeCnt));
+ SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
+ DashInfo info;
+ info.fCount = 2;
+ SkAutoTArray<SkScalar> intervals(info.fCount);
+ info.fIntervals = intervals.get();
+ info.fIntervals[0] = 0;
+ info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
+ info.fPhase = random->nextRangeScalar(0, info.fIntervals[1]);
+
+ effect = DashingCircleEffect::Create(edgeType, info, strokeWidth);
+ return effect;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
class GLDashingLineEffect;
+/*
+ * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
+ * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
+ * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
+ * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
+ * vertex coords (in device space) if we transform the line to be horizontal, with the start of
+ * line at the origin then shifted to the right by half the off interval. The line then goes in the
+ * positive x direction.
+ */
class DashingLineEffect : public GrVertexEffect {
public:
typedef SkPathEffect::DashInfo DashInfo;
- /**
- * The effect calculates the coverage for the case of a horizontal line in device space.
- * The matrix that is passed in should be able to convert a line in source space to a
- * horizontal line in device space. Additionally, the coord transform matrix should translate
- * the the start of line to origin, and the shift it along the positive x-axis by the phase
- * and half the off interval.
- */
static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info,
SkScalar strokeWidth);
@@ -447,7 +666,7 @@
GR_DECLARE_EFFECT_TEST;
- typedef GrEffect INHERITED;
+ typedef GrVertexEffect INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
@@ -481,7 +700,6 @@
: INHERITED (factory) {
fPrevRect.fLeft = SK_ScalarNaN;
fPrevIntervalLength = SK_ScalarMax;
-
}
void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder,
@@ -623,6 +841,14 @@
//////////////////////////////////////////////////////////////////////////////
GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
- SkScalar strokeWidth) {
- return DashingLineEffect::Create(edgeType, info, strokeWidth);
+ SkScalar strokeWidth, GrDashingEffect::DashCap cap) {
+ switch (cap) {
+ case GrDashingEffect::kRound_DashCap:
+ return DashingCircleEffect::Create(edgeType, info, SkScalarHalf(strokeWidth));
+ case GrDashingEffect::kNonRound_DashCap:
+ return DashingLineEffect::Create(edgeType, info, strokeWidth);
+ default:
+ SkFAIL("Unexpected dashed cap.");
+ }
+ return NULL;
}
diff --git a/src/gpu/effects/GrDashingEffect.h b/src/gpu/effects/GrDashingEffect.h
index 8096017..ced9671 100644
--- a/src/gpu/effects/GrDashingEffect.h
+++ b/src/gpu/effects/GrDashingEffect.h
@@ -24,6 +24,11 @@
bool DrawDashLine(const SkPoint pts[2], const GrPaint& paint, const GrStrokeInfo& strokeInfo,
GrGpu* gpu, GrDrawTarget* target, const SkMatrix& vm);
+ enum DashCap {
+ kRound_DashCap,
+ kNonRound_DashCap,
+ };
+
/**
* An effect that renders a dashed line. It is intended to be used as a coverage effect.
* The effect is meant for dashed lines that only have a single on/off interval pair.
@@ -31,7 +36,7 @@
* position relative to the dashed line.
*/
GrEffectRef* Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
- SkScalar strokeWidth);
+ SkScalar strokeWidth, DashCap cap);
}
#endif