Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / effects / GrDashingEffect.cpp
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7
8 #include "GrDashingEffect.h"
9
10 #include "../GrAARectRenderer.h"
11
12 #include "effects/GrVertexEffect.h"
13 #include "gl/GrGLEffect.h"
14 #include "gl/GrGLVertexEffect.h"
15 #include "gl/GrGLShaderBuilder.h"
16 #include "gl/GrGLSL.h"
17 #include "GrContext.h"
18 #include "GrCoordTransform.h"
19 #include "GrDrawTarget.h"
20 #include "GrDrawTargetCaps.h"
21 #include "GrEffect.h"
22 #include "GrGpu.h"
23 #include "GrStrokeInfo.h"
24 #include "GrTBackendEffectFactory.h"
25 #include "SkGr.h"
26
27 ///////////////////////////////////////////////////////////////////////////////
28
29 // Returns whether or not the gpu can fast path the dash line effect.
30 static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
31                                const GrDrawTarget& target, const SkMatrix& viewMatrix) {
32     if (target.getDrawState().getRenderTarget()->isMultisampled()) {
33         return false;
34     }
35
36     // Pts must be either horizontal or vertical in src space
37     if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
38         return false;
39     }
40
41     // May be able to relax this to include skew. As of now cannot do perspective
42     // because of the non uniform scaling of bloating a rect
43     if (!viewMatrix.preservesRightAngles()) {
44         return false;
45     }
46
47     if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) {
48         return false;
49     }
50
51     const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
52     if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) {
53         return false;
54     }
55
56     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
57     // Current we do don't handle Round or Square cap dashes
58     if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) {
59         return false;
60     }
61
62     return true;
63 }
64
65 namespace {
66
67 struct DashLineVertex {
68     SkPoint fPos;
69     SkPoint fDashPos;
70 };
71
72 extern const GrVertexAttrib gDashLineVertexAttribs[] = {
73     { kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding },
74     { kVec2f_GrVertexAttribType, sizeof(SkPoint),   kEffect_GrVertexAttribBinding },
75 };
76
77 };
78 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
79                             const SkMatrix& viewMatrix, const SkPoint pts[2]) {
80     SkVector vecSrc = pts[1] - pts[0];
81     SkScalar magSrc = vecSrc.length();
82     SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
83     vecSrc.scale(invSrc);
84
85     SkVector vecSrcPerp;
86     vecSrc.rotateCW(&vecSrcPerp);
87     viewMatrix.mapVectors(&vecSrc, 1);
88     viewMatrix.mapVectors(&vecSrcPerp, 1);
89
90     // parallelScale tells how much to scale along the line parallel to the dash line
91     // perpScale tells how much to scale in the direction perpendicular to the dash line
92     *parallelScale = vecSrc.length();
93     *perpScale = vecSrcPerp.length();
94 }
95
96 // calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
97 // Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
98 static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) {
99     SkVector vec = pts[1] - pts[0];
100     SkScalar mag = vec.length();
101     SkScalar inv = mag ? SkScalarInvert(mag) : 0;
102
103     vec.scale(inv);
104     rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
105     if (ptsRot) {
106         rotMatrix->mapPoints(ptsRot, pts, 2);
107         // correction for numerical issues if map doesn't make ptsRot exactly horizontal
108         ptsRot[1].fY = pts[0].fY;
109     }
110 }
111
112 // Assumes phase < sum of all intervals
113 static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) {
114     SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
115     if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) {
116         SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
117         return srcIntervalLen - info.fPhase;
118     }
119     return 0;
120 }
121
122 static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2],
123                                     SkScalar phase, SkScalar* endingInt) {
124     if (pts[1].fX <= pts[0].fX) {
125         return 0;
126     }
127     SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
128     SkScalar totalLen = pts[1].fX - pts[0].fX;
129     SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
130     SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
131     *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
132     temp = SkScalarDiv(*endingInt, srcIntervalLen);
133     *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
134     if (0 == *endingInt) {
135         *endingInt = srcIntervalLen;
136     }
137     if (*endingInt > info.fIntervals[0]) {
138         if (0 == info.fIntervals[0]) {
139             *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
140         }
141         return *endingInt - info.fIntervals[0];
142     }
143     return 0;
144 }
145
146 static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix,
147                        SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) {
148
149         SkScalar startDashX = offset - bloat;
150         SkScalar endDashX = offset + len + bloat;
151         SkScalar startDashY = -stroke - bloat;
152         SkScalar endDashY = stroke + bloat;
153         verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
154         verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
155         verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
156         verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
157
158         verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
159         verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
160         verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
161         verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
162
163         matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4);
164 }
165
166
167 bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint,
168                                    const GrStrokeInfo& strokeInfo, GrGpu* gpu,
169                                    GrDrawTarget* target, const SkMatrix& vm) {
170
171     if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) {
172         return false;
173     }
174
175     const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
176
177     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
178
179     SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
180
181     // the phase should be normalized to be [0, sum of all intervals)
182     SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
183
184     SkScalar srcPhase = info.fPhase;
185
186     // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
187     SkMatrix srcRotInv;
188     SkPoint ptsRot[2];
189     if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
190         SkMatrix rotMatrix;
191         align_to_x_axis(pts, &rotMatrix, ptsRot);
192         if(!rotMatrix.invert(&srcRotInv)) {
193             GrPrintf("Failed to create invertible rotation matrix!\n");
194             return false;
195         }
196     } else {
197         srcRotInv.reset();
198         memcpy(ptsRot, pts, 2 * sizeof(SkPoint));
199     }
200
201     bool useAA = paint.isAntiAlias();
202
203     // Scale corrections of intervals and stroke from view matrix
204     SkScalar parallelScale;
205     SkScalar perpScale;
206     calc_dash_scaling(&parallelScale, &perpScale, vm, ptsRot);
207
208     bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth;
209
210     // We always want to at least stroke out half a pixel on each side in device space
211     // so 0.5f / perpScale gives us this min in src space
212     SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale);
213
214     SkScalar strokeAdj;
215     if (!hasCap) {
216         strokeAdj = 0.f;
217     } else {
218         strokeAdj = halfSrcStroke;
219     }
220
221     SkScalar startAdj = 0;
222
223     SkMatrix combinedMatrix = srcRotInv;
224     combinedMatrix.postConcat(vm);
225
226     bool lineDone = false;
227     SkRect startRect;
228     bool hasStartRect = false;
229     // If we are using AA, check to see if we are drawing a partial dash at the start. If so
230     // draw it separately here and adjust our start point accordingly
231     if (useAA) {
232         if (srcPhase > 0 && srcPhase < info.fIntervals[0]) {
233             SkPoint startPts[2];
234             startPts[0] = ptsRot[0];
235             startPts[1].fY = startPts[0].fY;
236             startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase,
237                                          ptsRot[1].fX);
238             startRect.set(startPts, 2);
239             startRect.outset(strokeAdj, halfSrcStroke);
240
241             hasStartRect = true;
242             startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase;
243         }
244     }
245
246     // adjustments for start and end of bounding rect so we only draw dash intervals
247     // contained in the original line segment.
248     startAdj += calc_start_adjustment(info);
249     if (startAdj != 0) {
250         ptsRot[0].fX += startAdj;
251         srcPhase = 0;
252     }
253     SkScalar endingInterval = 0;
254     SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval);
255     ptsRot[1].fX -= endAdj;
256     if (ptsRot[0].fX >= ptsRot[1].fX) {
257         lineDone = true;
258     }
259
260     SkRect endRect;
261     bool hasEndRect = false;
262     // If we are using AA, check to see if we are drawing a partial dash at then end. If so
263     // draw it separately here and adjust our end point accordingly
264     if (useAA && !lineDone) {
265         // If we adjusted the end then we will not be drawing a partial dash at the end.
266         // If we didn't adjust the end point then we just need to make sure the ending
267         // dash isn't a full dash
268         if (0 == endAdj && endingInterval != info.fIntervals[0]) {
269             SkPoint endPts[2];
270             endPts[1] = ptsRot[1];
271             endPts[0].fY = endPts[1].fY;
272             endPts[0].fX = endPts[1].fX - endingInterval;
273
274             endRect.set(endPts, 2);
275             endRect.outset(strokeAdj, halfSrcStroke);
276
277             hasEndRect = true;
278             endAdj = endingInterval + info.fIntervals[1];
279
280             ptsRot[1].fX -= endAdj;
281             if (ptsRot[0].fX >= ptsRot[1].fX) {
282                 lineDone = true;
283             }
284         }
285     }
286
287     if (startAdj != 0) {
288         srcPhase = 0;
289     }
290
291     // Change the dashing info from src space into device space
292     SkScalar devIntervals[2];
293     devIntervals[0] = info.fIntervals[0] * parallelScale;
294     devIntervals[1] = info.fIntervals[1] * parallelScale;
295     SkScalar devPhase = srcPhase * parallelScale;
296     SkScalar strokeWidth = srcStrokeWidth * perpScale;
297
298     if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
299         strokeWidth = 1.f;
300     }
301
302     SkScalar halfDevStroke = strokeWidth * 0.5f;
303
304     if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) {
305         // add cap to on interveal and remove from off interval
306         devIntervals[0] += strokeWidth;
307         devIntervals[1] -= strokeWidth;
308     }
309     SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
310
311     SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f;
312     SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f;
313
314     SkScalar devBloat = useAA ? 0.5f : 0.f;
315
316     GrDrawState* drawState = target->drawState();
317     if (devIntervals[1] <= 0.f && useAA) {
318         // Case when we end up drawing a solid AA rect
319         // Reset the start rect to draw this single solid rect
320         // but it requires to upload a new intervals uniform so we can mimic
321         // one giant dash
322         ptsRot[0].fX -= hasStartRect ? startAdj : 0;
323         ptsRot[1].fX += hasEndRect ? endAdj : 0;
324         startRect.set(ptsRot, 2);
325         startRect.outset(strokeAdj, halfSrcStroke);
326         hasStartRect = true;
327         hasEndRect = false;
328         lineDone = true;
329
330         SkPoint devicePts[2];
331         vm.mapPoints(devicePts, ptsRot, 2);
332         SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
333         if (hasCap) {
334             lineLength += 2.f * halfDevStroke;
335         }
336         devIntervals[0] = lineLength;
337     }
338     if (devIntervals[1] > 0.f || useAA) {
339         SkPathEffect::DashInfo devInfo;
340         devInfo.fPhase = devPhase;
341         devInfo.fCount = 2;
342         devInfo.fIntervals = devIntervals;
343         GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType :
344             kFillBW_GrEffectEdgeType;
345         bool isRoundCap = SkPaint::kRound_Cap == cap;
346         GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap :
347                                                         GrDashingEffect::kNonRound_DashCap;
348         drawState->addCoverageEffect(
349             GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType), 1)->unref();
350     }
351
352     // Set up the vertex data for the line and start/end dashes
353     drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs));
354
355     int totalRectCnt = 0;
356
357     totalRectCnt += !lineDone ? 1 : 0;
358     totalRectCnt += hasStartRect ? 1 : 0;
359     totalRectCnt += hasEndRect ? 1 : 0;
360
361     GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0);
362     if (!geo.succeeded()) {
363         GrPrintf("Failed to get space for vertices!\n");
364         return false;
365     }
366
367     DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
368
369     int curVIdx = 0;
370
371     if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) {
372         // need to adjust this for round caps to correctly set the dashPos attrib on vertices
373         startOffset -= halfDevStroke;
374     }
375
376     // Draw interior part of dashed line
377     if (!lineDone) {
378         SkPoint devicePts[2];
379         vm.mapPoints(devicePts, ptsRot, 2);
380         SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
381         if (hasCap) {
382             lineLength += 2.f * halfDevStroke;
383         }
384
385         SkRect bounds;
386         bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY);
387         bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
388         setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat,
389                           lineLength, halfDevStroke);
390         curVIdx += 4;
391     }
392
393     if (hasStartRect) {
394         SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
395         startRect.outset(bloatX, bloatY);
396         setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
397                           devIntervals[0], halfDevStroke);
398         curVIdx += 4;
399     }
400
401     if (hasEndRect) {
402         SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
403         endRect.outset(bloatX, bloatY);
404         setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
405                           devIntervals[0], halfDevStroke);
406     }
407
408     target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
409     target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6);
410     target->resetIndexSource();
411     return true;
412 }
413
414 //////////////////////////////////////////////////////////////////////////////
415
416 class GLDashingCircleEffect;
417 /*
418  * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
419  * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
420  * Both of the previous two parameters are in device space. This effect also requires the setting of
421  * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
422  * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
423  * transform the line to be horizontal, with the start of line at the origin then shifted to the
424  * right by half the off interval. The line then goes in the positive x direction.
425  */
426 class DashingCircleEffect : public GrVertexEffect {
427 public:
428     typedef SkPathEffect::DashInfo DashInfo;
429
430     static GrEffect* Create(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar radius);
431
432     virtual ~DashingCircleEffect();
433
434     static const char* Name() { return "DashingCircleEffect"; }
435
436     GrEffectEdgeType getEdgeType() const { return fEdgeType; }
437
438     SkScalar getRadius() const { return fRadius; }
439
440     SkScalar getCenterX() const { return fCenterX; }
441
442     SkScalar getIntervalLength() const { return fIntervalLength; }
443
444     typedef GLDashingCircleEffect GLEffect;
445
446     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
447
448     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
449
450 private:
451     DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar radius);
452
453     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
454
455     GrEffectEdgeType    fEdgeType;
456     SkScalar            fIntervalLength;
457     SkScalar            fRadius;
458     SkScalar            fCenterX;
459
460     GR_DECLARE_EFFECT_TEST;
461
462     typedef GrVertexEffect INHERITED;
463 };
464
465 //////////////////////////////////////////////////////////////////////////////
466
467 class GLDashingCircleEffect : public GrGLVertexEffect {
468 public:
469     GLDashingCircleEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
470
471     virtual void emitCode(GrGLFullShaderBuilder* builder,
472                           const GrDrawEffect& drawEffect,
473                           const GrEffectKey& key,
474                           const char* outputColor,
475                           const char* inputColor,
476                           const TransformedCoordsArray&,
477                           const TextureSamplerArray&) SK_OVERRIDE;
478
479     static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
480
481     virtual void setData(const GrGLProgramDataManager&, const GrDrawEffect&) SK_OVERRIDE;
482
483 private:
484     GrGLProgramDataManager::UniformHandle fParamUniform;
485     SkScalar                              fPrevRadius;
486     SkScalar                              fPrevCenterX;
487     SkScalar                              fPrevIntervalLength;
488     typedef GrGLVertexEffect INHERITED;
489 };
490
491 GLDashingCircleEffect::GLDashingCircleEffect(const GrBackendEffectFactory& factory,
492                                              const GrDrawEffect& drawEffect)
493     : INHERITED (factory) {
494     fPrevRadius = SK_ScalarMin;
495     fPrevCenterX = SK_ScalarMin;
496     fPrevIntervalLength = SK_ScalarMax;
497 }
498
499 void GLDashingCircleEffect::emitCode(GrGLFullShaderBuilder* builder,
500                                     const GrDrawEffect& drawEffect,
501                                     const GrEffectKey& key,
502                                     const char* outputColor,
503                                     const char* inputColor,
504                                     const TransformedCoordsArray&,
505                                     const TextureSamplerArray& samplers) {
506     const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
507     const char *paramName;
508     // The param uniforms, xyz, refer to circle radius - 0.5, cicles center x coord, and
509     // the total interval length of the dash.
510     fParamUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
511                                        kVec3f_GrSLType,
512                                        "params",
513                                        &paramName);
514
515     const char *vsCoordName, *fsCoordName;
516     builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
517     const SkString* attr0Name =
518         builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
519     builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
520
521     // transforms all points so that we can compare them to our test circle
522     builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n",
523                            fsCoordName, fsCoordName, paramName, paramName);
524     builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
525     builder->fsCodeAppendf("\t\tvec2 center = vec2(%s.y, 0.0);\n", paramName);
526     builder->fsCodeAppend("\t\tfloat dist = length(center - fragPosShifted);\n");
527     if (GrEffectEdgeTypeIsAA(dce.getEdgeType())) {
528         builder->fsCodeAppendf("\t\tfloat diff = dist - %s.x;\n", paramName);
529         builder->fsCodeAppend("\t\tdiff = 1.0 - diff;\n");
530         builder->fsCodeAppend("\t\tfloat alpha = clamp(diff, 0.0, 1.0);\n");
531     } else {
532         builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
533         builder->fsCodeAppendf("\t\talpha *=  dist < %s.x + 0.5 ? 1.0 : 0.0;\n", paramName);
534     }
535     builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
536                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
537 }
538
539 void GLDashingCircleEffect::setData(const GrGLProgramDataManager& pdman, const GrDrawEffect& drawEffect) {
540     const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
541     SkScalar radius = dce.getRadius();
542     SkScalar centerX = dce.getCenterX();
543     SkScalar intervalLength = dce.getIntervalLength();
544     if (radius != fPrevRadius || centerX != fPrevCenterX || intervalLength != fPrevIntervalLength) {
545         pdman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength);
546         fPrevRadius = radius;
547         fPrevCenterX = centerX;
548         fPrevIntervalLength = intervalLength;
549     }
550 }
551
552 void GLDashingCircleEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
553                                    GrEffectKeyBuilder* b) {
554     const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
555     b->add32(dce.getEdgeType());
556 }
557
558 //////////////////////////////////////////////////////////////////////////////
559
560 GrEffect* DashingCircleEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
561                                       SkScalar radius) {
562     if (info.fCount != 2 || info.fIntervals[0] != 0) {
563         return NULL;
564     }
565
566     return SkNEW_ARGS(DashingCircleEffect, (edgeType, info, radius));
567 }
568
569 DashingCircleEffect::~DashingCircleEffect() {}
570
571 void DashingCircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
572     *validFlags = 0;
573 }
574
575 const GrBackendEffectFactory& DashingCircleEffect::getFactory() const {
576     return GrTBackendEffectFactory<DashingCircleEffect>::getInstance();
577 }
578
579 DashingCircleEffect::DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info,
580                                          SkScalar radius)
581     : fEdgeType(edgeType) {
582     SkScalar onLen = info.fIntervals[0];
583     SkScalar offLen = info.fIntervals[1];
584     fIntervalLength = onLen + offLen;
585     fRadius = radius;
586     fCenterX = SkScalarHalf(offLen);
587
588     this->addVertexAttrib(kVec2f_GrSLType);
589 }
590
591 bool DashingCircleEffect::onIsEqual(const GrEffect& other) const {
592     const DashingCircleEffect& dce = CastEffect<DashingCircleEffect>(other);
593     return (fEdgeType == dce.fEdgeType &&
594             fIntervalLength == dce.fIntervalLength &&
595             fRadius == dce.fRadius &&
596             fCenterX == dce.fCenterX);
597 }
598
599 GR_DEFINE_EFFECT_TEST(DashingCircleEffect);
600
601 GrEffect* DashingCircleEffect::TestCreate(SkRandom* random,
602                                           GrContext*,
603                                           const GrDrawTargetCaps& caps,
604                                           GrTexture*[]) {
605     GrEffect* effect;
606     GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
607             kGrEffectEdgeTypeCnt));
608     SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
609     DashInfo info;
610     info.fCount = 2;
611     SkAutoTArray<SkScalar> intervals(info.fCount);
612     info.fIntervals = intervals.get();
613     info.fIntervals[0] = 0; 
614     info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
615     info.fPhase = random->nextRangeScalar(0, info.fIntervals[1]);
616
617     effect = DashingCircleEffect::Create(edgeType, info, strokeWidth);
618     return effect;
619 }
620
621 //////////////////////////////////////////////////////////////////////////////
622
623 class GLDashingLineEffect;
624
625 /*
626  * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
627  * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
628  * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
629  * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
630  * vertex coords (in device space) if we transform the line to be horizontal, with the start of
631  * line at the origin then shifted to the right by half the off interval. The line then goes in the
632  * positive x direction.
633  */
634 class DashingLineEffect : public GrVertexEffect {
635 public:
636     typedef SkPathEffect::DashInfo DashInfo;
637
638     static GrEffect* Create(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
639
640     virtual ~DashingLineEffect();
641
642     static const char* Name() { return "DashingEffect"; }
643
644     GrEffectEdgeType getEdgeType() const { return fEdgeType; }
645
646     const SkRect& getRect() const { return fRect; }
647
648     SkScalar getIntervalLength() const { return fIntervalLength; }
649
650     typedef GLDashingLineEffect GLEffect;
651
652     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
653
654     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
655
656 private:
657     DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
658
659     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
660
661     GrEffectEdgeType    fEdgeType;
662     SkRect              fRect;
663     SkScalar            fIntervalLength;
664
665     GR_DECLARE_EFFECT_TEST;
666
667     typedef GrVertexEffect INHERITED;
668 };
669
670 //////////////////////////////////////////////////////////////////////////////
671
672 class GLDashingLineEffect : public GrGLVertexEffect {
673 public:
674     GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
675
676     virtual void emitCode(GrGLFullShaderBuilder* builder,
677                           const GrDrawEffect& drawEffect,
678                           const GrEffectKey& key,
679                           const char* outputColor,
680                           const char* inputColor,
681                           const TransformedCoordsArray&,
682                           const TextureSamplerArray&) SK_OVERRIDE;
683
684     static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
685
686     virtual void setData(const GrGLProgramDataManager&, const GrDrawEffect&) SK_OVERRIDE;
687
688 private:
689     GrGLProgramDataManager::UniformHandle fRectUniform;
690     GrGLProgramDataManager::UniformHandle fIntervalUniform;
691     SkRect                                fPrevRect;
692     SkScalar                              fPrevIntervalLength;
693     typedef GrGLVertexEffect INHERITED;
694 };
695
696 GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory,
697                                      const GrDrawEffect& drawEffect)
698     : INHERITED (factory) {
699     fPrevRect.fLeft = SK_ScalarNaN;
700     fPrevIntervalLength = SK_ScalarMax;
701 }
702
703 void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder,
704                                     const GrDrawEffect& drawEffect,
705                                     const GrEffectKey& key,
706                                     const char* outputColor,
707                                     const char* inputColor,
708                                     const TransformedCoordsArray&,
709                                     const TextureSamplerArray& samplers) {
710     const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
711     const char *rectName;
712     // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
713     // respectively.
714     fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
715                                        kVec4f_GrSLType,
716                                        "rect",
717                                        &rectName);
718     const char *intervalName;
719     // The interval uniform's refers to the total length of the interval (on + off)
720     fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
721                                        kFloat_GrSLType,
722                                        "interval",
723                                        &intervalName);
724
725     const char *vsCoordName, *fsCoordName;
726     builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
727     const SkString* attr0Name =
728         builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
729     builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
730
731     // transforms all points so that we can compare them to our test rect
732     builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n",
733                            fsCoordName, fsCoordName, intervalName, intervalName);
734     builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
735     if (GrEffectEdgeTypeIsAA(de.getEdgeType())) {
736         // The amount of coverage removed in x and y by the edges is computed as a pair of negative
737         // numbers, xSub and ySub.
738         builder->fsCodeAppend("\t\tfloat xSub, ySub;\n");
739         builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName);
740         builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName);
741         builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName);
742         builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName);
743         // Now compute coverage in x and y and multiply them to get the fraction of the pixel
744         // covered.
745         builder->fsCodeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n");
746     } else {
747         // Assuming the bounding geometry is tight so no need to check y values
748         builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
749         builder->fsCodeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName);
750         builder->fsCodeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName);
751     }
752     builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
753                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
754 }
755
756 void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman, const GrDrawEffect& drawEffect) {
757     const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
758     const SkRect& rect = de.getRect();
759     SkScalar intervalLength = de.getIntervalLength();
760     if (rect != fPrevRect || intervalLength != fPrevIntervalLength) {
761         pdman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
762                     rect.fRight - 0.5f, rect.fBottom - 0.5f);
763         pdman.set1f(fIntervalUniform, intervalLength);
764         fPrevRect = rect;
765         fPrevIntervalLength = intervalLength;
766     }
767 }
768
769 void GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
770                                  GrEffectKeyBuilder* b) {
771     const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
772     b->add32(de.getEdgeType());
773 }
774
775 //////////////////////////////////////////////////////////////////////////////
776
777 GrEffect* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
778                                     SkScalar strokeWidth) {
779     if (info.fCount != 2) {
780         return NULL;
781     }
782
783     return SkNEW_ARGS(DashingLineEffect, (edgeType, info, strokeWidth));
784 }
785
786 DashingLineEffect::~DashingLineEffect() {}
787
788 void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
789     *validFlags = 0;
790 }
791
792 const GrBackendEffectFactory& DashingLineEffect::getFactory() const {
793     return GrTBackendEffectFactory<DashingLineEffect>::getInstance();
794 }
795
796 DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info,
797                                      SkScalar strokeWidth)
798     : fEdgeType(edgeType) {
799     SkScalar onLen = info.fIntervals[0];
800     SkScalar offLen = info.fIntervals[1];
801     SkScalar halfOffLen = SkScalarHalf(offLen);
802     SkScalar halfStroke = SkScalarHalf(strokeWidth);
803     fIntervalLength = onLen + offLen;
804     fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke);
805
806     this->addVertexAttrib(kVec2f_GrSLType);
807 }
808
809 bool DashingLineEffect::onIsEqual(const GrEffect& other) const {
810     const DashingLineEffect& de = CastEffect<DashingLineEffect>(other);
811     return (fEdgeType == de.fEdgeType &&
812             fRect == de.fRect &&
813             fIntervalLength == de.fIntervalLength);
814 }
815
816 GR_DEFINE_EFFECT_TEST(DashingLineEffect);
817
818 GrEffect* DashingLineEffect::TestCreate(SkRandom* random,
819                                         GrContext*,
820                                         const GrDrawTargetCaps& caps,
821                                         GrTexture*[]) {
822     GrEffect* effect;
823     GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
824             kGrEffectEdgeTypeCnt));
825     SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
826     DashInfo info;
827     info.fCount = 2;
828     SkAutoTArray<SkScalar> intervals(info.fCount);
829     info.fIntervals = intervals.get();
830     info.fIntervals[0] = random->nextRangeScalar(0, 10.f);
831     info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
832     info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]);
833
834     effect = DashingLineEffect::Create(edgeType, info, strokeWidth);
835     return effect;
836 }
837
838 //////////////////////////////////////////////////////////////////////////////
839
840 GrEffect* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
841                                   SkScalar strokeWidth, GrDashingEffect::DashCap cap) {
842     switch (cap) {
843         case GrDashingEffect::kRound_DashCap:
844             return DashingCircleEffect::Create(edgeType, info, SkScalarHalf(strokeWidth));
845         case GrDashingEffect::kNonRound_DashCap:
846             return DashingLineEffect::Create(edgeType, info, strokeWidth);
847         default:
848             SkFAIL("Unexpected dashed cap.");
849     }
850     return NULL;
851 }