GrDashOp.cpp 51 KB

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