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/GrGLSL.h"
16#include "GrContext.h"
17#include "GrCoordTransform.h"
18#include "GrDrawTarget.h"
19#include "GrDrawTargetCaps.h"
20#include "GrEffect.h"
21#include "GrGpu.h"
22#include "GrStrokeInfo.h"
23#include "GrTBackendEffectFactory.h"
24#include "SkGr.h"
25
26///////////////////////////////////////////////////////////////////////////////
27
28// Returns whether or not the gpu can fast path the dash line effect.
29static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
30                               const GrDrawTarget& target, const SkMatrix& viewMatrix) {
31    if (target.getDrawState().getRenderTarget()->isMultisampled()) {
32        return false;
33    }
34
35    // Pts must be either horizontal or vertical in src space
36    if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
37        return false;
38    }
39
40    // May be able to relax this to include skew. As of now cannot do perspective
41    // because of the non uniform scaling of bloating a rect
42    if (!viewMatrix.preservesRightAngles()) {
43        return false;
44    }
45
46    if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) {
47        return false;
48    }
49
50    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
51    if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) {
52        return false;
53    }
54
55    SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
56    // Current we do don't handle Round or Square cap dashes
57    if (SkPaint::kRound_Cap == cap) {
58        return false;
59    }
60
61    return true;
62}
63
64namespace {
65
66struct DashLineVertex {
67    SkPoint fPos;
68    SkPoint fDashPos;
69};
70
71extern const GrVertexAttrib gDashLineVertexAttribs[] = {
72    { kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding },
73    { kVec2f_GrVertexAttribType, sizeof(SkPoint),   kEffect_GrVertexAttribBinding },
74};
75
76};
77static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
78                            const SkMatrix& viewMatrix, const SkPoint pts[2]) {
79    SkVector vecSrc = pts[1] - pts[0];
80    SkScalar magSrc = vecSrc.length();
81    SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
82    vecSrc.scale(invSrc);
83
84    SkVector vecSrcPerp;
85    vecSrc.rotateCW(&vecSrcPerp);
86    viewMatrix.mapVectors(&vecSrc, 1);
87    viewMatrix.mapVectors(&vecSrcPerp, 1);
88
89    // parallelScale tells how much to scale along the line parallel to the dash line
90    // perpScale tells how much to scale in the direction perpendicular to the dash line
91    *parallelScale = vecSrc.length();
92    *perpScale = vecSrcPerp.length();
93}
94
95// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
96// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
97static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) {
98    SkVector vec = pts[1] - pts[0];
99    SkScalar mag = vec.length();
100    SkScalar inv = mag ? SkScalarInvert(mag) : 0;
101
102    vec.scale(inv);
103    rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
104    if (ptsRot) {
105        rotMatrix->mapPoints(ptsRot, pts, 2);
106        // correction for numerical issues if map doesn't make ptsRot exactly horizontal
107        ptsRot[1].fY = pts[0].fY;
108    }
109}
110
111// Assumes phase < sum of all intervals
112static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) {
113    SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
114    if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) {
115        SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
116        return srcIntervalLen - info.fPhase;
117    }
118    return 0;
119}
120
121static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2],
122                                    SkScalar phase, SkScalar* endingInt) {
123    if (pts[1].fX <= pts[0].fX) {
124        return 0;
125    }
126    SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
127    SkScalar totalLen = pts[1].fX - pts[0].fX;
128    SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
129    SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
130    *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
131    temp = SkScalarDiv(*endingInt, srcIntervalLen);
132    *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
133    if (0 == *endingInt) {
134        *endingInt = srcIntervalLen;
135    }
136    if (*endingInt > info.fIntervals[0]) {
137        if (0 == info.fIntervals[0]) {
138            *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
139        }
140        return *endingInt - info.fIntervals[0];
141    }
142    return 0;
143}
144
145static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix,
146                       SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) {
147
148        SkScalar startDashX = offset - bloat;
149        SkScalar endDashX = offset + len + bloat;
150        SkScalar startDashY = -stroke - bloat;
151        SkScalar endDashY = stroke + bloat;
152        verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
153        verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
154        verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
155        verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
156
157        verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
158        verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
159        verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
160        verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
161
162        matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4);
163}
164
165
166bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint,
167                                   const GrStrokeInfo& strokeInfo, GrGpu* gpu,
168                                   GrDrawTarget* target, const SkMatrix& vm) {
169
170    if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) {
171        return false;
172    }
173
174    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
175
176    SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
177
178    SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
179
180    // the phase should be normalized to be [0, sum of all intervals)
181    SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
182
183    SkScalar srcPhase = info.fPhase;
184
185    // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
186    SkMatrix srcRotInv;
187    SkPoint ptsRot[2];
188    if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
189        SkMatrix rotMatrix;
190        align_to_x_axis(pts, &rotMatrix, ptsRot);
191        if(!rotMatrix.invert(&srcRotInv)) {
192            GrPrintf("Failed to create invertible rotation matrix!\n");
193            return false;
194        }
195    } else {
196        srcRotInv.reset();
197        memcpy(ptsRot, pts, 2 * sizeof(SkPoint));
198    }
199
200    bool useAA = paint.isAntiAlias();
201
202    // Scale corrections of intervals and stroke from view matrix
203    SkScalar parallelScale;
204    SkScalar perpScale;
205    calc_dash_scaling(&parallelScale, &perpScale, vm, ptsRot);
206
207    bool hasCap = SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth;
208
209    // We always want to at least stroke out half a pixel on each side in device space
210    // so 0.5f / perpScale gives us this min in src space
211    SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale);
212
213    SkScalar strokeAdj;
214    if (!hasCap) {
215        strokeAdj = 0.f;
216    } else {
217        strokeAdj = halfSrcStroke;
218    }
219
220    SkScalar startAdj = 0;
221
222    SkMatrix combinedMatrix = srcRotInv;
223    combinedMatrix.postConcat(vm);
224
225    bool lineDone = false;
226    SkRect startRect;
227    bool hasStartRect = false;
228    // If we are using AA, check to see if we are drawing a partial dash at the start. If so
229    // draw it separately here and adjust our start point accordingly
230    if (useAA) {
231        if (srcPhase > 0 && srcPhase < info.fIntervals[0]) {
232            SkPoint startPts[2];
233            startPts[0] = ptsRot[0];
234            startPts[1].fY = startPts[0].fY;
235            startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase,
236                                         ptsRot[1].fX);
237            startRect.set(startPts, 2);
238            startRect.outset(strokeAdj, halfSrcStroke);
239
240            hasStartRect = true;
241            startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase;
242        }
243    }
244
245    // adjustments for start and end of bounding rect so we only draw dash intervals
246    // contained in the original line segment.
247    startAdj += calc_start_adjustment(info);
248    if (startAdj != 0) {
249        ptsRot[0].fX += startAdj;
250        srcPhase = 0;
251    }
252    SkScalar endingInterval = 0;
253    SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval);
254    ptsRot[1].fX -= endAdj;
255    if (ptsRot[0].fX >= ptsRot[1].fX) {
256        lineDone = true;
257    }
258
259    SkRect endRect;
260    bool hasEndRect = false;
261    // If we are using AA, check to see if we are drawing a partial dash at then end. If so
262    // draw it separately here and adjust our end point accordingly
263    if (useAA && !lineDone) {
264        // If we adjusted the end then we will not be drawing a partial dash at the end.
265        // If we didn't adjust the end point then we just need to make sure the ending
266        // dash isn't a full dash
267        if (0 == endAdj && endingInterval != info.fIntervals[0]) {
268            SkPoint endPts[2];
269            endPts[1] = ptsRot[1];
270            endPts[0].fY = endPts[1].fY;
271            endPts[0].fX = endPts[1].fX - endingInterval;
272
273            endRect.set(endPts, 2);
274            endRect.outset(strokeAdj, halfSrcStroke);
275
276            hasEndRect = true;
277            endAdj = endingInterval + info.fIntervals[1];
278
279            ptsRot[1].fX -= endAdj;
280            if (ptsRot[0].fX >= ptsRot[1].fX) {
281                lineDone = true;
282            }
283        }
284    }
285
286    if (startAdj != 0) {
287        srcPhase = 0;
288    }
289
290    // Change the dashing info from src space into device space
291    SkScalar devIntervals[2];
292    devIntervals[0] = info.fIntervals[0] * parallelScale;
293    devIntervals[1] = info.fIntervals[1] * parallelScale;
294    SkScalar devPhase = srcPhase * parallelScale;
295    SkScalar strokeWidth = srcStrokeWidth * perpScale;
296
297    if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
298        strokeWidth = 1.f;
299    }
300
301    SkScalar halfDevStroke = strokeWidth * 0.5f;
302
303    if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) {
304        // add cap to on interveal and remove from off interval
305        devIntervals[0] += strokeWidth;
306        devIntervals[1] -= strokeWidth;
307    }
308    SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
309
310    SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f;
311    SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f;
312
313    SkScalar devBloat = useAA ? 0.5f : 0.f;
314
315    GrDrawState* drawState = target->drawState();
316    if (devIntervals[1] <= 0.f && useAA) {
317        // Case when we end up drawing a solid AA rect
318        // Reset the start rect to draw this single solid rect
319        // but it requires to upload a new intervals uniform so we can mimic
320        // one giant dash
321        ptsRot[0].fX -= hasStartRect ? startAdj : 0;
322        ptsRot[1].fX += hasEndRect ? endAdj : 0;
323        startRect.set(ptsRot, 2);
324        startRect.outset(strokeAdj, halfSrcStroke);
325        hasStartRect = true;
326        hasEndRect = false;
327        lineDone = true;
328
329        SkPoint devicePts[2];
330        vm.mapPoints(devicePts, ptsRot, 2);
331        SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
332        if (hasCap) {
333            lineLength += 2.f * halfDevStroke;
334        }
335        devIntervals[0] = lineLength;
336    }
337    if (devIntervals[1] > 0.f || useAA) {
338        SkPathEffect::DashInfo devInfo;
339        devInfo.fPhase = devPhase;
340        devInfo.fCount = 2;
341        devInfo.fIntervals = devIntervals;
342        GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType :
343            kFillBW_GrEffectEdgeType;
344        drawState->addCoverageEffect(
345            GrDashingEffect::Create(edgeType, devInfo, strokeWidth), 1)->unref();
346    }
347
348    // Set up the vertex data for the line and start/end dashes
349    drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs));
350
351    int totalRectCnt = 0;
352
353    totalRectCnt += !lineDone ? 1 : 0;
354    totalRectCnt += hasStartRect ? 1 : 0;
355    totalRectCnt += hasEndRect ? 1 : 0;
356
357    GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0);
358    if (!geo.succeeded()) {
359        GrPrintf("Failed to get space for vertices!\n");
360        return false;
361    }
362
363    DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
364
365    int curVIdx = 0;
366
367    // Draw interior part of dashed line
368    if (!lineDone) {
369        SkPoint devicePts[2];
370        vm.mapPoints(devicePts, ptsRot, 2);
371        SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
372        if (hasCap) {
373            lineLength += 2.f * halfDevStroke;
374        }
375
376        SkRect bounds;
377        bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY);
378        bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
379        setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat,
380                          lineLength, halfDevStroke);
381        curVIdx += 4;
382    }
383
384    if (hasStartRect) {
385        SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
386        startRect.outset(bloatX, bloatY);
387        setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
388                          devIntervals[0], halfDevStroke);
389        curVIdx += 4;
390    }
391
392    if (hasEndRect) {
393        SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
394        endRect.outset(bloatX, bloatY);
395        setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
396                          devIntervals[0], halfDevStroke);
397    }
398
399    target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
400    target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6);
401    target->resetIndexSource();
402    return true;
403}
404
405//////////////////////////////////////////////////////////////////////////////
406
407class GLDashingLineEffect;
408
409class DashingLineEffect : public GrVertexEffect {
410public:
411    typedef SkPathEffect::DashInfo DashInfo;
412
413    /**
414     * The effect calculates the coverage for the case of a horizontal line in device space.
415     * The matrix that is passed in should be able to convert a line in source space to a
416     * horizontal line in device space. Additionally, the coord transform matrix should translate
417     * the the start of line to origin, and the shift it along the positive x-axis by the phase
418     * and half the off interval.
419     */
420    static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info,
421                               SkScalar strokeWidth);
422
423    virtual ~DashingLineEffect();
424
425    static const char* Name() { return "DashingEffect"; }
426
427    GrEffectEdgeType getEdgeType() const { return fEdgeType; }
428
429    const SkRect& getRect() const { return fRect; }
430
431    SkScalar getIntervalLength() const { return fIntervalLength; }
432
433    typedef GLDashingLineEffect GLEffect;
434
435    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
436
437    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
438
439private:
440    DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
441
442    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
443
444    GrEffectEdgeType    fEdgeType;
445    SkRect              fRect;
446    SkScalar            fIntervalLength;
447
448    GR_DECLARE_EFFECT_TEST;
449
450    typedef GrEffect INHERITED;
451};
452
453//////////////////////////////////////////////////////////////////////////////
454
455class GLDashingLineEffect : public GrGLVertexEffect {
456public:
457    GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
458
459    virtual void emitCode(GrGLFullShaderBuilder* builder,
460                          const GrDrawEffect& drawEffect,
461                          EffectKey key,
462                          const char* outputColor,
463                          const char* inputColor,
464                          const TransformedCoordsArray&,
465                          const TextureSamplerArray&) SK_OVERRIDE;
466
467    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
468
469    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
470
471private:
472    GrGLUniformManager::UniformHandle   fRectUniform;
473    GrGLUniformManager::UniformHandle   fIntervalUniform;
474    SkRect                              fPrevRect;
475    SkScalar                            fPrevIntervalLength;
476    typedef GrGLVertexEffect INHERITED;
477};
478
479GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory,
480                                     const GrDrawEffect& drawEffect)
481    : INHERITED (factory) {
482    fPrevRect.fLeft = SK_ScalarNaN;
483    fPrevIntervalLength = SK_ScalarMax;
484
485}
486
487void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder,
488                                    const GrDrawEffect& drawEffect,
489                                    EffectKey key,
490                                    const char* outputColor,
491                                    const char* inputColor,
492                                    const TransformedCoordsArray&,
493                                    const TextureSamplerArray& samplers) {
494    const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
495    const char *rectName;
496    // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
497    // respectively.
498    fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
499                                       kVec4f_GrSLType,
500                                       "rect",
501                                       &rectName);
502    const char *intervalName;
503    // The interval uniform's refers to the total length of the interval (on + off)
504    fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
505                                       kFloat_GrSLType,
506                                       "interval",
507                                       &intervalName);
508
509    const char *vsCoordName, *fsCoordName;
510    builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
511    const SkString* attr0Name =
512        builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
513    builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
514
515    // transforms all points so that we can compare them to our test rect
516    builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n",
517                           fsCoordName, fsCoordName, intervalName, intervalName);
518    builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
519    if (GrEffectEdgeTypeIsAA(de.getEdgeType())) {
520        // The amount of coverage removed in x and y by the edges is computed as a pair of negative
521        // numbers, xSub and ySub.
522        builder->fsCodeAppend("\t\tfloat xSub, ySub;\n");
523        builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName);
524        builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName);
525        builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName);
526        builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName);
527        // Now compute coverage in x and y and multiply them to get the fraction of the pixel
528        // covered.
529        builder->fsCodeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n");
530    } else {
531        // Assuming the bounding geometry is tight so no need to check y values
532        builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
533        builder->fsCodeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName);
534        builder->fsCodeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName);
535    }
536    builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
537                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
538}
539
540void GLDashingLineEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
541    const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
542    const SkRect& rect = de.getRect();
543    SkScalar intervalLength = de.getIntervalLength();
544    if (rect != fPrevRect || intervalLength != fPrevIntervalLength) {
545        uman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
546                   rect.fRight - 0.5f, rect.fBottom - 0.5f);
547        uman.set1f(fIntervalUniform, intervalLength);
548        fPrevRect = rect;
549        fPrevIntervalLength = intervalLength;
550    }
551}
552
553GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect,
554                                                const GrGLCaps&) {
555    const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
556    return de.getEdgeType();
557}
558
559//////////////////////////////////////////////////////////////////////////////
560
561GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
562                                       SkScalar strokeWidth) {
563    if (info.fCount != 2) {
564        return NULL;
565    }
566
567    return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect,
568                                                      (edgeType, info, strokeWidth))));
569}
570
571DashingLineEffect::~DashingLineEffect() {}
572
573void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
574    *validFlags = 0;
575}
576
577const GrBackendEffectFactory& DashingLineEffect::getFactory() const {
578    return GrTBackendEffectFactory<DashingLineEffect>::getInstance();
579}
580
581DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info,
582                                     SkScalar strokeWidth)
583    : fEdgeType(edgeType) {
584    SkScalar onLen = info.fIntervals[0];
585    SkScalar offLen = info.fIntervals[1];
586    SkScalar halfOffLen = SkScalarHalf(offLen);
587    SkScalar halfStroke = SkScalarHalf(strokeWidth);
588    fIntervalLength = onLen + offLen;
589    fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke);
590
591    this->addVertexAttrib(kVec2f_GrSLType);
592}
593
594bool DashingLineEffect::onIsEqual(const GrEffect& other) const {
595    const DashingLineEffect& de = CastEffect<DashingLineEffect>(other);
596    return (fEdgeType == de.fEdgeType &&
597            fRect == de.fRect &&
598            fIntervalLength == de.fIntervalLength);
599}
600
601GR_DEFINE_EFFECT_TEST(DashingLineEffect);
602
603GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random,
604                                         GrContext*,
605                                         const GrDrawTargetCaps& caps,
606                                         GrTexture*[]) {
607    GrEffectRef* effect;
608    GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
609            kGrEffectEdgeTypeCnt));
610    SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
611    DashInfo info;
612    info.fCount = 2;
613    SkAutoTArray<SkScalar> intervals(info.fCount);
614    info.fIntervals = intervals.get();
615    info.fIntervals[0] = random->nextRangeScalar(0, 10.f);
616    info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
617    info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]);
618
619    effect = DashingLineEffect::Create(edgeType, info, strokeWidth);
620    return effect;
621}
622
623//////////////////////////////////////////////////////////////////////////////
624
625GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
626                                     SkScalar strokeWidth) {
627    return DashingLineEffect::Create(edgeType, info, strokeWidth);
628}
629