1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrAAConvexTessellator_DEFINED
#define GrAAConvexTessellator_DEFINED
#include "SkColor.h"
#include "SkPaint.h"
#include "SkPointPriv.h"
#include "SkScalar.h"
#include "SkStrokeRec.h"
#include "SkTDArray.h"
class SkCanvas;
class SkMatrix;
class SkPath;
//#define GR_AA_CONVEX_TESSELLATOR_VIZ 1
// device space distance which we inset / outset points in order to create the soft antialiased edge
static const SkScalar kAntialiasingRadius = 0.5f;
class GrAAConvexTessellator;
// The AAConvexTessellator holds the global pool of points and the triangulation
// that connects them. It also drives the tessellation process.
// The outward facing normals of the original polygon are stored (in 'fNorms') to service
// computeDepthFromEdge requests.
class GrAAConvexTessellator {
public:
GrAAConvexTessellator(SkStrokeRec::Style style = SkStrokeRec::kFill_Style,
SkScalar strokeWidth = -1.0f,
SkPaint::Join join = SkPaint::Join::kBevel_Join,
SkScalar miterLimit = 0.0f)
: fSide(SkPointPriv::kOn_Side)
, fStrokeWidth(strokeWidth)
, fStyle(style)
, fJoin(join)
, fMiterLimit(miterLimit) {
}
SkPointPriv::Side side() const { return fSide; }
bool tessellate(const SkMatrix& m, const SkPath& path);
// The next five should only be called after tessellate to extract the result
int numPts() const { return fPts.count(); }
int numIndices() const { return fIndices.count(); }
const SkPoint& lastPoint() const { return fPts.top(); }
const SkPoint& point(int index) const { return fPts[index]; }
int index(int index) const { return fIndices[index]; }
SkScalar coverage(int index) const { return fCoverages[index]; }
#if GR_AA_CONVEX_TESSELLATOR_VIZ
void draw(SkCanvas* canvas) const;
#endif
// The tessellator can be reused for multiple paths by rewinding in between
void rewind();
private:
// CandidateVerts holds the vertices for the next ring while they are
// being generated. Its main function is to de-dup the points.
class CandidateVerts {
public:
void setReserve(int numPts) { fPts.setReserve(numPts); }
void rewind() { fPts.rewind(); }
int numPts() const { return fPts.count(); }
const SkPoint& lastPoint() const { return fPts.top().fPt; }
const SkPoint& firstPoint() const { return fPts[0].fPt; }
const SkPoint& point(int index) const { return fPts[index].fPt; }
int originatingIdx(int index) const { return fPts[index].fOriginatingIdx; }
int origEdge(int index) const { return fPts[index].fOrigEdgeId; }
bool needsToBeNew(int index) const { return fPts[index].fNeedsToBeNew; }
int addNewPt(const SkPoint& newPt, int originatingIdx, int origEdge, bool needsToBeNew) {
struct PointData* pt = fPts.push();
pt->fPt = newPt;
pt->fOrigEdgeId = origEdge;
pt->fOriginatingIdx = originatingIdx;
pt->fNeedsToBeNew = needsToBeNew;
return fPts.count() - 1;
}
int fuseWithPrior(int origEdgeId) {
fPts.top().fOrigEdgeId = origEdgeId;
fPts.top().fOriginatingIdx = -1;
fPts.top().fNeedsToBeNew = true;
return fPts.count() - 1;
}
int fuseWithNext() {
fPts[0].fOriginatingIdx = -1;
fPts[0].fNeedsToBeNew = true;
return 0;
}
int fuseWithBoth() {
if (fPts.count() > 1) {
fPts.pop();
}
fPts[0].fOriginatingIdx = -1;
fPts[0].fNeedsToBeNew = true;
return 0;
}
private:
struct PointData {
SkPoint fPt;
int fOriginatingIdx;
int fOrigEdgeId;
bool fNeedsToBeNew;
};
SkTDArray<struct PointData> fPts;
};
// The Ring holds a set of indices into the global pool that together define
// a single polygon inset.
class Ring {
public:
void setReserve(int numPts) { fPts.setReserve(numPts); }
void rewind() { fPts.rewind(); }
int numPts() const { return fPts.count(); }
void addIdx(int index, int origEdgeId) {
struct PointData* pt = fPts.push();
pt->fIndex = index;
pt->fOrigEdgeId = origEdgeId;
}
// Upgrade this ring so that it can behave like an originating ring
void makeOriginalRing() {
for (int i = 0; i < fPts.count(); ++i) {
fPts[i].fOrigEdgeId = fPts[i].fIndex;
}
}
// init should be called after all the indices have been added (via addIdx)
void init(const GrAAConvexTessellator& tess);
void init(const SkTDArray<SkVector>& norms, const SkTDArray<SkVector>& bisectors);
const SkPoint& norm(int index) const { return fPts[index].fNorm; }
const SkPoint& bisector(int index) const { return fPts[index].fBisector; }
int index(int index) const { return fPts[index].fIndex; }
int origEdgeID(int index) const { return fPts[index].fOrigEdgeId; }
void setOrigEdgeId(int index, int id) { fPts[index].fOrigEdgeId = id; }
#if GR_AA_CONVEX_TESSELLATOR_VIZ
void draw(SkCanvas* canvas, const GrAAConvexTessellator& tess) const;
#endif
private:
void computeNormals(const GrAAConvexTessellator& result);
void computeBisectors(const GrAAConvexTessellator& tess);
SkDEBUGCODE(bool isConvex(const GrAAConvexTessellator& tess) const;)
struct PointData {
SkPoint fNorm;
SkPoint fBisector;
int fIndex;
int fOrigEdgeId;
};
SkTDArray<PointData> fPts;
};
// Represents whether a given point is within a curve. A point is inside a curve only if it is
// an interior point within a quad, cubic, or conic, or if it is the endpoint of a quad, cubic,
// or conic with another curve meeting it at (more or less) the same angle.
enum CurveState {
// point is a sharp vertex
kSharp_CurveState,
// endpoint of a curve with the other side's curvature not yet determined
kIndeterminate_CurveState,
// point is in the interior of a curve
kCurve_CurveState
};
bool movable(int index) const { return fMovable[index]; }
// Movable points are those that can be slid along their bisector.
// Basically, a point is immovable if it is part of the original
// polygon or it results from the fusing of two bisectors.
int addPt(const SkPoint& pt, SkScalar depth, SkScalar coverage, bool movable, CurveState curve);
void popLastPt();
void popFirstPtShuffle();
void updatePt(int index, const SkPoint& pt, SkScalar depth, SkScalar coverage);
void addTri(int i0, int i1, int i2);
void reservePts(int count) {
fPts.setReserve(count);
fCoverages.setReserve(count);
fMovable.setReserve(count);
}
SkScalar computeDepthFromEdge(int edgeIdx, const SkPoint& p) const;
bool computePtAlongBisector(int startIdx, const SkPoint& bisector,
int edgeIdx, SkScalar desiredDepth,
SkPoint* result) const;
void lineTo(const SkPoint& p, CurveState curve);
void lineTo(const SkMatrix& m, SkPoint p, CurveState curve);
void quadTo(const SkPoint pts[3]);
void quadTo(const SkMatrix& m, SkPoint pts[3]);
void cubicTo(const SkMatrix& m, SkPoint pts[4]);
void conicTo(const SkMatrix& m, SkPoint pts[3], SkScalar w);
void terminate(const Ring& lastRing);
// return false on failure/degenerate path
bool extractFromPath(const SkMatrix& m, const SkPath& path);
void computeBisectors();
void fanRing(const Ring& ring);
Ring* getNextRing(Ring* lastRing);
void createOuterRing(const Ring& previousRing, SkScalar outset, SkScalar coverage,
Ring* nextRing);
bool createInsetRings(Ring& previousRing, SkScalar initialDepth, SkScalar initialCoverage,
SkScalar targetDepth, SkScalar targetCoverage, Ring** finalRing);
bool createInsetRing(const Ring& lastRing, Ring* nextRing,
SkScalar initialDepth, SkScalar initialCoverage, SkScalar targetDepth,
SkScalar targetCoverage, bool forceNew);
void validate() const;
// fPts, fCoverages, fMovable & fCurveState should always have the same # of elements
SkTDArray<SkPoint> fPts;
SkTDArray<SkScalar> fCoverages;
// movable points are those that can be slid further along their bisector
SkTDArray<bool> fMovable;
// Tracks whether a given point is interior to a curve. Such points are
// assumed to have shallow curvature.
SkTDArray<CurveState> fCurveState;
// The outward facing normals for the original polygon
SkTDArray<SkVector> fNorms;
// The inward facing bisector at each point in the original polygon. Only
// needed for exterior ring creation and then handed off to the initial ring.
SkTDArray<SkVector> fBisectors;
SkPointPriv::Side fSide; // winding of the original polygon
// The triangulation of the points
SkTDArray<int> fIndices;
Ring fInitialRing;
#if GR_AA_CONVEX_TESSELLATOR_VIZ
// When visualizing save all the rings
SkTDArray<Ring*> fRings;
#else
Ring fRings[2];
#endif
CandidateVerts fCandidateVerts;
// the stroke width is only used for stroke or stroke-and-fill styles
SkScalar fStrokeWidth;
SkStrokeRec::Style fStyle;
SkPaint::Join fJoin;
SkScalar fMiterLimit;
SkTDArray<SkPoint> fPointBuffer;
};
#endif
|