LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/ops - GrDashOp.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 585 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 72 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "GrDashOp.h"
       9             : 
      10             : #include "GrCaps.h"
      11             : #include "GrContext.h"
      12             : #include "GrCoordTransform.h"
      13             : #include "GrDefaultGeoProcFactory.h"
      14             : #include "GrDrawOpTest.h"
      15             : #include "GrGeometryProcessor.h"
      16             : #include "GrOpFlushState.h"
      17             : #include "GrProcessor.h"
      18             : #include "GrStyle.h"
      19             : #include "SkGr.h"
      20             : #include "glsl/GrGLSLFragmentShaderBuilder.h"
      21             : #include "glsl/GrGLSLGeometryProcessor.h"
      22             : #include "glsl/GrGLSLProgramDataManager.h"
      23             : #include "glsl/GrGLSLUniformHandler.h"
      24             : #include "glsl/GrGLSLVarying.h"
      25             : #include "glsl/GrGLSLVertexShaderBuilder.h"
      26             : #include "ops/GrMeshDrawOp.h"
      27             : 
      28             : using AAMode = GrDashOp::AAMode;
      29             : 
      30             : ///////////////////////////////////////////////////////////////////////////////
      31             : 
      32             : // Returns whether or not the gpu can fast path the dash line effect.
      33           0 : bool GrDashOp::CanDrawDashLine(const SkPoint pts[2], const GrStyle& style,
      34             :                                const SkMatrix& viewMatrix) {
      35             :     // Pts must be either horizontal or vertical in src space
      36           0 :     if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
      37           0 :         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           0 :     if (!viewMatrix.preservesRightAngles()) {
      43           0 :         return false;
      44             :     }
      45             : 
      46           0 :     if (!style.isDashed() || 2 != style.dashIntervalCnt()) {
      47           0 :         return false;
      48             :     }
      49             : 
      50           0 :     const SkScalar* intervals = style.dashIntervals();
      51           0 :     if (0 == intervals[0] && 0 == intervals[1]) {
      52           0 :         return false;
      53             :     }
      54             : 
      55           0 :     SkPaint::Cap cap = style.strokeRec().getCap();
      56             :     // Current we do don't handle Round or Square cap dashes
      57           0 :     if (SkPaint::kRound_Cap == cap && intervals[0] != 0.f) {
      58           0 :         return false;
      59             :     }
      60             : 
      61           0 :     return true;
      62             : }
      63             : 
      64             : namespace {
      65             : struct DashLineVertex {
      66             :     SkPoint fPos;
      67             :     SkPoint fDashPos;
      68             :     SkScalar fIntervalLength;
      69             :     SkRect fRect;
      70             : };
      71             : struct DashCircleVertex {
      72             :     SkPoint fPos;
      73             :     SkPoint fDashPos;
      74             :     SkScalar fIntervalLength;
      75             :     SkScalar fRadius;
      76             :     SkScalar fCenterX;
      77             : };
      78             : };
      79             : 
      80           0 : static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
      81             :                             const SkMatrix& viewMatrix, const SkPoint pts[2]) {
      82           0 :     SkVector vecSrc = pts[1] - pts[0];
      83           0 :     SkScalar magSrc = vecSrc.length();
      84           0 :     SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
      85           0 :     vecSrc.scale(invSrc);
      86             : 
      87             :     SkVector vecSrcPerp;
      88           0 :     vecSrc.rotateCW(&vecSrcPerp);
      89           0 :     viewMatrix.mapVectors(&vecSrc, 1);
      90           0 :     viewMatrix.mapVectors(&vecSrcPerp, 1);
      91             : 
      92             :     // parallelScale tells how much to scale along the line parallel to the dash line
      93             :     // perpScale tells how much to scale in the direction perpendicular to the dash line
      94           0 :     *parallelScale = vecSrc.length();
      95           0 :     *perpScale = vecSrcPerp.length();
      96           0 : }
      97             : 
      98             : // calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
      99             : // Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
     100           0 : static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = nullptr) {
     101           0 :     SkVector vec = pts[1] - pts[0];
     102           0 :     SkScalar mag = vec.length();
     103           0 :     SkScalar inv = mag ? SkScalarInvert(mag) : 0;
     104             : 
     105           0 :     vec.scale(inv);
     106           0 :     rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
     107           0 :     if (ptsRot) {
     108           0 :         rotMatrix->mapPoints(ptsRot, pts, 2);
     109             :         // correction for numerical issues if map doesn't make ptsRot exactly horizontal
     110           0 :         ptsRot[1].fY = pts[0].fY;
     111             :     }
     112           0 : }
     113             : 
     114             : // Assumes phase < sum of all intervals
     115           0 : static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) {
     116           0 :     SkASSERT(phase < intervals[0] + intervals[1]);
     117           0 :     if (phase >= intervals[0] && phase != 0) {
     118           0 :         SkScalar srcIntervalLen = intervals[0] + intervals[1];
     119           0 :         return srcIntervalLen - phase;
     120             :     }
     121           0 :     return 0;
     122             : }
     123             : 
     124           0 : static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2],
     125             :                                     SkScalar phase, SkScalar* endingInt) {
     126           0 :     if (pts[1].fX <= pts[0].fX) {
     127           0 :         return 0;
     128             :     }
     129           0 :     SkScalar srcIntervalLen = intervals[0] + intervals[1];
     130           0 :     SkScalar totalLen = pts[1].fX - pts[0].fX;
     131           0 :     SkScalar temp = totalLen / srcIntervalLen;
     132           0 :     SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
     133           0 :     *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
     134           0 :     temp = *endingInt / srcIntervalLen;
     135           0 :     *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
     136           0 :     if (0 == *endingInt) {
     137           0 :         *endingInt = srcIntervalLen;
     138             :     }
     139           0 :     if (*endingInt > intervals[0]) {
     140           0 :         if (0 == intervals[0]) {
     141           0 :             *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
     142             :         }
     143           0 :         return *endingInt - intervals[0];
     144             :     }
     145           0 :     return 0;
     146             : }
     147             : 
     148             : enum DashCap {
     149             :     kRound_DashCap,
     150             :     kNonRound_DashCap,
     151             : };
     152             : 
     153             : static int kDashVertices = 4;
     154             : 
     155             : template <typename T>
     156           0 : void setup_dashed_rect_common(const SkRect& rect, const SkMatrix& matrix, T* vertices, int idx,
     157             :                               SkScalar offset, SkScalar bloatX, SkScalar bloatY, SkScalar len,
     158             :                               SkScalar stroke) {
     159           0 :     SkScalar startDashX = offset - bloatX;
     160           0 :     SkScalar endDashX = offset + len + bloatX;
     161           0 :     SkScalar startDashY = -stroke - bloatY;
     162           0 :     SkScalar endDashY = stroke + bloatY;
     163           0 :     vertices[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
     164           0 :     vertices[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
     165           0 :     vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
     166           0 :     vertices[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
     167             : 
     168           0 :     vertices[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
     169           0 :     vertices[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
     170           0 :     vertices[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
     171           0 :     vertices[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
     172             : 
     173           0 :     matrix.mapPointsWithStride(&vertices[idx].fPos, sizeof(T), 4);
     174           0 : }
     175             : 
     176           0 : static void setup_dashed_rect(const SkRect& rect, void* vertices, int idx,
     177             :                               const SkMatrix& matrix, SkScalar offset, SkScalar bloatX,
     178             :                               SkScalar bloatY, SkScalar len, SkScalar stroke,
     179             :                               SkScalar startInterval, SkScalar endInterval, SkScalar strokeWidth,
     180             :                               DashCap cap, const size_t vertexStride) {
     181           0 :     SkScalar intervalLength = startInterval + endInterval;
     182             : 
     183           0 :     if (kRound_DashCap == cap) {
     184           0 :         SkASSERT(vertexStride == sizeof(DashCircleVertex));
     185           0 :         DashCircleVertex* verts = reinterpret_cast<DashCircleVertex*>(vertices);
     186             : 
     187             :         setup_dashed_rect_common<DashCircleVertex>(rect, matrix, verts, idx, offset, bloatX,
     188           0 :                                                    bloatY, len, stroke);
     189             : 
     190           0 :         SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
     191           0 :         SkScalar centerX = SkScalarHalf(endInterval);
     192             : 
     193           0 :         for (int i = 0; i < kDashVertices; i++) {
     194           0 :             verts[idx + i].fIntervalLength = intervalLength;
     195           0 :             verts[idx + i].fRadius = radius;
     196           0 :             verts[idx + i].fCenterX = centerX;
     197             :         }
     198             : 
     199             :     } else {
     200           0 :         SkASSERT(kNonRound_DashCap == cap && vertexStride == sizeof(DashLineVertex));
     201           0 :         DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(vertices);
     202             : 
     203             :         setup_dashed_rect_common<DashLineVertex>(rect, matrix, verts, idx, offset, bloatX,
     204           0 :                                                  bloatY, len, stroke);
     205             : 
     206           0 :         SkScalar halfOffLen = SkScalarHalf(endInterval);
     207           0 :         SkScalar halfStroke = SkScalarHalf(strokeWidth);
     208             :         SkRect rectParam;
     209           0 :         rectParam.set(halfOffLen + 0.5f, -halfStroke + 0.5f,
     210           0 :                       halfOffLen + startInterval - 0.5f, halfStroke - 0.5f);
     211           0 :         for (int i = 0; i < kDashVertices; i++) {
     212           0 :             verts[idx + i].fIntervalLength = intervalLength;
     213           0 :             verts[idx + i].fRect = rectParam;
     214             :         }
     215             :     }
     216           0 : }
     217             : 
     218           0 : static void setup_dashed_rect_pos(const SkRect& rect, int idx, const SkMatrix& matrix,
     219             :                                   SkPoint* verts) {
     220           0 :     verts[idx] = SkPoint::Make(rect.fLeft, rect.fTop);
     221           0 :     verts[idx + 1] = SkPoint::Make(rect.fLeft, rect.fBottom);
     222           0 :     verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fBottom);
     223           0 :     verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fTop);
     224           0 :     matrix.mapPoints(&verts[idx], 4);
     225           0 : }
     226             : 
     227             : 
     228             : /**
     229             :  * An GrGeometryProcessor that renders a dashed line.
     230             :  * This GrGeometryProcessor is meant for dashed lines that only have a single on/off interval pair.
     231             :  * Bounding geometry is rendered and the effect computes coverage based on the fragment's
     232             :  * position relative to the dashed line.
     233             :  */
     234             : static sk_sp<GrGeometryProcessor> make_dash_gp(GrColor,
     235             :                                                AAMode aaMode,
     236             :                                                DashCap cap,
     237             :                                                const SkMatrix& localMatrix,
     238             :                                                bool usesLocalCoords);
     239             : 
     240           0 : class DashOp final : public GrLegacyMeshDrawOp {
     241             : public:
     242           0 :     DEFINE_OP_CLASS_ID
     243             :     struct LineData {
     244             :         SkMatrix fViewMatrix;
     245             :         SkMatrix fSrcRotInv;
     246             :         SkPoint fPtsRot[2];
     247             :         SkScalar fSrcStrokeWidth;
     248             :         SkScalar fPhase;
     249             :         SkScalar fIntervals[2];
     250             :         SkScalar fParallelScale;
     251             :         SkScalar fPerpendicularScale;
     252             :     };
     253             : 
     254           0 :     static std::unique_ptr<GrLegacyMeshDrawOp> Make(const LineData& geometry, GrColor color,
     255             :                                                     SkPaint::Cap cap, AAMode aaMode,
     256             :                                                     bool fullDash) {
     257             :         return std::unique_ptr<GrLegacyMeshDrawOp>(
     258           0 :                 new DashOp(geometry, color, cap, aaMode, fullDash));
     259             :     }
     260             : 
     261           0 :     const char* name() const override { return "DashOp"; }
     262             : 
     263           0 :     SkString dumpInfo() const override {
     264           0 :         SkString string;
     265           0 :         for (const auto& geo : fLines) {
     266           0 :             string.appendf("Pt0: [%.2f, %.2f], Pt1: [%.2f, %.2f], Width: %.2f, Ival0: %.2f, "
     267             :                            "Ival1 : %.2f, Phase: %.2f\n",
     268           0 :                            geo.fPtsRot[0].fX, geo.fPtsRot[0].fY,
     269           0 :                            geo.fPtsRot[1].fX, geo.fPtsRot[1].fY,
     270           0 :                            geo.fSrcStrokeWidth,
     271           0 :                            geo.fIntervals[0],
     272           0 :                            geo.fIntervals[1],
     273           0 :                            geo.fPhase);
     274             :         }
     275           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     276           0 :         string.append(INHERITED::dumpInfo());
     277           0 :         return string;
     278             :     }
     279             : 
     280             : private:
     281           0 :     DashOp(const LineData& geometry, GrColor color, SkPaint::Cap cap, AAMode aaMode, bool fullDash)
     282           0 :             : INHERITED(ClassID()), fColor(color), fCap(cap), fAAMode(aaMode), fFullDash(fullDash) {
     283           0 :         fLines.push_back(geometry);
     284             : 
     285             :         // compute bounds
     286           0 :         SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
     287           0 :         SkScalar xBloat = SkPaint::kButt_Cap == cap ? 0 : halfStrokeWidth;
     288             :         SkRect bounds;
     289           0 :         bounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
     290           0 :         bounds.outset(xBloat, halfStrokeWidth);
     291             : 
     292             :         // Note, we actually create the combined matrix here, and save the work
     293           0 :         SkMatrix& combinedMatrix = fLines[0].fSrcRotInv;
     294           0 :         combinedMatrix.postConcat(geometry.fViewMatrix);
     295             : 
     296           0 :         IsZeroArea zeroArea = geometry.fSrcStrokeWidth ? IsZeroArea::kNo : IsZeroArea::kYes;
     297           0 :         HasAABloat aaBloat = (aaMode == AAMode::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
     298           0 :         this->setTransformedBounds(bounds, combinedMatrix, aaBloat, zeroArea);
     299           0 :     }
     300             : 
     301           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     302             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     303           0 :         color->setToConstant(fColor);
     304           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     305           0 :     }
     306             : 
     307           0 :     void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
     308           0 :         optimizations.getOverrideColorIfSet(&fColor);
     309             : 
     310           0 :         fUsesLocalCoords = optimizations.readsLocalCoords();
     311           0 :     }
     312             : 
     313             :     struct DashDraw {
     314           0 :         DashDraw(const LineData& geo) {
     315           0 :             memcpy(fPtsRot, geo.fPtsRot, sizeof(geo.fPtsRot));
     316           0 :             memcpy(fIntervals, geo.fIntervals, sizeof(geo.fIntervals));
     317           0 :             fPhase = geo.fPhase;
     318           0 :         }
     319             :         SkPoint fPtsRot[2];
     320             :         SkScalar fIntervals[2];
     321             :         SkScalar fPhase;
     322             :         SkScalar fStartOffset;
     323             :         SkScalar fStrokeWidth;
     324             :         SkScalar fLineLength;
     325             :         SkScalar fHalfDevStroke;
     326             :         SkScalar fDevBloatX;
     327             :         SkScalar fDevBloatY;
     328             :         bool fLineDone;
     329             :         bool fHasStartRect;
     330             :         bool fHasEndRect;
     331             :     };
     332             : 
     333           0 :     void onPrepareDraws(Target* target) const override {
     334           0 :         int instanceCount = fLines.count();
     335           0 :         SkPaint::Cap cap = this->cap();
     336           0 :         bool isRoundCap = SkPaint::kRound_Cap == cap;
     337           0 :         DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
     338             : 
     339           0 :         sk_sp<GrGeometryProcessor> gp;
     340           0 :         if (this->fullDash()) {
     341           0 :             gp = make_dash_gp(this->color(), this->aaMode(), capType, this->viewMatrix(),
     342           0 :                               this->usesLocalCoords());
     343             :         } else {
     344             :             // Set up the vertex data for the line and start/end dashes
     345             :             using namespace GrDefaultGeoProcFactory;
     346           0 :             Color color(this->color());
     347           0 :             LocalCoords::Type localCoordsType = this->usesLocalCoords()
     348           0 :                                                         ? LocalCoords::kUsePosition_Type
     349           0 :                                                         : LocalCoords::kUnused_Type;
     350           0 :             gp = MakeForDeviceSpace(color, Coverage::kSolid_Type, localCoordsType,
     351           0 :                                     this->viewMatrix());
     352             :         }
     353             : 
     354           0 :         if (!gp) {
     355           0 :             SkDebugf("Could not create GrGeometryProcessor\n");
     356           0 :             return;
     357             :         }
     358             : 
     359             :         // useAA here means Edge AA or MSAA
     360           0 :         bool useAA = this->aaMode() != AAMode::kNone;
     361           0 :         bool fullDash = this->fullDash();
     362             : 
     363             :         // We do two passes over all of the dashes.  First we setup the start, end, and bounds,
     364             :         // rectangles.  We preserve all of this work in the rects / draws arrays below.  Then we
     365             :         // iterate again over these decomposed dashes to generate vertices
     366             :         static const int kNumStackDashes = 128;
     367           0 :         SkSTArray<kNumStackDashes, SkRect, true> rects;
     368           0 :         SkSTArray<kNumStackDashes, DashDraw, true> draws;
     369             : 
     370           0 :         int totalRectCount = 0;
     371           0 :         int rectOffset = 0;
     372           0 :         rects.push_back_n(3 * instanceCount);
     373           0 :         for (int i = 0; i < instanceCount; i++) {
     374           0 :             const LineData& args = fLines[i];
     375             : 
     376           0 :             DashDraw& draw = draws.push_back(args);
     377             : 
     378           0 :             bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth;
     379             : 
     380             :             // We always want to at least stroke out half a pixel on each side in device space
     381             :             // so 0.5f / perpScale gives us this min in src space
     382             :             SkScalar halfSrcStroke =
     383           0 :                     SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale);
     384             : 
     385             :             SkScalar strokeAdj;
     386           0 :             if (!hasCap) {
     387           0 :                 strokeAdj = 0.f;
     388             :             } else {
     389           0 :                 strokeAdj = halfSrcStroke;
     390             :             }
     391             : 
     392           0 :             SkScalar startAdj = 0;
     393             : 
     394           0 :             bool lineDone = false;
     395             : 
     396             :             // Too simplify the algorithm, we always push back rects for start and end rect.
     397             :             // Otherwise we'd have to track start / end rects for each individual geometry
     398           0 :             SkRect& bounds = rects[rectOffset++];
     399           0 :             SkRect& startRect = rects[rectOffset++];
     400           0 :             SkRect& endRect = rects[rectOffset++];
     401             : 
     402           0 :             bool hasStartRect = false;
     403             :             // If we are using AA, check to see if we are drawing a partial dash at the start. If so
     404             :             // draw it separately here and adjust our start point accordingly
     405           0 :             if (useAA) {
     406           0 :                 if (draw.fPhase > 0 && draw.fPhase < draw.fIntervals[0]) {
     407             :                     SkPoint startPts[2];
     408           0 :                     startPts[0] = draw.fPtsRot[0];
     409           0 :                     startPts[1].fY = startPts[0].fY;
     410           0 :                     startPts[1].fX = SkMinScalar(startPts[0].fX + draw.fIntervals[0] - draw.fPhase,
     411             :                                                  draw.fPtsRot[1].fX);
     412           0 :                     startRect.set(startPts, 2);
     413           0 :                     startRect.outset(strokeAdj, halfSrcStroke);
     414             : 
     415           0 :                     hasStartRect = true;
     416           0 :                     startAdj = draw.fIntervals[0] + draw.fIntervals[1] - draw.fPhase;
     417             :                 }
     418             :             }
     419             : 
     420             :             // adjustments for start and end of bounding rect so we only draw dash intervals
     421             :             // contained in the original line segment.
     422           0 :             startAdj += calc_start_adjustment(draw.fIntervals, draw.fPhase);
     423           0 :             if (startAdj != 0) {
     424           0 :                 draw.fPtsRot[0].fX += startAdj;
     425           0 :                 draw.fPhase = 0;
     426             :             }
     427           0 :             SkScalar endingInterval = 0;
     428           0 :             SkScalar endAdj = calc_end_adjustment(draw.fIntervals, draw.fPtsRot, draw.fPhase,
     429           0 :                                                   &endingInterval);
     430           0 :             draw.fPtsRot[1].fX -= endAdj;
     431           0 :             if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
     432           0 :                 lineDone = true;
     433             :             }
     434             : 
     435           0 :             bool hasEndRect = false;
     436             :             // If we are using AA, check to see if we are drawing a partial dash at then end. If so
     437             :             // draw it separately here and adjust our end point accordingly
     438           0 :             if (useAA && !lineDone) {
     439             :                 // If we adjusted the end then we will not be drawing a partial dash at the end.
     440             :                 // If we didn't adjust the end point then we just need to make sure the ending
     441             :                 // dash isn't a full dash
     442           0 :                 if (0 == endAdj && endingInterval != draw.fIntervals[0]) {
     443             :                     SkPoint endPts[2];
     444           0 :                     endPts[1] = draw.fPtsRot[1];
     445           0 :                     endPts[0].fY = endPts[1].fY;
     446           0 :                     endPts[0].fX = endPts[1].fX - endingInterval;
     447             : 
     448           0 :                     endRect.set(endPts, 2);
     449           0 :                     endRect.outset(strokeAdj, halfSrcStroke);
     450             : 
     451           0 :                     hasEndRect = true;
     452           0 :                     endAdj = endingInterval + draw.fIntervals[1];
     453             : 
     454           0 :                     draw.fPtsRot[1].fX -= endAdj;
     455           0 :                     if (draw.fPtsRot[0].fX >= draw.fPtsRot[1].fX) {
     456           0 :                         lineDone = true;
     457             :                     }
     458             :                 }
     459             :             }
     460             : 
     461           0 :             if (startAdj != 0) {
     462           0 :                 draw.fPhase = 0;
     463             :             }
     464             : 
     465             :             // Change the dashing info from src space into device space
     466           0 :             SkScalar* devIntervals = draw.fIntervals;
     467           0 :             devIntervals[0] = draw.fIntervals[0] * args.fParallelScale;
     468           0 :             devIntervals[1] = draw.fIntervals[1] * args.fParallelScale;
     469           0 :             SkScalar devPhase = draw.fPhase * args.fParallelScale;
     470           0 :             SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
     471             : 
     472           0 :             if ((strokeWidth < 1.f && useAA) || 0.f == strokeWidth) {
     473           0 :                 strokeWidth = 1.f;
     474             :             }
     475             : 
     476           0 :             SkScalar halfDevStroke = strokeWidth * 0.5f;
     477             : 
     478           0 :             if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) {
     479             :                 // add cap to on interval and remove from off interval
     480           0 :                 devIntervals[0] += strokeWidth;
     481           0 :                 devIntervals[1] -= strokeWidth;
     482             :             }
     483           0 :             SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
     484             : 
     485             :             // For EdgeAA, we bloat in X & Y for both square and round caps.
     486             :             // For MSAA, we don't bloat at all for square caps, and bloat in Y only for round caps.
     487           0 :             SkScalar devBloatX = this->aaMode() == AAMode::kCoverage ? 0.5f : 0.0f;
     488             :             SkScalar devBloatY;
     489           0 :             if (SkPaint::kRound_Cap == cap && this->aaMode() == AAMode::kCoverageWithMSAA) {
     490           0 :                 devBloatY = 0.5f;
     491             :             } else {
     492           0 :                 devBloatY = devBloatX;
     493             :             }
     494             : 
     495           0 :             SkScalar bloatX = devBloatX / args.fParallelScale;
     496           0 :             SkScalar bloatY = devBloatY / args.fPerpendicularScale;
     497             : 
     498           0 :             if (devIntervals[1] <= 0.f && useAA) {
     499             :                 // Case when we end up drawing a solid AA rect
     500             :                 // Reset the start rect to draw this single solid rect
     501             :                 // but it requires to upload a new intervals uniform so we can mimic
     502             :                 // one giant dash
     503           0 :                 draw.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
     504           0 :                 draw.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
     505           0 :                 startRect.set(draw.fPtsRot, 2);
     506           0 :                 startRect.outset(strokeAdj, halfSrcStroke);
     507           0 :                 hasStartRect = true;
     508           0 :                 hasEndRect = false;
     509           0 :                 lineDone = true;
     510             : 
     511             :                 SkPoint devicePts[2];
     512           0 :                 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
     513           0 :                 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
     514           0 :                 if (hasCap) {
     515           0 :                     lineLength += 2.f * halfDevStroke;
     516             :                 }
     517           0 :                 devIntervals[0] = lineLength;
     518             :             }
     519             : 
     520           0 :             totalRectCount += !lineDone ? 1 : 0;
     521           0 :             totalRectCount += hasStartRect ? 1 : 0;
     522           0 :             totalRectCount += hasEndRect ? 1 : 0;
     523             : 
     524           0 :             if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) {
     525             :                 // need to adjust this for round caps to correctly set the dashPos attrib on
     526             :                 // vertices
     527           0 :                 startOffset -= halfDevStroke;
     528             :             }
     529             : 
     530           0 :             if (!lineDone) {
     531             :                 SkPoint devicePts[2];
     532           0 :                 args.fViewMatrix.mapPoints(devicePts, draw.fPtsRot, 2);
     533           0 :                 draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
     534           0 :                 if (hasCap) {
     535           0 :                     draw.fLineLength += 2.f * halfDevStroke;
     536             :                 }
     537             : 
     538           0 :                 bounds.set(draw.fPtsRot[0].fX, draw.fPtsRot[0].fY,
     539           0 :                            draw.fPtsRot[1].fX, draw.fPtsRot[1].fY);
     540           0 :                 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
     541             :             }
     542             : 
     543           0 :             if (hasStartRect) {
     544           0 :                 SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
     545           0 :                 startRect.outset(bloatX, bloatY);
     546             :             }
     547             : 
     548           0 :             if (hasEndRect) {
     549           0 :                 SkASSERT(useAA);  // so that we know bloatX and bloatY have been set
     550           0 :                 endRect.outset(bloatX, bloatY);
     551             :             }
     552             : 
     553           0 :             draw.fStartOffset = startOffset;
     554           0 :             draw.fDevBloatX = devBloatX;
     555           0 :             draw.fDevBloatY = devBloatY;
     556           0 :             draw.fHalfDevStroke = halfDevStroke;
     557           0 :             draw.fStrokeWidth = strokeWidth;
     558           0 :             draw.fHasStartRect = hasStartRect;
     559           0 :             draw.fLineDone = lineDone;
     560           0 :             draw.fHasEndRect = hasEndRect;
     561             :         }
     562             : 
     563           0 :         if (!totalRectCount) {
     564           0 :             return;
     565             :         }
     566             : 
     567           0 :         QuadHelper helper;
     568           0 :         void* vertices = helper.init(target, gp->getVertexStride(), totalRectCount);
     569           0 :         if (!vertices) {
     570           0 :             return;
     571             :         }
     572             : 
     573           0 :         int curVIdx = 0;
     574           0 :         int rectIndex = 0;
     575           0 :         for (int i = 0; i < instanceCount; i++) {
     576           0 :             const LineData& geom = fLines[i];
     577             : 
     578           0 :             if (!draws[i].fLineDone) {
     579           0 :                 if (fullDash) {
     580           0 :                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
     581           0 :                                       draws[i].fStartOffset, draws[i].fDevBloatX,
     582           0 :                                       draws[i].fDevBloatY, draws[i].fLineLength,
     583           0 :                                       draws[i].fHalfDevStroke, draws[i].fIntervals[0],
     584           0 :                                       draws[i].fIntervals[1], draws[i].fStrokeWidth,
     585           0 :                                       capType, gp->getVertexStride());
     586             :                 } else {
     587           0 :                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
     588           0 :                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
     589           0 :                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
     590             :                 }
     591           0 :                 curVIdx += 4;
     592             :             }
     593           0 :             rectIndex++;
     594             : 
     595           0 :             if (draws[i].fHasStartRect) {
     596           0 :                 if (fullDash) {
     597           0 :                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
     598           0 :                                       draws[i].fStartOffset, draws[i].fDevBloatX,
     599           0 :                                       draws[i].fDevBloatY, draws[i].fIntervals[0],
     600           0 :                                       draws[i].fHalfDevStroke, draws[i].fIntervals[0],
     601           0 :                                       draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
     602           0 :                                       gp->getVertexStride());
     603             :                 } else {
     604           0 :                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
     605           0 :                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
     606           0 :                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
     607             :                 }
     608           0 :                 curVIdx += 4;
     609             :             }
     610           0 :             rectIndex++;
     611             : 
     612           0 :             if (draws[i].fHasEndRect) {
     613           0 :                 if (fullDash) {
     614           0 :                     setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
     615           0 :                                       draws[i].fStartOffset, draws[i].fDevBloatX,
     616           0 :                                       draws[i].fDevBloatY, draws[i].fIntervals[0],
     617           0 :                                       draws[i].fHalfDevStroke, draws[i].fIntervals[0],
     618           0 :                                       draws[i].fIntervals[1], draws[i].fStrokeWidth, capType,
     619           0 :                                       gp->getVertexStride());
     620             :                 } else {
     621           0 :                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
     622           0 :                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
     623           0 :                     setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
     624             :                 }
     625           0 :                 curVIdx += 4;
     626             :             }
     627           0 :             rectIndex++;
     628             :         }
     629           0 :         SkASSERT(0 == (curVIdx % 4) && (curVIdx / 4) == totalRectCount);
     630           0 :         helper.recordDraw(target, gp.get(), this->pipeline());
     631             :     }
     632             : 
     633           0 :     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
     634           0 :         DashOp* that = t->cast<DashOp>();
     635           0 :         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     636             :                                     that->bounds(), caps)) {
     637           0 :             return false;
     638             :         }
     639             : 
     640           0 :         if (this->aaMode() != that->aaMode()) {
     641           0 :             return false;
     642             :         }
     643             : 
     644           0 :         if (this->fullDash() != that->fullDash()) {
     645           0 :             return false;
     646             :         }
     647             : 
     648           0 :         if (this->cap() != that->cap()) {
     649           0 :             return false;
     650             :         }
     651             : 
     652             :         // TODO vertex color
     653           0 :         if (this->color() != that->color()) {
     654           0 :             return false;
     655             :         }
     656             : 
     657           0 :         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
     658           0 :         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
     659           0 :             return false;
     660             :         }
     661             : 
     662           0 :         fLines.push_back_n(that->fLines.count(), that->fLines.begin());
     663           0 :         this->joinBounds(*that);
     664           0 :         return true;
     665             :     }
     666             : 
     667           0 :     GrColor color() const { return fColor; }
     668           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     669           0 :     const SkMatrix& viewMatrix() const { return fLines[0].fViewMatrix; }
     670           0 :     AAMode aaMode() const { return fAAMode; }
     671           0 :     bool fullDash() const { return fFullDash; }
     672           0 :     SkPaint::Cap cap() const { return fCap; }
     673             : 
     674             :     static const int kVertsPerDash = 4;
     675             :     static const int kIndicesPerDash = 6;
     676             : 
     677             :     GrColor fColor;
     678             :     bool fUsesLocalCoords;
     679             :     SkPaint::Cap fCap;
     680             :     AAMode fAAMode;
     681             :     bool fFullDash;
     682             :     SkSTArray<1, LineData, true> fLines;
     683             : 
     684             :     typedef GrLegacyMeshDrawOp INHERITED;
     685             : };
     686             : 
     687           0 : std::unique_ptr<GrLegacyMeshDrawOp> GrDashOp::MakeDashLineOp(GrColor color,
     688             :                                                              const SkMatrix& viewMatrix,
     689             :                                                              const SkPoint pts[2],
     690             :                                                              AAMode aaMode,
     691             :                                                              const GrStyle& style) {
     692           0 :     SkASSERT(GrDashOp::CanDrawDashLine(pts, style, viewMatrix));
     693           0 :     const SkScalar* intervals = style.dashIntervals();
     694           0 :     SkScalar phase = style.dashPhase();
     695             : 
     696           0 :     SkPaint::Cap cap = style.strokeRec().getCap();
     697             : 
     698             :     DashOp::LineData lineData;
     699           0 :     lineData.fSrcStrokeWidth = style.strokeRec().getWidth();
     700             : 
     701             :     // the phase should be normalized to be [0, sum of all intervals)
     702           0 :     SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
     703             : 
     704             :     // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
     705           0 :     if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
     706             :         SkMatrix rotMatrix;
     707           0 :         align_to_x_axis(pts, &rotMatrix, lineData.fPtsRot);
     708           0 :         if (!rotMatrix.invert(&lineData.fSrcRotInv)) {
     709           0 :             SkDebugf("Failed to create invertible rotation matrix!\n");
     710           0 :             return nullptr;
     711           0 :         }
     712             :     } else {
     713           0 :         lineData.fSrcRotInv.reset();
     714           0 :         memcpy(lineData.fPtsRot, pts, 2 * sizeof(SkPoint));
     715             :     }
     716             : 
     717             :     // Scale corrections of intervals and stroke from view matrix
     718             :     calc_dash_scaling(&lineData.fParallelScale, &lineData.fPerpendicularScale, viewMatrix,
     719           0 :                       lineData.fPtsRot);
     720             : 
     721           0 :     SkScalar offInterval = intervals[1] * lineData.fParallelScale;
     722           0 :     SkScalar strokeWidth = lineData.fSrcStrokeWidth * lineData.fPerpendicularScale;
     723             : 
     724           0 :     if (SkPaint::kSquare_Cap == cap && 0 != lineData.fSrcStrokeWidth) {
     725             :         // add cap to on interveal and remove from off interval
     726           0 :         offInterval -= strokeWidth;
     727             :     }
     728             : 
     729             :     // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
     730           0 :     bool fullDash = offInterval > 0.f || aaMode != AAMode::kNone;
     731             : 
     732           0 :     lineData.fViewMatrix = viewMatrix;
     733           0 :     lineData.fPhase = phase;
     734           0 :     lineData.fIntervals[0] = intervals[0];
     735           0 :     lineData.fIntervals[1] = intervals[1];
     736             : 
     737           0 :     return DashOp::Make(lineData, color, cap, aaMode, fullDash);
     738             : }
     739             : 
     740             : //////////////////////////////////////////////////////////////////////////////
     741             : 
     742             : class GLDashingCircleEffect;
     743             : 
     744             : /*
     745             :  * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
     746             :  * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
     747             :  * Both of the previous two parameters are in device space. This effect also requires the setting of
     748             :  * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
     749             :  * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
     750             :  * transform the line to be horizontal, with the start of line at the origin then shifted to the
     751             :  * right by half the off interval. The line then goes in the positive x direction.
     752             :  */
     753           0 : class DashingCircleEffect : public GrGeometryProcessor {
     754             : public:
     755             :     typedef SkPathEffect::DashInfo DashInfo;
     756             : 
     757             :     static sk_sp<GrGeometryProcessor> Make(GrColor,
     758             :                                            AAMode aaMode,
     759             :                                            const SkMatrix& localMatrix,
     760             :                                            bool usesLocalCoords);
     761             : 
     762           0 :     const char* name() const override { return "DashingCircleEffect"; }
     763             : 
     764           0 :     const Attribute* inPosition() const { return fInPosition; }
     765             : 
     766           0 :     const Attribute* inDashParams() const { return fInDashParams; }
     767             : 
     768           0 :     const Attribute* inCircleParams() const { return fInCircleParams; }
     769             : 
     770           0 :     AAMode aaMode() const { return fAAMode; }
     771             : 
     772           0 :     GrColor color() const { return fColor; }
     773             : 
     774           0 :     const SkMatrix& localMatrix() const { return fLocalMatrix; }
     775             : 
     776           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     777             : 
     778             :     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override;
     779             : 
     780             :     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
     781             : 
     782             : private:
     783             :     DashingCircleEffect(GrColor, AAMode aaMode, const SkMatrix& localMatrix,
     784             :                         bool usesLocalCoords);
     785             : 
     786             :     GrColor             fColor;
     787             :     SkMatrix            fLocalMatrix;
     788             :     bool                fUsesLocalCoords;
     789             :     AAMode              fAAMode;
     790             :     const Attribute*    fInPosition;
     791             :     const Attribute*    fInDashParams;
     792             :     const Attribute*    fInCircleParams;
     793             : 
     794             :     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
     795             : 
     796             :     typedef GrGeometryProcessor INHERITED;
     797             : };
     798             : 
     799             : //////////////////////////////////////////////////////////////////////////////
     800             : 
     801           0 : class GLDashingCircleEffect : public GrGLSLGeometryProcessor {
     802             : public:
     803             :     GLDashingCircleEffect();
     804             : 
     805             :     void onEmitCode(EmitArgs&, GrGPArgs*) override;
     806             : 
     807             :     static inline void GenKey(const GrGeometryProcessor&,
     808             :                               const GrShaderCaps&,
     809             :                               GrProcessorKeyBuilder*);
     810             : 
     811             :     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
     812             :                  FPCoordTransformIter&& transformIter) override;
     813             : private:
     814             :     UniformHandle fParamUniform;
     815             :     UniformHandle fColorUniform;
     816             :     GrColor       fColor;
     817             :     SkScalar      fPrevRadius;
     818             :     SkScalar      fPrevCenterX;
     819             :     SkScalar      fPrevIntervalLength;
     820             :     typedef GrGLSLGeometryProcessor INHERITED;
     821             : };
     822             : 
     823           0 : GLDashingCircleEffect::GLDashingCircleEffect() {
     824           0 :     fColor = GrColor_ILLEGAL;
     825           0 :     fPrevRadius = SK_ScalarMin;
     826           0 :     fPrevCenterX = SK_ScalarMin;
     827           0 :     fPrevIntervalLength = SK_ScalarMax;
     828           0 : }
     829             : 
     830           0 : void GLDashingCircleEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
     831           0 :     const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
     832           0 :     GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
     833           0 :     GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
     834           0 :     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
     835             : 
     836             :     // emit attributes
     837           0 :     varyingHandler->emitAttributes(dce);
     838             : 
     839             :     // XY are dashPos, Z is dashInterval
     840           0 :     GrGLSLVertToFrag dashParams(kVec3f_GrSLType);
     841           0 :     varyingHandler->addVarying("DashParam", &dashParams);
     842           0 :     vertBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.inDashParams()->fName);
     843             : 
     844             :     // x refers to circle radius - 0.5, y refers to cicle's center x coord
     845           0 :     GrGLSLVertToFrag circleParams(kVec2f_GrSLType);
     846           0 :     varyingHandler->addVarying("CircleParams", &circleParams);
     847           0 :     vertBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.inCircleParams()->fName);
     848             : 
     849           0 :     GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
     850             :     // Setup pass through color
     851           0 :     this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
     852             : 
     853             :     // Setup position
     854           0 :     this->setupPosition(vertBuilder, gpArgs, dce.inPosition()->fName);
     855             : 
     856             :     // emit transforms
     857           0 :     this->emitTransforms(vertBuilder,
     858             :                          varyingHandler,
     859             :                          uniformHandler,
     860             :                          gpArgs->fPositionVar,
     861           0 :                          dce.inPosition()->fName,
     862             :                          dce.localMatrix(),
     863           0 :                          args.fFPCoordTransformHandler);
     864             : 
     865             :     // transforms all points so that we can compare them to our test circle
     866           0 :     fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
     867             :                              dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
     868           0 :                              dashParams.fsIn());
     869           0 :     fragBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", dashParams.fsIn());
     870           0 :     fragBuilder->codeAppendf("vec2 center = vec2(%s.y, 0.0);", circleParams.fsIn());
     871           0 :     fragBuilder->codeAppend("float dist = length(center - fragPosShifted);");
     872           0 :     if (dce.aaMode() != AAMode::kNone) {
     873           0 :         fragBuilder->codeAppendf("float diff = dist - %s.x;", circleParams.fsIn());
     874           0 :         fragBuilder->codeAppend("diff = 1.0 - diff;");
     875           0 :         fragBuilder->codeAppend("float alpha = clamp(diff, 0.0, 1.0);");
     876             :     } else {
     877           0 :         fragBuilder->codeAppendf("float alpha = 1.0;");
     878           0 :         fragBuilder->codeAppendf("alpha *=  dist < %s.x + 0.5 ? 1.0 : 0.0;", circleParams.fsIn());
     879             :     }
     880           0 :     fragBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
     881           0 : }
     882             : 
     883           0 : void GLDashingCircleEffect::setData(const GrGLSLProgramDataManager& pdman,
     884             :                                     const GrPrimitiveProcessor& processor,
     885             :                                     FPCoordTransformIter&& transformIter)  {
     886           0 :     const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
     887           0 :     if (dce.color() != fColor) {
     888             :         float c[4];
     889           0 :         GrColorToRGBAFloat(dce.color(), c);
     890           0 :         pdman.set4fv(fColorUniform, 1, c);
     891           0 :         fColor = dce.color();
     892             :     }
     893           0 :     this->setTransformDataHelper(dce.localMatrix(), pdman, &transformIter);
     894           0 : }
     895             : 
     896           0 : void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
     897             :                                    const GrShaderCaps&,
     898             :                                    GrProcessorKeyBuilder* b) {
     899           0 :     const DashingCircleEffect& dce = gp.cast<DashingCircleEffect>();
     900           0 :     uint32_t key = 0;
     901           0 :     key |= dce.usesLocalCoords() && dce.localMatrix().hasPerspective() ? 0x1 : 0x0;
     902           0 :     key |= static_cast<uint32_t>(dce.aaMode()) << 1;
     903           0 :     b->add32(key);
     904           0 : }
     905             : 
     906             : //////////////////////////////////////////////////////////////////////////////
     907             : 
     908           0 : sk_sp<GrGeometryProcessor> DashingCircleEffect::Make(GrColor color,
     909             :                                                      AAMode aaMode,
     910             :                                                      const SkMatrix& localMatrix,
     911             :                                                      bool usesLocalCoords) {
     912             :     return sk_sp<GrGeometryProcessor>(
     913           0 :         new DashingCircleEffect(color, aaMode, localMatrix, usesLocalCoords));
     914             : }
     915             : 
     916           0 : void DashingCircleEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
     917             :                                               GrProcessorKeyBuilder* b) const {
     918           0 :     GLDashingCircleEffect::GenKey(*this, caps, b);
     919           0 : }
     920             : 
     921           0 : GrGLSLPrimitiveProcessor* DashingCircleEffect::createGLSLInstance(const GrShaderCaps&) const {
     922           0 :     return new GLDashingCircleEffect();
     923             : }
     924             : 
     925           0 : DashingCircleEffect::DashingCircleEffect(GrColor color,
     926             :                                          AAMode aaMode,
     927             :                                          const SkMatrix& localMatrix,
     928           0 :                                          bool usesLocalCoords)
     929             :     : fColor(color)
     930             :     , fLocalMatrix(localMatrix)
     931             :     , fUsesLocalCoords(usesLocalCoords)
     932           0 :     , fAAMode(aaMode) {
     933           0 :     this->initClassID<DashingCircleEffect>();
     934           0 :     fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
     935           0 :     fInDashParams = &this->addVertexAttrib("inDashParams", kVec3f_GrVertexAttribType);
     936           0 :     fInCircleParams = &this->addVertexAttrib("inCircleParams", kVec2f_GrVertexAttribType);
     937           0 : }
     938             : 
     939             : GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
     940             : 
     941             : #if GR_TEST_UTILS
     942           0 : sk_sp<GrGeometryProcessor> DashingCircleEffect::TestCreate(GrProcessorTestData* d) {
     943           0 :     AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
     944             :     return DashingCircleEffect::Make(GrRandomColor(d->fRandom),
     945             :                                     aaMode, GrTest::TestMatrix(d->fRandom),
     946           0 :                                     d->fRandom->nextBool());
     947             : }
     948             : #endif
     949             : 
     950             : //////////////////////////////////////////////////////////////////////////////
     951             : 
     952             : class GLDashingLineEffect;
     953             : 
     954             : /*
     955             :  * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
     956             :  * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
     957             :  * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
     958             :  * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
     959             :  * vertex coords (in device space) if we transform the line to be horizontal, with the start of
     960             :  * line at the origin then shifted to the right by half the off interval. The line then goes in the
     961             :  * positive x direction.
     962             :  */
     963           0 : class DashingLineEffect : public GrGeometryProcessor {
     964             : public:
     965             :     typedef SkPathEffect::DashInfo DashInfo;
     966             : 
     967             :     static sk_sp<GrGeometryProcessor> Make(GrColor,
     968             :                                            AAMode aaMode,
     969             :                                            const SkMatrix& localMatrix,
     970             :                                            bool usesLocalCoords);
     971             : 
     972           0 :     const char* name() const override { return "DashingEffect"; }
     973             : 
     974           0 :     const Attribute* inPosition() const { return fInPosition; }
     975             : 
     976           0 :     const Attribute* inDashParams() const { return fInDashParams; }
     977             : 
     978           0 :     const Attribute* inRectParams() const { return fInRectParams; }
     979             : 
     980           0 :     AAMode aaMode() const { return fAAMode; }
     981             : 
     982           0 :     GrColor color() const { return fColor; }
     983             : 
     984           0 :      const SkMatrix& localMatrix() const { return fLocalMatrix; }
     985             : 
     986           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     987             : 
     988             :     void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
     989             : 
     990             :     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
     991             : 
     992             : private:
     993             :     DashingLineEffect(GrColor, AAMode aaMode, const SkMatrix& localMatrix,
     994             :                       bool usesLocalCoords);
     995             : 
     996             :     GrColor             fColor;
     997             :     SkMatrix            fLocalMatrix;
     998             :     bool                fUsesLocalCoords;
     999             :     AAMode              fAAMode;
    1000             :     const Attribute*    fInPosition;
    1001             :     const Attribute*    fInDashParams;
    1002             :     const Attribute*    fInRectParams;
    1003             : 
    1004             :     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
    1005             : 
    1006             :     typedef GrGeometryProcessor INHERITED;
    1007             : };
    1008             : 
    1009             : //////////////////////////////////////////////////////////////////////////////
    1010             : 
    1011           0 : class GLDashingLineEffect : public GrGLSLGeometryProcessor {
    1012             : public:
    1013             :     GLDashingLineEffect();
    1014             : 
    1015             :     void onEmitCode(EmitArgs&, GrGPArgs*) override;
    1016             : 
    1017             :     static inline void GenKey(const GrGeometryProcessor&,
    1018             :                               const GrShaderCaps&,
    1019             :                               GrProcessorKeyBuilder*);
    1020             : 
    1021             :     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
    1022             :                  FPCoordTransformIter&& iter) override;
    1023             : 
    1024             : private:
    1025             :     GrColor       fColor;
    1026             :     UniformHandle fColorUniform;
    1027             :     typedef GrGLSLGeometryProcessor INHERITED;
    1028             : };
    1029             : 
    1030           0 : GLDashingLineEffect::GLDashingLineEffect() : fColor(GrColor_ILLEGAL) {}
    1031             : 
    1032           0 : void GLDashingLineEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
    1033           0 :     const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
    1034             : 
    1035           0 :     GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
    1036           0 :     GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
    1037           0 :     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
    1038             : 
    1039             :     // emit attributes
    1040           0 :     varyingHandler->emitAttributes(de);
    1041             : 
    1042             :     // XY refers to dashPos, Z is the dash interval length
    1043           0 :     GrGLSLVertToFrag inDashParams(kVec3f_GrSLType);
    1044           0 :     varyingHandler->addVarying("DashParams", &inDashParams, GrSLPrecision::kHigh_GrSLPrecision);
    1045           0 :     vertBuilder->codeAppendf("%s = %s;", inDashParams.vsOut(), de.inDashParams()->fName);
    1046             : 
    1047             :     // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
    1048             :     // respectively.
    1049           0 :     GrGLSLVertToFrag inRectParams(kVec4f_GrSLType);
    1050           0 :     varyingHandler->addVarying("RectParams", &inRectParams, GrSLPrecision::kHigh_GrSLPrecision);
    1051           0 :     vertBuilder->codeAppendf("%s = %s;", inRectParams.vsOut(), de.inRectParams()->fName);
    1052             : 
    1053           0 :     GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
    1054             :     // Setup pass through color
    1055           0 :     this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform);
    1056             : 
    1057             :     // Setup position
    1058           0 :     this->setupPosition(vertBuilder, gpArgs, de.inPosition()->fName);
    1059             : 
    1060             :     // emit transforms
    1061           0 :     this->emitTransforms(vertBuilder,
    1062             :                          varyingHandler,
    1063             :                          uniformHandler,
    1064             :                          gpArgs->fPositionVar,
    1065           0 :                          de.inPosition()->fName,
    1066             :                          de.localMatrix(),
    1067           0 :                          args.fFPCoordTransformHandler);
    1068             : 
    1069             :     // transforms all points so that we can compare them to our test rect
    1070           0 :     fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
    1071             :                              inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
    1072           0 :                              inDashParams.fsIn());
    1073           0 :     fragBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", inDashParams.fsIn());
    1074           0 :     if (de.aaMode() == AAMode::kCoverage) {
    1075             :         // The amount of coverage removed in x and y by the edges is computed as a pair of negative
    1076             :         // numbers, xSub and ySub.
    1077           0 :         fragBuilder->codeAppend("float xSub, ySub;");
    1078           0 :         fragBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
    1079           0 :         fragBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
    1080           0 :         fragBuilder->codeAppendf("ySub = min(fragPosShifted.y - %s.y, 0.0);", inRectParams.fsIn());
    1081           0 :         fragBuilder->codeAppendf("ySub += min(%s.w - fragPosShifted.y, 0.0);", inRectParams.fsIn());
    1082             :         // Now compute coverage in x and y and multiply them to get the fraction of the pixel
    1083             :         // covered.
    1084           0 :         fragBuilder->codeAppendf(
    1085           0 :             "float alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
    1086           0 :     } else if (de.aaMode() == AAMode::kCoverageWithMSAA) {
    1087             :         // For MSAA, we don't modulate the alpha by the Y distance, since MSAA coverage will handle
    1088             :         // AA on the the top and bottom edges. The shader is only responsible for intra-dash alpha.
    1089           0 :         fragBuilder->codeAppend("float xSub;");
    1090           0 :         fragBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
    1091           0 :         fragBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
    1092             :         // Now compute coverage in x to get the fraction of the pixel covered.
    1093           0 :         fragBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0));");
    1094             :     } else {
    1095             :         // Assuming the bounding geometry is tight so no need to check y values
    1096           0 :         fragBuilder->codeAppendf("float alpha = 1.0;");
    1097           0 :         fragBuilder->codeAppendf("alpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;",
    1098           0 :                                  inRectParams.fsIn());
    1099           0 :         fragBuilder->codeAppendf("alpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;",
    1100           0 :                                  inRectParams.fsIn());
    1101             :     }
    1102           0 :     fragBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage);
    1103           0 : }
    1104             : 
    1105           0 : void GLDashingLineEffect::setData(const GrGLSLProgramDataManager& pdman,
    1106             :                                   const GrPrimitiveProcessor& processor,
    1107             :                                   FPCoordTransformIter&& transformIter) {
    1108           0 :     const DashingLineEffect& de = processor.cast<DashingLineEffect>();
    1109           0 :     if (de.color() != fColor) {
    1110             :         float c[4];
    1111           0 :         GrColorToRGBAFloat(de.color(), c);
    1112           0 :         pdman.set4fv(fColorUniform, 1, c);
    1113           0 :         fColor = de.color();
    1114             :     }
    1115           0 :     this->setTransformDataHelper(de.localMatrix(), pdman, &transformIter);
    1116           0 : }
    1117             : 
    1118           0 : void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
    1119             :                                  const GrShaderCaps&,
    1120             :                                  GrProcessorKeyBuilder* b) {
    1121           0 :     const DashingLineEffect& de = gp.cast<DashingLineEffect>();
    1122           0 :     uint32_t key = 0;
    1123           0 :     key |= de.usesLocalCoords() && de.localMatrix().hasPerspective() ? 0x1 : 0x0;
    1124           0 :     key |= static_cast<int>(de.aaMode()) << 8;
    1125           0 :     b->add32(key);
    1126           0 : }
    1127             : 
    1128             : //////////////////////////////////////////////////////////////////////////////
    1129             : 
    1130           0 : sk_sp<GrGeometryProcessor> DashingLineEffect::Make(GrColor color,
    1131             :                                                    AAMode aaMode,
    1132             :                                                    const SkMatrix& localMatrix,
    1133             :                                                    bool usesLocalCoords) {
    1134             :     return sk_sp<GrGeometryProcessor>(
    1135           0 :         new DashingLineEffect(color, aaMode, localMatrix, usesLocalCoords));
    1136             : }
    1137             : 
    1138           0 : void DashingLineEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
    1139             :                                             GrProcessorKeyBuilder* b) const {
    1140           0 :     GLDashingLineEffect::GenKey(*this, caps, b);
    1141           0 : }
    1142             : 
    1143           0 : GrGLSLPrimitiveProcessor* DashingLineEffect::createGLSLInstance(const GrShaderCaps&) const {
    1144           0 :     return new GLDashingLineEffect();
    1145             : }
    1146             : 
    1147           0 : DashingLineEffect::DashingLineEffect(GrColor color,
    1148             :                                      AAMode aaMode,
    1149             :                                      const SkMatrix& localMatrix,
    1150           0 :                                      bool usesLocalCoords)
    1151             :     : fColor(color)
    1152             :     , fLocalMatrix(localMatrix)
    1153             :     , fUsesLocalCoords(usesLocalCoords)
    1154           0 :     , fAAMode(aaMode) {
    1155           0 :     this->initClassID<DashingLineEffect>();
    1156           0 :     fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
    1157           0 :     fInDashParams = &this->addVertexAttrib("inDashParams", kVec3f_GrVertexAttribType);
    1158           0 :     fInRectParams = &this->addVertexAttrib("inRect", kVec4f_GrVertexAttribType);
    1159           0 : }
    1160             : 
    1161             : GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
    1162             : 
    1163             : #if GR_TEST_UTILS
    1164           0 : sk_sp<GrGeometryProcessor> DashingLineEffect::TestCreate(GrProcessorTestData* d) {
    1165           0 :     AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
    1166             :     return DashingLineEffect::Make(GrRandomColor(d->fRandom),
    1167             :                                    aaMode, GrTest::TestMatrix(d->fRandom),
    1168           0 :                                    d->fRandom->nextBool());
    1169             : }
    1170             : #endif
    1171             : 
    1172             : //////////////////////////////////////////////////////////////////////////////
    1173             : 
    1174           0 : static sk_sp<GrGeometryProcessor> make_dash_gp(GrColor color,
    1175             :                                                AAMode aaMode,
    1176             :                                                DashCap cap,
    1177             :                                                const SkMatrix& viewMatrix,
    1178             :                                                bool usesLocalCoords) {
    1179             :     SkMatrix invert;
    1180           0 :     if (usesLocalCoords && !viewMatrix.invert(&invert)) {
    1181           0 :         SkDebugf("Failed to invert\n");
    1182           0 :         return nullptr;
    1183             :     }
    1184             : 
    1185           0 :     switch (cap) {
    1186             :         case kRound_DashCap:
    1187           0 :             return DashingCircleEffect::Make(color, aaMode, invert, usesLocalCoords);
    1188             :         case kNonRound_DashCap:
    1189           0 :             return DashingLineEffect::Make(color, aaMode, invert, usesLocalCoords);
    1190             :     }
    1191           0 :     return nullptr;
    1192             : }
    1193             : 
    1194             : /////////////////////////////////////////////////////////////////////////////////////////////////
    1195             : 
    1196             : #if GR_TEST_UTILS
    1197             : 
    1198           0 : DRAW_OP_TEST_DEFINE(DashOp) {
    1199           0 :     GrColor color = GrRandomColor(random);
    1200           0 :     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
    1201           0 :     AAMode aaMode = static_cast<AAMode>(random->nextULessThan(GrDashOp::kAAModeCnt));
    1202             : 
    1203             :     // We can only dash either horizontal or vertical lines
    1204             :     SkPoint pts[2];
    1205           0 :     if (random->nextBool()) {
    1206             :         // vertical
    1207           0 :         pts[0].fX = 1.f;
    1208           0 :         pts[0].fY = random->nextF() * 10.f;
    1209           0 :         pts[1].fX = 1.f;
    1210           0 :         pts[1].fY = random->nextF() * 10.f;
    1211             :     } else {
    1212             :         // horizontal
    1213           0 :         pts[0].fX = random->nextF() * 10.f;
    1214           0 :         pts[0].fY = 1.f;
    1215           0 :         pts[1].fX = random->nextF() * 10.f;
    1216           0 :         pts[1].fY = 1.f;
    1217             :     }
    1218             : 
    1219             :     // pick random cap
    1220           0 :     SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount));
    1221             : 
    1222             :     SkScalar intervals[2];
    1223             : 
    1224             :     // We can only dash with the following intervals
    1225             :     enum Intervals {
    1226             :         kOpenOpen_Intervals ,
    1227             :         kOpenClose_Intervals,
    1228             :         kCloseOpen_Intervals,
    1229             :     };
    1230             : 
    1231             :     Intervals intervalType = SkPaint::kRound_Cap ?
    1232             :                              kOpenClose_Intervals :
    1233           0 :                              Intervals(random->nextULessThan(kCloseOpen_Intervals + 1));
    1234             :     static const SkScalar kIntervalMin = 0.1f;
    1235             :     static const SkScalar kIntervalMax = 10.f;
    1236           0 :     switch (intervalType) {
    1237             :         case kOpenOpen_Intervals:
    1238           0 :             intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
    1239           0 :             intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
    1240           0 :             break;
    1241             :         case kOpenClose_Intervals:
    1242           0 :             intervals[0] = 0.f;
    1243           0 :             intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
    1244           0 :             break;
    1245             :         case kCloseOpen_Intervals:
    1246           0 :             intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
    1247           0 :             intervals[1] = 0.f;
    1248           0 :             break;
    1249             : 
    1250             :     }
    1251             : 
    1252             :     // phase is 0 < sum (i0, i1)
    1253           0 :     SkScalar phase = random->nextRangeScalar(0, intervals[0] + intervals[1]);
    1254             : 
    1255           0 :     SkPaint p;
    1256           0 :     p.setStyle(SkPaint::kStroke_Style);
    1257           0 :     p.setStrokeWidth(SkIntToScalar(1));
    1258           0 :     p.setStrokeCap(cap);
    1259           0 :     p.setPathEffect(GrTest::TestDashPathEffect::Make(intervals, 2, phase));
    1260             : 
    1261           0 :     GrStyle style(p);
    1262             : 
    1263           0 :     return GrDashOp::MakeDashLineOp(color, viewMatrix, pts, aaMode, style);
    1264             : }
    1265             : 
    1266             : #endif

Generated by: LCOV version 1.13