GrCCStrokeGeometry.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. #ifndef GrGrCCStrokeGeometry_DEFINED
  8. #define GrGrCCStrokeGeometry_DEFINED
  9. #include "include/core/SkPaint.h"
  10. #include "include/core/SkPoint.h"
  11. #include "include/private/SkTArray.h"
  12. class SkStrokeRec;
  13. /**
  14. * This class converts device-space stroked paths into a set of independent strokes, joins, and caps
  15. * that map directly to coverage-counted GPU instances. Non-hairline strokes can only be drawn with
  16. * rigid body transforms; we don't yet support skewing the stroke lines themselves.
  17. */
  18. class GrCCStrokeGeometry {
  19. public:
  20. static constexpr int kMaxNumLinearSegmentsLog2 = 15;
  21. GrCCStrokeGeometry(int numSkPoints = 0, int numSkVerbs = 0)
  22. : fVerbs(numSkVerbs * 5/2) // Reserve for a 2.5x expansion in verbs. (Joins get their
  23. // own separate verb in our representation.)
  24. , fParams(numSkVerbs * 3) // Somewhere around 1-2 params per verb.
  25. , fPoints(numSkPoints * 5/4) // Reserve for a 1.25x expansion in points and normals.
  26. , fNormals(numSkPoints * 5/4) {}
  27. // A string of verbs and their corresponding, params, points, and normals are a compact
  28. // representation of what will eventually be independent instances in GPU buffers. When added
  29. // up, the combined coverage of all these instances will make complete stroked paths.
  30. enum class Verb : uint8_t {
  31. kBeginPath, // Instructs the iterator to advance its stroke width, atlas offset, etc.
  32. // Independent strokes of a single line or curve, with (antialiased) butt caps on the ends.
  33. kLinearStroke,
  34. kQuadraticStroke,
  35. kCubicStroke,
  36. // Joins are a triangles that connect the outer corners of two adjoining strokes. Miters
  37. // have an additional triangle cap on top of the bevel, and round joins have an arc on top.
  38. kBevelJoin,
  39. kMiterJoin,
  40. kRoundJoin,
  41. // We use internal joins when we have to internally break up a stroke because its curvature
  42. // is too strong for a triangle strip. They are coverage-counted, self-intersecting
  43. // quadrilaterals that tie the four corners of two adjoining strokes together a like a
  44. // shoelace. (Coverage is negative on the inside half.) We place an arc on both ends of an
  45. // internal round join.
  46. kInternalBevelJoin,
  47. kInternalRoundJoin,
  48. kSquareCap,
  49. kRoundCap,
  50. kEndContour // Instructs the iterator to advance its internal point and normal ptrs.
  51. };
  52. static bool IsInternalJoinVerb(Verb verb);
  53. // Some verbs require additional parameters(s).
  54. union Parameter {
  55. // For cubic and quadratic strokes: How many flat line segments to chop the curve into?
  56. int fNumLinearSegmentsLog2;
  57. // For miter and round joins: How tall should the triangle cap be on top of the join?
  58. // (This triangle is the conic control points for a round join.)
  59. float fMiterCapHeightOverWidth;
  60. float fConicWeight; // Round joins only.
  61. };
  62. const SkTArray<Verb, true>& verbs() const { SkASSERT(!fInsideContour); return fVerbs; }
  63. const SkTArray<Parameter, true>& params() const { SkASSERT(!fInsideContour); return fParams; }
  64. const SkTArray<SkPoint, true>& points() const { SkASSERT(!fInsideContour); return fPoints; }
  65. const SkTArray<SkVector, true>& normals() const { SkASSERT(!fInsideContour); return fNormals; }
  66. // These track the numbers of instances required to draw all the recorded strokes.
  67. struct InstanceTallies {
  68. int fStrokes[kMaxNumLinearSegmentsLog2 + 1];
  69. int fTriangles;
  70. int fConics;
  71. InstanceTallies operator+(const InstanceTallies&) const;
  72. };
  73. void beginPath(const SkStrokeRec&, float strokeDevWidth, InstanceTallies*);
  74. void moveTo(SkPoint);
  75. void lineTo(SkPoint);
  76. void quadraticTo(const SkPoint[3]);
  77. void cubicTo(const SkPoint[4]);
  78. void closeContour(); // Connect back to the first point in the contour and exit.
  79. void capContourAndExit(); // Add endcaps (if any) and exit the contour.
  80. private:
  81. void lineTo(Verb leftJoinVerb, SkPoint);
  82. void quadraticTo(Verb leftJoinVerb, const SkPoint[3], float maxCurvatureT);
  83. static constexpr float kLeftMaxCurvatureNone = 1;
  84. static constexpr float kRightMaxCurvatureNone = 0;
  85. void cubicTo(Verb leftJoinVerb, const SkPoint[4], float maxCurvatureT, float leftMaxCurvatureT,
  86. float rightMaxCurvatureT);
  87. // Pushes a new normal to fNormals and records a join, without changing the current position.
  88. void rotateTo(Verb leftJoinVerb, SkVector normal);
  89. // Records a stroke in fElememts.
  90. void recordStroke(Verb, int numSegmentsLog2);
  91. // Records a join in fElememts with the previous stroke, if the cuurent contour is not empty.
  92. void recordLeftJoinIfNotEmpty(Verb joinType, SkVector nextNormal);
  93. void recordBevelJoin(Verb originalJoinVerb);
  94. void recordMiterJoin(float miterCapHeightOverWidth);
  95. void recordRoundJoin(Verb roundJoinVerb, float miterCapHeightOverWidth, float conicWeight);
  96. void recordCapsIfAny();
  97. float fCurrStrokeRadius;
  98. Verb fCurrStrokeJoinVerb;
  99. SkPaint::Cap fCurrStrokeCapType;
  100. InstanceTallies* fCurrStrokeTallies = nullptr;
  101. // We implement miters by placing a triangle-shaped cap on top of a bevel join. This field tells
  102. // us what the miter limit is, restated in terms of how tall that triangle cap can be.
  103. float fMiterMaxCapHeightOverWidth;
  104. // Any curvature on the original curve gets magnified on the outer edge of the stroke,
  105. // proportional to how thick the stroke radius is. This field tells us the maximum curvature we
  106. // can tolerate using the current stroke radius, before linearization artifacts begin to appear
  107. // on the outer edge.
  108. //
  109. // (Curvature this strong is quite rare in practice, but when it does happen, we decompose the
  110. // section with strong curvature into lineTo's with round joins in between.)
  111. float fMaxCurvatureCosTheta;
  112. int fCurrContourFirstPtIdx;
  113. int fCurrContourFirstNormalIdx;
  114. SkDEBUGCODE(bool fInsideContour = false);
  115. SkSTArray<128, Verb, true> fVerbs;
  116. SkSTArray<128, Parameter, true> fParams;
  117. SkSTArray<128, SkPoint, true> fPoints;
  118. SkSTArray<128, SkVector, true> fNormals;
  119. };
  120. inline GrCCStrokeGeometry::InstanceTallies GrCCStrokeGeometry::InstanceTallies::operator+(
  121. const InstanceTallies& t) const {
  122. InstanceTallies ret;
  123. for (int i = 0; i <= kMaxNumLinearSegmentsLog2; ++i) {
  124. ret.fStrokes[i] = fStrokes[i] + t.fStrokes[i];
  125. }
  126. ret.fTriangles = fTriangles + t.fTriangles;
  127. ret.fConics = fConics + t.fConics;
  128. return ret;
  129. }
  130. inline bool GrCCStrokeGeometry::IsInternalJoinVerb(Verb verb) {
  131. switch (verb) {
  132. case Verb::kInternalBevelJoin:
  133. case Verb::kInternalRoundJoin:
  134. return true;
  135. case Verb::kBeginPath:
  136. case Verb::kLinearStroke:
  137. case Verb::kQuadraticStroke:
  138. case Verb::kCubicStroke:
  139. case Verb::kBevelJoin:
  140. case Verb::kMiterJoin:
  141. case Verb::kRoundJoin:
  142. case Verb::kSquareCap:
  143. case Verb::kRoundCap:
  144. case Verb::kEndContour:
  145. return false;
  146. }
  147. SK_ABORT("Invalid GrCCStrokeGeometry::Verb.");
  148. return false;
  149. }
  150. #endif