#include "effects/GrVertexEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLVertexEffect.h"
+#include "gl/GrGLShaderBuilder.h"
#include "gl/GrGLSL.h"
#include "GrContext.h"
#include "GrCoordTransform.h"
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;
}
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
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
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];
//////////////////////////////////////////////////////////////////////////////
+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 GrEffect* 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,
+ const GrEffectKey& key,
+ const char* outputColor,
+ const char* inputColor,
+ const TransformedCoordsArray&,
+ const TextureSamplerArray&) SK_OVERRIDE;
+
+ static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
+
+ virtual void setData(const GrGLProgramDataManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+ GrGLProgramDataManager::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,
+ const GrEffectKey& 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.0 - 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 GrGLProgramDataManager& pdman, 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) {
+ pdman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength);
+ fPrevRadius = radius;
+ fPrevCenterX = centerX;
+ fPrevIntervalLength = intervalLength;
+ }
+}
+
+void GLDashingCircleEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
+ GrEffectKeyBuilder* b) {
+ const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
+ b->add32(dce.getEdgeType());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffect* DashingCircleEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
+ SkScalar radius) {
+ if (info.fCount != 2 || info.fIntervals[0] != 0) {
+ return NULL;
+ }
+
+ return 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);
+
+GrEffect* DashingCircleEffect::TestCreate(SkRandom* random,
+ GrContext*,
+ const GrDrawTargetCaps& caps,
+ GrTexture*[]) {
+ GrEffect* 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);
+ static GrEffect* Create(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
virtual ~DashingLineEffect();
GR_DECLARE_EFFECT_TEST;
- typedef GrEffect INHERITED;
+ typedef GrVertexEffect INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
virtual void emitCode(GrGLFullShaderBuilder* builder,
const GrDrawEffect& drawEffect,
- EffectKey key,
+ const GrEffectKey& key,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE;
- static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
+ static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
- virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+ virtual void setData(const GrGLProgramDataManager&, const GrDrawEffect&) SK_OVERRIDE;
private:
- GrGLUniformManager::UniformHandle fRectUniform;
- GrGLUniformManager::UniformHandle fIntervalUniform;
- SkRect fPrevRect;
- SkScalar fPrevIntervalLength;
+ GrGLProgramDataManager::UniformHandle fRectUniform;
+ GrGLProgramDataManager::UniformHandle fIntervalUniform;
+ SkRect fPrevRect;
+ SkScalar fPrevIntervalLength;
typedef GrGLVertexEffect INHERITED;
};
: INHERITED (factory) {
fPrevRect.fLeft = SK_ScalarNaN;
fPrevIntervalLength = SK_ScalarMax;
-
}
void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder,
const GrDrawEffect& drawEffect,
- EffectKey key,
+ const GrEffectKey& key,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
(GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
}
-void GLDashingLineEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
+void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman, const GrDrawEffect& drawEffect) {
const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
const SkRect& rect = de.getRect();
SkScalar intervalLength = de.getIntervalLength();
if (rect != fPrevRect || intervalLength != fPrevIntervalLength) {
- uman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
- rect.fRight - 0.5f, rect.fBottom - 0.5f);
- uman.set1f(fIntervalUniform, intervalLength);
+ pdman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
+ rect.fRight - 0.5f, rect.fBottom - 0.5f);
+ pdman.set1f(fIntervalUniform, intervalLength);
fPrevRect = rect;
fPrevIntervalLength = intervalLength;
}
}
-GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect,
- const GrGLCaps&) {
+void GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
+ GrEffectKeyBuilder* b) {
const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
- return de.getEdgeType();
+ b->add32(de.getEdgeType());
}
//////////////////////////////////////////////////////////////////////////////
-GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
- SkScalar strokeWidth) {
+GrEffect* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
+ SkScalar strokeWidth) {
if (info.fCount != 2) {
return NULL;
}
- return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect,
- (edgeType, info, strokeWidth))));
+ return SkNEW_ARGS(DashingLineEffect, (edgeType, info, strokeWidth));
}
DashingLineEffect::~DashingLineEffect() {}
GR_DEFINE_EFFECT_TEST(DashingLineEffect);
-GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random,
- GrContext*,
- const GrDrawTargetCaps& caps,
- GrTexture*[]) {
- GrEffectRef* effect;
+GrEffect* DashingLineEffect::TestCreate(SkRandom* random,
+ GrContext*,
+ const GrDrawTargetCaps& caps,
+ GrTexture*[]) {
+ GrEffect* effect;
GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
kGrEffectEdgeTypeCnt));
SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
//////////////////////////////////////////////////////////////////////////////
-GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
- SkScalar strokeWidth) {
- return DashingLineEffect::Create(edgeType, info, strokeWidth);
+GrEffect* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
+ 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;
}