GrCCStrokeGeometry.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. /*
  2. * Copyright 2018 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 "src/gpu/ccpr/GrCCStrokeGeometry.h"
  8. #include "include/core/SkStrokeRec.h"
  9. #include "include/private/SkNx.h"
  10. #include "src/core/SkGeometry.h"
  11. #include "src/core/SkMathPriv.h"
  12. // This is the maximum distance in pixels that we can stray from the edge of a stroke when
  13. // converting it to flat line segments.
  14. static constexpr float kMaxErrorFromLinearization = 1/8.f;
  15. static inline float length(const Sk2f& n) {
  16. Sk2f nn = n*n;
  17. return SkScalarSqrt(nn[0] + nn[1]);
  18. }
  19. static inline Sk2f normalize(const Sk2f& v) {
  20. Sk2f vv = v*v;
  21. vv += SkNx_shuffle<1,0>(vv);
  22. return v * vv.rsqrt();
  23. }
  24. static inline void transpose(const Sk2f& a, const Sk2f& b, Sk2f* X, Sk2f* Y) {
  25. float transpose[4];
  26. a.store(transpose);
  27. b.store(transpose+2);
  28. Sk2f::Load2(transpose, X, Y);
  29. }
  30. static inline void normalize2(const Sk2f& v0, const Sk2f& v1, SkPoint out[2]) {
  31. Sk2f X, Y;
  32. transpose(v0, v1, &X, &Y);
  33. Sk2f invlength = (X*X + Y*Y).rsqrt();
  34. Sk2f::Store2(out, Y * invlength, -X * invlength);
  35. }
  36. static inline float calc_curvature_costheta(const Sk2f& leftTan, const Sk2f& rightTan) {
  37. Sk2f X, Y;
  38. transpose(leftTan, rightTan, &X, &Y);
  39. Sk2f invlength = (X*X + Y*Y).rsqrt();
  40. Sk2f dotprod = leftTan * rightTan;
  41. return (dotprod[0] + dotprod[1]) * invlength[0] * invlength[1];
  42. }
  43. static GrCCStrokeGeometry::Verb join_verb_from_join(SkPaint::Join join) {
  44. using Verb = GrCCStrokeGeometry::Verb;
  45. switch (join) {
  46. case SkPaint::kBevel_Join:
  47. return Verb::kBevelJoin;
  48. case SkPaint::kMiter_Join:
  49. return Verb::kMiterJoin;
  50. case SkPaint::kRound_Join:
  51. return Verb::kRoundJoin;
  52. }
  53. SK_ABORT("Invalid SkPaint::Join.");
  54. return Verb::kBevelJoin;
  55. }
  56. void GrCCStrokeGeometry::beginPath(const SkStrokeRec& stroke, float strokeDevWidth,
  57. InstanceTallies* tallies) {
  58. SkASSERT(!fInsideContour);
  59. // Client should have already converted the stroke to device space (i.e. width=1 for hairline).
  60. SkASSERT(strokeDevWidth > 0);
  61. fCurrStrokeRadius = strokeDevWidth/2;
  62. fCurrStrokeJoinVerb = join_verb_from_join(stroke.getJoin());
  63. fCurrStrokeCapType = stroke.getCap();
  64. fCurrStrokeTallies = tallies;
  65. if (Verb::kMiterJoin == fCurrStrokeJoinVerb) {
  66. // We implement miters by placing a triangle-shaped cap on top of a bevel join. Convert the
  67. // "miter limit" to how tall that triangle cap can be.
  68. float m = stroke.getMiter();
  69. fMiterMaxCapHeightOverWidth = .5f * SkScalarSqrt(m*m - 1);
  70. }
  71. // Find the angle of curvature where the arc height above a simple line from point A to point B
  72. // is equal to kMaxErrorFromLinearization.
  73. float r = SkTMax(1 - kMaxErrorFromLinearization / fCurrStrokeRadius, 0.f);
  74. fMaxCurvatureCosTheta = 2*r*r - 1;
  75. fCurrContourFirstPtIdx = -1;
  76. fCurrContourFirstNormalIdx = -1;
  77. fVerbs.push_back(Verb::kBeginPath);
  78. }
  79. void GrCCStrokeGeometry::moveTo(SkPoint pt) {
  80. SkASSERT(!fInsideContour);
  81. fCurrContourFirstPtIdx = fPoints.count();
  82. fCurrContourFirstNormalIdx = fNormals.count();
  83. fPoints.push_back(pt);
  84. SkDEBUGCODE(fInsideContour = true);
  85. }
  86. void GrCCStrokeGeometry::lineTo(SkPoint pt) {
  87. SkASSERT(fInsideContour);
  88. this->lineTo(fCurrStrokeJoinVerb, pt);
  89. }
  90. void GrCCStrokeGeometry::lineTo(Verb leftJoinVerb, SkPoint pt) {
  91. Sk2f tan = Sk2f::Load(&pt) - Sk2f::Load(&fPoints.back());
  92. if ((tan == 0).allTrue()) {
  93. return;
  94. }
  95. tan = normalize(tan);
  96. SkVector n = SkVector::Make(tan[1], -tan[0]);
  97. this->recordLeftJoinIfNotEmpty(leftJoinVerb, n);
  98. fNormals.push_back(n);
  99. this->recordStroke(Verb::kLinearStroke, 0);
  100. fPoints.push_back(pt);
  101. }
  102. void GrCCStrokeGeometry::quadraticTo(const SkPoint P[3]) {
  103. SkASSERT(fInsideContour);
  104. this->quadraticTo(fCurrStrokeJoinVerb, P, SkFindQuadMaxCurvature(P));
  105. }
  106. // Wang's formula for quadratics (1985) gives us the number of evenly spaced (in the parametric
  107. // sense) line segments that are guaranteed to be within a distance of "kMaxErrorFromLinearization"
  108. // from the actual curve.
  109. static inline float wangs_formula_quadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
  110. static constexpr float k = 2 / (8 * kMaxErrorFromLinearization);
  111. float f = SkScalarSqrt(k * length(p2 - p1*2 + p0));
  112. return SkScalarCeilToInt(f);
  113. }
  114. void GrCCStrokeGeometry::quadraticTo(Verb leftJoinVerb, const SkPoint P[3], float maxCurvatureT) {
  115. Sk2f p0 = Sk2f::Load(P);
  116. Sk2f p1 = Sk2f::Load(P+1);
  117. Sk2f p2 = Sk2f::Load(P+2);
  118. Sk2f tan0 = p1 - p0;
  119. Sk2f tan1 = p2 - p1;
  120. // Snap to a "lineTo" if the control point is so close to an endpoint that FP error will become
  121. // an issue.
  122. if ((tan0.abs() < SK_ScalarNearlyZero).allTrue() || // p0 ~= p1
  123. (tan1.abs() < SK_ScalarNearlyZero).allTrue()) { // p1 ~= p2
  124. this->lineTo(leftJoinVerb, P[2]);
  125. return;
  126. }
  127. SkPoint normals[2];
  128. normalize2(tan0, tan1, normals);
  129. // Decide how many flat line segments to chop the curve into.
  130. int numSegments = wangs_formula_quadratic(p0, p1, p2);
  131. numSegments = SkTMin(numSegments, 1 << kMaxNumLinearSegmentsLog2);
  132. if (numSegments <= 1) {
  133. this->rotateTo(leftJoinVerb, normals[0]);
  134. this->lineTo(Verb::kInternalRoundJoin, P[2]);
  135. this->rotateTo(Verb::kInternalRoundJoin, normals[1]);
  136. return;
  137. }
  138. // At + B gives a vector tangent to the quadratic.
  139. Sk2f A = p0 - p1*2 + p2;
  140. Sk2f B = p1 - p0;
  141. // Find a line segment that crosses max curvature.
  142. float segmentLength = SkScalarInvert(numSegments);
  143. float leftT = maxCurvatureT - segmentLength/2;
  144. float rightT = maxCurvatureT + segmentLength/2;
  145. Sk2f leftTan, rightTan;
  146. if (leftT <= 0) {
  147. leftT = 0;
  148. leftTan = tan0;
  149. rightT = segmentLength;
  150. rightTan = A*rightT + B;
  151. } else if (rightT >= 1) {
  152. leftT = 1 - segmentLength;
  153. leftTan = A*leftT + B;
  154. rightT = 1;
  155. rightTan = tan1;
  156. } else {
  157. leftTan = A*leftT + B;
  158. rightTan = A*rightT + B;
  159. }
  160. // Check if curvature is too strong for a triangle strip on the line segment that crosses max
  161. // curvature. If it is, we will chop and convert the segment to a "lineTo" with round joins.
  162. //
  163. // FIXME: This is quite costly and the vast majority of curves only have moderate curvature. We
  164. // would benefit significantly from a quick reject that detects curves that don't need special
  165. // treatment for strong curvature.
  166. bool isCurvatureTooStrong = calc_curvature_costheta(leftTan, rightTan) < fMaxCurvatureCosTheta;
  167. if (isCurvatureTooStrong) {
  168. SkPoint ptsBuffer[5];
  169. const SkPoint* currQuadratic = P;
  170. if (leftT > 0) {
  171. SkChopQuadAt(currQuadratic, ptsBuffer, leftT);
  172. this->quadraticTo(leftJoinVerb, ptsBuffer, /*maxCurvatureT=*/1);
  173. if (rightT < 1) {
  174. rightT = (rightT - leftT) / (1 - leftT);
  175. }
  176. currQuadratic = ptsBuffer + 2;
  177. } else {
  178. this->rotateTo(leftJoinVerb, normals[0]);
  179. }
  180. if (rightT < 1) {
  181. SkChopQuadAt(currQuadratic, ptsBuffer, rightT);
  182. this->lineTo(Verb::kInternalRoundJoin, ptsBuffer[2]);
  183. this->quadraticTo(Verb::kInternalRoundJoin, ptsBuffer + 2, /*maxCurvatureT=*/0);
  184. } else {
  185. this->lineTo(Verb::kInternalRoundJoin, currQuadratic[2]);
  186. this->rotateTo(Verb::kInternalRoundJoin, normals[1]);
  187. }
  188. return;
  189. }
  190. this->recordLeftJoinIfNotEmpty(leftJoinVerb, normals[0]);
  191. fNormals.push_back_n(2, normals);
  192. this->recordStroke(Verb::kQuadraticStroke, SkNextLog2(numSegments));
  193. p1.store(&fPoints.push_back());
  194. p2.store(&fPoints.push_back());
  195. }
  196. void GrCCStrokeGeometry::cubicTo(const SkPoint P[4]) {
  197. SkASSERT(fInsideContour);
  198. float roots[3];
  199. int numRoots = SkFindCubicMaxCurvature(P, roots);
  200. this->cubicTo(fCurrStrokeJoinVerb, P,
  201. numRoots > 0 ? roots[numRoots/2] : 0,
  202. numRoots > 1 ? roots[0] : kLeftMaxCurvatureNone,
  203. numRoots > 2 ? roots[2] : kRightMaxCurvatureNone);
  204. }
  205. // Wang's formula for cubics (1985) gives us the number of evenly spaced (in the parametric sense)
  206. // line segments that are guaranteed to be within a distance of "kMaxErrorFromLinearization"
  207. // from the actual curve.
  208. static inline float wangs_formula_cubic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
  209. const Sk2f& p3) {
  210. static constexpr float k = (3 * 2) / (8 * kMaxErrorFromLinearization);
  211. float f = SkScalarSqrt(k * length(Sk2f::Max((p2 - p1*2 + p0).abs(),
  212. (p3 - p2*2 + p1).abs())));
  213. return SkScalarCeilToInt(f);
  214. }
  215. void GrCCStrokeGeometry::cubicTo(Verb leftJoinVerb, const SkPoint P[4], float maxCurvatureT,
  216. float leftMaxCurvatureT, float rightMaxCurvatureT) {
  217. Sk2f p0 = Sk2f::Load(P);
  218. Sk2f p1 = Sk2f::Load(P+1);
  219. Sk2f p2 = Sk2f::Load(P+2);
  220. Sk2f p3 = Sk2f::Load(P+3);
  221. Sk2f tan0 = p1 - p0;
  222. Sk2f tan1 = p3 - p2;
  223. // Snap control points to endpoints if they are so close that FP error will become an issue.
  224. if ((tan0.abs() < SK_ScalarNearlyZero).allTrue()) { // p0 ~= p1
  225. p1 = p0;
  226. tan0 = p2 - p0;
  227. if ((tan0.abs() < SK_ScalarNearlyZero).allTrue()) { // p0 ~= p1 ~= p2
  228. this->lineTo(leftJoinVerb, P[3]);
  229. return;
  230. }
  231. }
  232. if ((tan1.abs() < SK_ScalarNearlyZero).allTrue()) { // p2 ~= p3
  233. p2 = p3;
  234. tan1 = p3 - p1;
  235. if ((tan1.abs() < SK_ScalarNearlyZero).allTrue() || // p1 ~= p2 ~= p3
  236. (p0 == p1).allTrue()) { // p0 ~= p1 AND p2 ~= p3
  237. this->lineTo(leftJoinVerb, P[3]);
  238. return;
  239. }
  240. }
  241. SkPoint normals[2];
  242. normalize2(tan0, tan1, normals);
  243. // Decide how many flat line segments to chop the curve into.
  244. int numSegments = wangs_formula_cubic(p0, p1, p2, p3);
  245. numSegments = SkTMin(numSegments, 1 << kMaxNumLinearSegmentsLog2);
  246. if (numSegments <= 1) {
  247. this->rotateTo(leftJoinVerb, normals[0]);
  248. this->lineTo(leftJoinVerb, P[3]);
  249. this->rotateTo(Verb::kInternalRoundJoin, normals[1]);
  250. return;
  251. }
  252. // At^2 + Bt + C gives a vector tangent to the cubic. (More specifically, it's the derivative
  253. // minus an irrelevant scale by 3, since all we care about is the direction.)
  254. Sk2f A = p3 + (p1 - p2)*3 - p0;
  255. Sk2f B = (p0 - p1*2 + p2)*2;
  256. Sk2f C = p1 - p0;
  257. // Find a line segment that crosses max curvature.
  258. float segmentLength = SkScalarInvert(numSegments);
  259. float leftT = maxCurvatureT - segmentLength/2;
  260. float rightT = maxCurvatureT + segmentLength/2;
  261. Sk2f leftTan, rightTan;
  262. if (leftT <= 0) {
  263. leftT = 0;
  264. leftTan = tan0;
  265. rightT = segmentLength;
  266. rightTan = A*rightT*rightT + B*rightT + C;
  267. } else if (rightT >= 1) {
  268. leftT = 1 - segmentLength;
  269. leftTan = A*leftT*leftT + B*leftT + C;
  270. rightT = 1;
  271. rightTan = tan1;
  272. } else {
  273. leftTan = A*leftT*leftT + B*leftT + C;
  274. rightTan = A*rightT*rightT + B*rightT + C;
  275. }
  276. // Check if curvature is too strong for a triangle strip on the line segment that crosses max
  277. // curvature. If it is, we will chop and convert the segment to a "lineTo" with round joins.
  278. //
  279. // FIXME: This is quite costly and the vast majority of curves only have moderate curvature. We
  280. // would benefit significantly from a quick reject that detects curves that don't need special
  281. // treatment for strong curvature.
  282. bool isCurvatureTooStrong = calc_curvature_costheta(leftTan, rightTan) < fMaxCurvatureCosTheta;
  283. if (isCurvatureTooStrong) {
  284. SkPoint ptsBuffer[7];
  285. p0.store(ptsBuffer);
  286. p1.store(ptsBuffer + 1);
  287. p2.store(ptsBuffer + 2);
  288. p3.store(ptsBuffer + 3);
  289. const SkPoint* currCubic = ptsBuffer;
  290. if (leftT > 0) {
  291. SkChopCubicAt(currCubic, ptsBuffer, leftT);
  292. this->cubicTo(leftJoinVerb, ptsBuffer, /*maxCurvatureT=*/1,
  293. (kLeftMaxCurvatureNone != leftMaxCurvatureT)
  294. ? leftMaxCurvatureT/leftT : kLeftMaxCurvatureNone,
  295. kRightMaxCurvatureNone);
  296. if (rightT < 1) {
  297. rightT = (rightT - leftT) / (1 - leftT);
  298. }
  299. if (rightMaxCurvatureT < 1 && kRightMaxCurvatureNone != rightMaxCurvatureT) {
  300. rightMaxCurvatureT = (rightMaxCurvatureT - leftT) / (1 - leftT);
  301. }
  302. currCubic = ptsBuffer + 3;
  303. } else {
  304. this->rotateTo(leftJoinVerb, normals[0]);
  305. }
  306. if (rightT < 1) {
  307. SkChopCubicAt(currCubic, ptsBuffer, rightT);
  308. this->lineTo(Verb::kInternalRoundJoin, ptsBuffer[3]);
  309. currCubic = ptsBuffer + 3;
  310. this->cubicTo(Verb::kInternalRoundJoin, currCubic, /*maxCurvatureT=*/0,
  311. kLeftMaxCurvatureNone, kRightMaxCurvatureNone);
  312. } else {
  313. this->lineTo(Verb::kInternalRoundJoin, currCubic[3]);
  314. this->rotateTo(Verb::kInternalRoundJoin, normals[1]);
  315. }
  316. return;
  317. }
  318. // Recurse and check the other two points of max curvature, if any.
  319. if (kRightMaxCurvatureNone != rightMaxCurvatureT) {
  320. this->cubicTo(leftJoinVerb, P, rightMaxCurvatureT, leftMaxCurvatureT,
  321. kRightMaxCurvatureNone);
  322. return;
  323. }
  324. if (kLeftMaxCurvatureNone != leftMaxCurvatureT) {
  325. SkASSERT(kRightMaxCurvatureNone == rightMaxCurvatureT);
  326. this->cubicTo(leftJoinVerb, P, leftMaxCurvatureT, kLeftMaxCurvatureNone,
  327. kRightMaxCurvatureNone);
  328. return;
  329. }
  330. this->recordLeftJoinIfNotEmpty(leftJoinVerb, normals[0]);
  331. fNormals.push_back_n(2, normals);
  332. this->recordStroke(Verb::kCubicStroke, SkNextLog2(numSegments));
  333. p1.store(&fPoints.push_back());
  334. p2.store(&fPoints.push_back());
  335. p3.store(&fPoints.push_back());
  336. }
  337. void GrCCStrokeGeometry::recordStroke(Verb verb, int numSegmentsLog2) {
  338. SkASSERT(Verb::kLinearStroke != verb || 0 == numSegmentsLog2);
  339. SkASSERT(numSegmentsLog2 <= kMaxNumLinearSegmentsLog2);
  340. fVerbs.push_back(verb);
  341. if (Verb::kLinearStroke != verb) {
  342. SkASSERT(numSegmentsLog2 > 0);
  343. fParams.push_back().fNumLinearSegmentsLog2 = numSegmentsLog2;
  344. }
  345. ++fCurrStrokeTallies->fStrokes[numSegmentsLog2];
  346. }
  347. void GrCCStrokeGeometry::rotateTo(Verb leftJoinVerb, SkVector normal) {
  348. this->recordLeftJoinIfNotEmpty(leftJoinVerb, normal);
  349. fNormals.push_back(normal);
  350. }
  351. void GrCCStrokeGeometry::recordLeftJoinIfNotEmpty(Verb joinVerb, SkVector nextNormal) {
  352. if (fNormals.count() <= fCurrContourFirstNormalIdx) {
  353. // The contour is empty. Nothing to join with.
  354. SkASSERT(fNormals.count() == fCurrContourFirstNormalIdx);
  355. return;
  356. }
  357. if (Verb::kBevelJoin == joinVerb) {
  358. this->recordBevelJoin(Verb::kBevelJoin);
  359. return;
  360. }
  361. Sk2f n0 = Sk2f::Load(&fNormals.back());
  362. Sk2f n1 = Sk2f::Load(&nextNormal);
  363. Sk2f base = n1 - n0;
  364. if ((base.abs() * fCurrStrokeRadius < kMaxErrorFromLinearization).allTrue()) {
  365. // Treat any join as a bevel when the outside corners of the two adjoining strokes are
  366. // close enough to each other. This is important because "miterCapHeightOverWidth" becomes
  367. // unstable when n0 and n1 are nearly equal.
  368. this->recordBevelJoin(joinVerb);
  369. return;
  370. }
  371. // We implement miters and round joins by placing a triangle-shaped cap on top of a bevel join.
  372. // (For round joins this triangle cap comprises the conic control points.) Find how tall to make
  373. // this triangle cap, relative to its width.
  374. //
  375. // NOTE: This value would be infinite at 180 degrees, but we clamp miterCapHeightOverWidth at
  376. // near-infinity. 180-degree round joins still look perfectly acceptable like this (though
  377. // technically not pure arcs).
  378. Sk2f cross = base * SkNx_shuffle<1,0>(n0);
  379. Sk2f dot = base * n0;
  380. float miterCapHeight = SkScalarAbs(dot[0] + dot[1]);
  381. float miterCapWidth = SkScalarAbs(cross[0] - cross[1]) * 2;
  382. if (Verb::kMiterJoin == joinVerb) {
  383. if (miterCapHeight > fMiterMaxCapHeightOverWidth * miterCapWidth) {
  384. // This join is tighter than the miter limit. Treat it as a bevel.
  385. this->recordBevelJoin(Verb::kMiterJoin);
  386. return;
  387. }
  388. this->recordMiterJoin(miterCapHeight / miterCapWidth);
  389. return;
  390. }
  391. SkASSERT(Verb::kRoundJoin == joinVerb || Verb::kInternalRoundJoin == joinVerb);
  392. // Conic arcs become unstable when they approach 180 degrees. When the conic control point
  393. // begins shooting off to infinity (i.e., height/width > 32), split the conic into two.
  394. static constexpr float kAlmost180Degrees = 32;
  395. if (miterCapHeight > kAlmost180Degrees * miterCapWidth) {
  396. Sk2f bisect = normalize(n0 - n1);
  397. this->rotateTo(joinVerb, SkVector::Make(-bisect[1], bisect[0]));
  398. this->recordLeftJoinIfNotEmpty(joinVerb, nextNormal);
  399. return;
  400. }
  401. float miterCapHeightOverWidth = miterCapHeight / miterCapWidth;
  402. // Find the heights of this round join's conic control point as well as the arc itself.
  403. Sk2f X, Y;
  404. transpose(base * base, n0 * n1, &X, &Y);
  405. Sk2f r = Sk2f::Max(X + Y + Sk2f(0, 1), 0.f).sqrt();
  406. Sk2f heights = SkNx_fma(r, Sk2f(miterCapHeightOverWidth, -SK_ScalarRoot2Over2), Sk2f(0, 1));
  407. float controlPointHeight = SkScalarAbs(heights[0]);
  408. float curveHeight = heights[1];
  409. if (curveHeight * fCurrStrokeRadius < kMaxErrorFromLinearization) {
  410. // Treat round joins as bevels when their curvature is nearly flat.
  411. this->recordBevelJoin(joinVerb);
  412. return;
  413. }
  414. float w = curveHeight / (controlPointHeight - curveHeight);
  415. this->recordRoundJoin(joinVerb, miterCapHeightOverWidth, w);
  416. }
  417. void GrCCStrokeGeometry::recordBevelJoin(Verb originalJoinVerb) {
  418. if (!IsInternalJoinVerb(originalJoinVerb)) {
  419. fVerbs.push_back(Verb::kBevelJoin);
  420. ++fCurrStrokeTallies->fTriangles;
  421. } else {
  422. fVerbs.push_back(Verb::kInternalBevelJoin);
  423. fCurrStrokeTallies->fTriangles += 2;
  424. }
  425. }
  426. void GrCCStrokeGeometry::recordMiterJoin(float miterCapHeightOverWidth) {
  427. fVerbs.push_back(Verb::kMiterJoin);
  428. fParams.push_back().fMiterCapHeightOverWidth = miterCapHeightOverWidth;
  429. fCurrStrokeTallies->fTriangles += 2;
  430. }
  431. void GrCCStrokeGeometry::recordRoundJoin(Verb joinVerb, float miterCapHeightOverWidth,
  432. float conicWeight) {
  433. fVerbs.push_back(joinVerb);
  434. fParams.push_back().fConicWeight = conicWeight;
  435. fParams.push_back().fMiterCapHeightOverWidth = miterCapHeightOverWidth;
  436. if (Verb::kRoundJoin == joinVerb) {
  437. ++fCurrStrokeTallies->fTriangles;
  438. ++fCurrStrokeTallies->fConics;
  439. } else {
  440. SkASSERT(Verb::kInternalRoundJoin == joinVerb);
  441. fCurrStrokeTallies->fTriangles += 2;
  442. fCurrStrokeTallies->fConics += 2;
  443. }
  444. }
  445. void GrCCStrokeGeometry::closeContour() {
  446. SkASSERT(fInsideContour);
  447. SkASSERT(fPoints.count() > fCurrContourFirstPtIdx);
  448. if (fPoints.back() != fPoints[fCurrContourFirstPtIdx]) {
  449. // Draw a line back to the beginning.
  450. this->lineTo(fCurrStrokeJoinVerb, fPoints[fCurrContourFirstPtIdx]);
  451. }
  452. if (fNormals.count() > fCurrContourFirstNormalIdx) {
  453. // Join the first and last lines.
  454. this->rotateTo(fCurrStrokeJoinVerb,fNormals[fCurrContourFirstNormalIdx]);
  455. } else {
  456. // This contour is empty. Add a bogus normal since the iterator always expects one.
  457. SkASSERT(fNormals.count() == fCurrContourFirstNormalIdx);
  458. fNormals.push_back({0, 0});
  459. }
  460. fVerbs.push_back(Verb::kEndContour);
  461. SkDEBUGCODE(fInsideContour = false);
  462. }
  463. void GrCCStrokeGeometry::capContourAndExit() {
  464. SkASSERT(fInsideContour);
  465. if (fCurrContourFirstNormalIdx >= fNormals.count()) {
  466. // This contour is empty. Add a normal in the direction that caps orient on empty geometry.
  467. SkASSERT(fNormals.count() == fCurrContourFirstNormalIdx);
  468. fNormals.push_back({1, 0});
  469. }
  470. this->recordCapsIfAny();
  471. fVerbs.push_back(Verb::kEndContour);
  472. SkDEBUGCODE(fInsideContour = false);
  473. }
  474. void GrCCStrokeGeometry::recordCapsIfAny() {
  475. SkASSERT(fInsideContour);
  476. SkASSERT(fCurrContourFirstNormalIdx < fNormals.count());
  477. if (SkPaint::kButt_Cap == fCurrStrokeCapType) {
  478. return;
  479. }
  480. Verb capVerb;
  481. if (SkPaint::kSquare_Cap == fCurrStrokeCapType) {
  482. if (fCurrStrokeRadius * SK_ScalarRoot2Over2 < kMaxErrorFromLinearization) {
  483. return;
  484. }
  485. capVerb = Verb::kSquareCap;
  486. fCurrStrokeTallies->fStrokes[0] += 2;
  487. } else {
  488. SkASSERT(SkPaint::kRound_Cap == fCurrStrokeCapType);
  489. if (fCurrStrokeRadius < kMaxErrorFromLinearization) {
  490. return;
  491. }
  492. capVerb = Verb::kRoundCap;
  493. fCurrStrokeTallies->fTriangles += 2;
  494. fCurrStrokeTallies->fConics += 4;
  495. }
  496. fVerbs.push_back(capVerb);
  497. fVerbs.push_back(Verb::kEndContour);
  498. fVerbs.push_back(capVerb);
  499. // Reserve the space first, since push_back() takes the point by reference and might
  500. // invalidate the reference if the array grows.
  501. fPoints.reserve(fPoints.count() + 1);
  502. fPoints.push_back(fPoints[fCurrContourFirstPtIdx]);
  503. // Reserve the space first, since push_back() takes the normal by reference and might
  504. // invalidate the reference if the array grows. (Although in this case we should be fine
  505. // since there is a negate operator.)
  506. fNormals.reserve(fNormals.count() + 1);
  507. fNormals.push_back(-fNormals[fCurrContourFirstNormalIdx]);
  508. }