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