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