Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrShape.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google LLC
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
9
11#include "src/core/SkPathPriv.h"
13
14#include <algorithm>
15
17 switch (shape.type()) {
18 case Type::kEmpty:
19 this->reset();
20 break;
21 case Type::kPoint:
22 this->setPoint(shape.fPoint);
23 break;
24 case Type::kRect:
25 this->setRect(shape.fRect);
26 break;
27 case Type::kRRect:
28 this->setRRect(shape.fRRect);
29 break;
30 case Type::kPath:
31 this->setPath(shape.fPath);
32 break;
33 case Type::kArc:
34 this->setArc(shape.fArc);
35 break;
36 case Type::kLine:
37 this->setLine(shape.fLine);
38 break;
39 }
40
41 fStart = shape.fStart;
42 fCW = shape.fCW;
43 fInverted = shape.fInverted;
44
45 return *this;
46}
47
48uint32_t GrShape::stateKey() const {
49 // Use the path's full fill type instead of just whether or not it's inverted.
50 uint32_t key = this->isPath() ? static_cast<uint32_t>(fPath.getFillType())
51 : (fInverted ? 1 : 0);
52 key |= ((uint32_t) fType) << 2; // fill type was 2 bits
53 key |= fStart << 5; // type was 3 bits, total 5 bits so far
54 key |= (fCW ? 1 : 0) << 8; // start was 3 bits, total 8 bits so far
55 return key;
56}
57
58bool GrShape::simplifyPath(unsigned flags) {
59 SkASSERT(this->isPath());
60
63 SkPoint pts[2];
64
66 unsigned start;
67
68 if (fPath.isEmpty()) {
69 this->setType(Type::kEmpty);
70 return false;
71 } else if (fPath.isLine(pts)) {
72 this->simplifyLine(pts[0], pts[1], flags);
73 return false;
74 } else if (SkPathPriv::IsRRect(fPath, &rrect, &dir, &start)) {
75 this->simplifyRRect(rrect, dir, start, flags);
76 return true;
77 } else if (SkPathPriv::IsOval(fPath, &rect, &dir, &start)) {
78 // Convert to rrect indexing since oval is not represented explicitly
79 this->simplifyRRect(SkRRect::MakeOval(rect), dir, start * 2, flags);
80 return true;
82 // When there is a path effect we restrict rect detection to the narrower API that
83 // gives us the starting position. Otherwise, we will retry with the more aggressive
84 // isRect().
85 this->simplifyRect(rect, dir, start, flags);
86 return true;
87 } else if (flags & kIgnoreWinding_Flag) {
88 // Attempt isRect() since we don't have to preserve any winding info
89 bool closed;
90 if (fPath.isRect(&rect, &closed) && (closed || (flags & kSimpleFill_Flag))) {
91 this->simplifyRect(rect, kDefaultDir, kDefaultStart, flags);
92 return true;
93 }
94 }
95 // No further simplification for a path. For performance reasons, we don't query the path to
96 // determine it was closed, as whether or not it was closed when it remains a path type is not
97 // important for styling.
98 return false;
99}
100
101bool GrShape::simplifyArc(unsigned flags) {
102 SkASSERT(this->isArc());
103
104 // Arcs can simplify to rrects, lines, points, or empty; regardless of what it simplifies to
105 // it was closed if went through the center point.
106 bool wasClosed = fArc.fUseCenter;
107 if (fArc.fOval.isEmpty() || !fArc.fSweepAngle) {
108 if (flags & kSimpleFill_Flag) {
109 // Go straight to empty, since the other degenerate shapes all have 0 area anyway.
110 this->setType(Type::kEmpty);
111 } else if (!fArc.fSweepAngle) {
114 SkPoint start = {center.fX + 0.5f * fArc.fOval.width() * SkScalarCos(startRad),
115 center.fY + 0.5f * fArc.fOval.height() * SkScalarSin(startRad)};
116 // Either just the starting point, or a line from the center to the start
117 if (fArc.fUseCenter) {
118 this->simplifyLine(center, start, flags);
119 } else {
120 this->simplifyPoint(start, flags);
121 }
122 } else {
123 // TODO: Theoretically, we could analyze the arc projected into the empty bounds to
124 // determine a line, but that is somewhat complex for little value (since the arc
125 // can backtrack on itself if the sweep angle is large enough).
126 this->setType(Type::kEmpty);
127 }
128 } else {
130 // Eligible to turn into an oval if it sweeps a full circle
131 if (fArc.fSweepAngle <= -360.f || fArc.fSweepAngle >= 360.f) {
132 this->simplifyRRect(SkRRect::MakeOval(fArc.fOval),
134 return true;
135 }
136 }
137
139 // Map start to 0 to 360, sweep is always positive
140 if (fArc.fSweepAngle < 0) {
143 }
144
145 if (fArc.fStartAngle < 0 || fArc.fStartAngle >= 360.f) {
147 }
148 }
149 }
150
151 return wasClosed;
152}
153
154void GrShape::simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start,
155 unsigned flags) {
156 if (rrect.isEmpty() || rrect.isRect()) {
157 // Change index from rrect to rect
158 start = ((start + 1) / 2) % 4;
159 this->simplifyRect(rrect.rect(), dir, start, flags);
160 } else if (!this->isRRect()) {
161 this->setType(Type::kRRect);
162 fRRect = rrect;
163 this->setPathWindingParams(dir, start);
164 // A round rect is already canonical, so there's nothing more to do
165 } else {
166 // If starting as a round rect, the provided rrect/winding params should be already set
167 SkASSERT(fRRect == rrect && this->dir() == dir && this->startIndex() == start);
168 }
169}
170
171void GrShape::simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start,
172 unsigned flags) {
173 if (!rect.width() || !rect.height()) {
174 if (flags & kSimpleFill_Flag) {
175 // A zero area, filled shape so go straight to empty
176 this->setType(Type::kEmpty);
177 } else if (!rect.width() ^ !rect.height()) {
178 // A line, choose the first point that best matches the starting index
179 SkPoint p1 = {rect.fLeft, rect.fTop};
181 if (start >= 2 && !(flags & kIgnoreWinding_Flag)) {
182 using std::swap;
183 swap(p1, p2);
184 }
185 this->simplifyLine(p1, p2, flags);
186 } else {
187 // A point (all edges are equal, so start+dir doesn't affect choice)
188 this->simplifyPoint({rect.fLeft, rect.fTop}, flags);
189 }
190 } else {
191 if (!this->isRect()) {
192 this->setType(Type::kRect);
193 fRect = rect;
194 this->setPathWindingParams(dir, start);
195 } else {
196 // If starting as a rect, the provided rect/winding params should already be set
197 SkASSERT(fRect == rect && this->dir() == dir && this->startIndex() == start);
198 }
200 fRect.sort();
201 }
202 }
203}
204
205void GrShape::simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags) {
206 if (flags & kSimpleFill_Flag) {
207 this->setType(Type::kEmpty);
208 } else if (p1 == p2) {
209 this->simplifyPoint(p1, false);
210 } else {
211 if (!this->isLine()) {
212 this->setType(Type::kLine);
213 fLine.fP1 = p1;
214 fLine.fP2 = p2;
215 } else {
216 // If starting as a line, the provided points should already be set
217 SkASSERT(fLine.fP1 == p1 && fLine.fP2 == p2);
218 }
220 // Sort the end points
221 if (fLine.fP2.fY < fLine.fP1.fY ||
222 (fLine.fP2.fY == fLine.fP1.fY && fLine.fP2.fX < fLine.fP1.fX)) {
223 using std::swap;
225 }
226 }
227 }
228}
229
230void GrShape::simplifyPoint(const SkPoint& point, unsigned flags) {
231 if (flags & kSimpleFill_Flag) {
232 this->setType(Type::kEmpty);
233 } else if (!this->isPoint()) {
234 this->setType(Type::kPoint);
235 fPoint = point;
236 } else {
237 // If starting as a point, the provided position should already be set
239 }
240}
241
242bool GrShape::simplify(unsigned flags) {
243 // Verify that winding parameters are valid for the current type.
244 SkASSERT((fType == Type::kRect || fType == Type::kRRect) ||
245 (this->dir() == kDefaultDir && this->startIndex() == kDefaultStart));
246
247 // The type specific functions automatically fall through to the simpler shapes, so
248 // we only need to start in the right place.
249 bool wasClosed = false;
250 switch (fType) {
251 case Type::kEmpty:
252 // do nothing
253 break;
254 case Type::kPoint:
255 this->simplifyPoint(fPoint, flags);
256 break;
257 case Type::kLine:
258 this->simplifyLine(fLine.fP1, fLine.fP2, flags);
259 break;
260 case Type::kRect:
261 this->simplifyRect(fRect, this->dir(), this->startIndex(), flags);
262 wasClosed = true;
263 break;
264 case Type::kRRect:
265 this->simplifyRRect(fRRect, this->dir(), this->startIndex(), flags);
266 wasClosed = true;
267 break;
268 case Type::kPath:
269 wasClosed = this->simplifyPath(flags);
270 break;
271 case Type::kArc:
272 wasClosed = this->simplifyArc(flags);
273 break;
274
275 default:
277 }
278
279 if (((flags & kIgnoreWinding_Flag) || (fType != Type::kRect && fType != Type::kRRect))) {
280 // Reset winding parameters if we don't need them anymore
282 }
283
284 return wasClosed;
285}
286
287bool GrShape::conservativeContains(const SkRect& rect) const {
288 switch (this->type()) {
289 case Type::kEmpty:
290 case Type::kPoint: // fall through since a point has 0 area
291 case Type::kLine: // fall through, "" (currently choosing not to test if 'rect' == line)
292 return false;
293 case Type::kRect:
294 return fRect.contains(rect);
295 case Type::kRRect:
296 return fRRect.contains(rect);
297 case Type::kPath:
299 case Type::kArc:
300 if (fArc.fUseCenter) {
301 SkPath arc;
302 this->asPath(&arc);
303 return arc.conservativelyContainsRect(rect);
304 } else {
305 return false;
306 }
307 }
309}
310
311bool GrShape::conservativeContains(const SkPoint& point) const {
312 switch (this->type()) {
313 case Type::kEmpty:
314 case Type::kPoint: // fall through, currently choosing not to test if shape == point
315 case Type::kLine: // fall through, ""
316 case Type::kArc:
317 return false;
318 case Type::kRect:
319 return fRect.contains(point.fX, point.fY);
320 case Type::kRRect:
322 case Type::kPath:
323 return fPath.contains(point.fX, point.fY);
324 }
326}
327
328bool GrShape::closed() const {
329 switch (this->type()) {
330 case Type::kEmpty: // fall through
331 case Type::kRect: // fall through
332 case Type::kRRect:
333 return true;
334 case Type::kPath:
335 // SkPath doesn't keep track of the closed status of each contour.
337 case Type::kArc:
338 return fArc.fUseCenter;
339 case Type::kPoint: // fall through
340 case Type::kLine:
341 return false;
342 }
344}
345
346bool GrShape::convex(bool simpleFill) const {
347 switch (this->type()) {
348 case Type::kEmpty: // fall through
349 case Type::kRect: // fall through
350 case Type::kRRect:
351 return true;
352 case Type::kPath:
353 // SkPath.isConvex() really means "is this path convex were it to be closed".
354 // Convex paths may only have one contour hence isLastContourClosed() is sufficient.
355 return (simpleFill || fPath.isLastContourClosed()) && fPath.isConvex();
356 case Type::kArc:
358 case Type::kPoint: // fall through
359 case Type::kLine:
360 return false;
361 }
363}
364
366 // Bounds where left == bottom or top == right can indicate a line or point shape. We return
367 // inverted bounds for a truly empty shape.
368 static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
369 switch (this->type()) {
370 case Type::kEmpty:
371 return kInverted;
372 case Type::kPoint:
373 return {fPoint.fX, fPoint.fY, fPoint.fX, fPoint.fY};
374 case Type::kRect:
375 return fRect.makeSorted();
376 case Type::kRRect:
377 return fRRect.getBounds();
378 case Type::kPath:
379 return fPath.getBounds();
380 case Type::kArc:
381 return fArc.fOval;
382 case Type::kLine: {
385 b.sort();
386 return b; }
387 }
389}
390
391uint32_t GrShape::segmentMask() const {
392 // In order to match what a path would report, this has to inspect the shapes slightly
393 // to reflect what they might simplify to.
394 switch (this->type()) {
395 case Type::kEmpty:
396 return 0;
397 case Type::kRRect:
398 if (fRRect.isEmpty() || fRRect.isRect()) {
400 } else if (fRRect.isOval()) {
402 } else {
404 }
405 case Type::kPath:
406 return fPath.getSegmentMasks();
407 case Type::kArc:
408 if (fArc.fUseCenter) {
410 } else {
412 }
413 case Type::kPoint: // fall through
414 case Type::kLine: // ""
415 case Type::kRect:
417 }
419}
420
421void GrShape::asPath(SkPath* out, bool simpleFill) const {
422 if (!this->isPath() && !this->isArc()) {
423 // When not a path, we need to set fill type on the path to match invertedness.
424 // All the non-path geometries produce equivalent shapes with either even-odd or winding
425 // so we can use the default fill type.
426 out->reset();
427 out->setFillType(kDefaultFillType);
428 if (fInverted) {
429 out->toggleInverseFillType();
430 }
431 } // Else when we're already a path, that will assign the fill type directly to 'out'.
432
433 switch (this->type()) {
434 case Type::kEmpty:
435 return;
436 case Type::kPoint:
437 // A plain moveTo() or moveTo+close() does not match the expected path for a
438 // point that is being dashed (see SkDashPath's handling of zero-length segments).
439 out->moveTo(fPoint);
440 out->lineTo(fPoint);
441 return;
442 case Type::kRect:
443 out->addRect(fRect, this->dir(), this->startIndex());
444 return;
445 case Type::kRRect:
446 out->addRRect(fRRect, this->dir(), this->startIndex());
447 return;
448 case Type::kPath:
449 *out = fPath;
450 return;
451 case Type::kArc:
453 fArc.fUseCenter, simpleFill);
454 // CreateDrawArcPath resets the output path and configures its fill type, so we just
455 // have to ensure invertedness is correct.
456 if (fInverted) {
457 out->toggleInverseFillType();
458 }
459 return;
460 case Type::kLine:
461 out->moveTo(fLine.fP1);
462 out->lineTo(fLine.fP2);
463 return;
464 }
466}
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT(cond)
Definition SkAssert.h:116
SkPathDirection
Definition SkPathTypes.h:34
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition SkRefCnt.h:341
#define SkDegreesToRadians(degrees)
Definition SkScalar.h:77
#define SkScalarMod(x, y)
Definition SkScalar.h:41
#define SkScalarSin(radians)
Definition SkScalar.h:45
#define SkScalarCos(radians)
Definition SkScalar.h:46
static SkScalar center(float pos0, float pos1)
bool closed() const
Definition GrShape.cpp:328
SkRRect fRRect
Definition GrShape.h:272
GrShape & operator=(const GrShape &shape)
Definition GrShape.cpp:16
SkRect & rect()
Definition GrShape.h:134
GrLineSegment fLine
Definition GrShape.h:275
Type type() const
Definition GrShape.h:92
uint32_t stateKey() const
Definition GrShape.cpp:48
bool isPath() const
Definition GrShape.h:88
void setRRect(const SkRRect &rrect)
Definition GrShape.h:163
uint32_t segmentMask() const
Definition GrShape.cpp:391
void reset()
Definition GrShape.h:188
bool isRRect() const
Definition GrShape.h:87
bool isLine() const
Definition GrShape.h:90
SkRRect & rrect()
Definition GrShape.h:137
bool isRect() const
Definition GrShape.h:86
void setPath(const SkPath &path)
Definition GrShape.h:175
SkRect bounds() const
Definition GrShape.cpp:365
void setLine(const GrLineSegment &line)
Definition GrShape.h:171
@ kMakeCanonical_Flag
Definition GrShape.h:203
@ kIgnoreWinding_Flag
Definition GrShape.h:200
@ kSimpleFill_Flag
Definition GrShape.h:197
bool isPoint() const
Definition GrShape.h:85
SkRect fRect
Definition GrShape.h:271
SkArc & arc()
Definition GrShape.h:143
SkPath fPath
Definition GrShape.h:273
void setPathWindingParams(SkPathDirection dir, unsigned start)
Definition GrShape.h:112
SkArc fArc
Definition GrShape.h:274
SkPathDirection dir() const
Definition GrShape.h:105
static constexpr SkPathDirection kDefaultDir
Definition GrShape.h:60
static constexpr SkPathFillType kDefaultFillType
Definition GrShape.h:63
bool isArc() const
Definition GrShape.h:89
void setArc(const SkArc &arc)
Definition GrShape.h:167
bool conservativeContains(const SkRect &rect) const
Definition GrShape.cpp:287
SkPoint fPoint
Definition GrShape.h:270
unsigned startIndex() const
Definition GrShape.h:108
static constexpr unsigned kDefaultStart
Definition GrShape.h:61
void asPath(SkPath *out, bool simpleFill=true) const
Definition GrShape.cpp:421
SkPoint & point()
Definition GrShape.h:131
void setPoint(const SkPoint &point)
Definition GrShape.h:155
bool convex(bool simpleFill=true) const
Definition GrShape.cpp:346
void setRect(const SkRect &rect)
Definition GrShape.h:159
bool simplify(unsigned flags=kAll_Flags)
Definition GrShape.cpp:242
static bool IsClosedSingleContour(const SkPath &path)
Definition SkPathPriv.h:66
static bool IsRRect(const SkPath &path, SkRRect *rrect, SkPathDirection *dir, unsigned *start)
Definition SkPathPriv.h:272
static bool IsSimpleRect(const SkPath &path, bool isSimpleFill, SkRect *rect, SkPathDirection *direction, unsigned *start)
Definition SkPath.cpp:3180
static bool DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect)
Definition SkPath.cpp:3276
static bool IsOval(const SkPath &path, SkRect *rect, SkPathDirection *dir, unsigned *start)
Definition SkPathPriv.h:245
static void CreateDrawArcPath(SkPath *path, const SkRect &oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect)
Definition SkPath.cpp:3290
@ kLine_SegmentMask
Definition SkPath.h:1437
@ kConic_SegmentMask
Definition SkPath.h:1439
bool isEmpty() const
Definition SkPath.cpp:406
bool conservativelyContainsRect(const SkRect &rect) const
Definition SkPath.cpp:289
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
bool isLine(SkPoint line[2]) const
Definition SkPath.cpp:388
SkPathFillType getFillType() const
Definition SkPath.h:230
SkPath & addRRect(const SkRRect &rrect, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:990
SkPath & reset()
Definition SkPath.cpp:360
bool isConvex() const
Definition SkPath.cpp:416
const SkRect & getBounds() const
Definition SkPath.cpp:420
uint32_t getSegmentMasks() const
Definition SkPath.cpp:424
bool isLastContourClosed() const
Definition SkPath.cpp:380
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition SkPath.cpp:506
bool contains(SkScalar x, SkScalar y) const
Definition SkPath.cpp:3054
static bool ContainsPoint(const SkRRect &rr, const SkPoint &p)
Definition SkRRectPriv.h:48
bool isOval() const
Definition SkRRect.h:85
const SkRect & rect() const
Definition SkRRect.h:264
static SkRRect MakeOval(const SkRect &oval)
Definition SkRRect.h:162
bool isRect() const
Definition SkRRect.h:84
bool contains(const SkRect &rect) const
Definition SkRRect.cpp:360
bool isEmpty() const
Definition SkRRect.h:83
const SkRect & getBounds() const
Definition SkRRect.h:279
float SkScalar
Definition extension.cpp:12
static bool b
FlutterSemanticsFlag flags
SkPoint fP2
Definition GrShape.h:28
SkPoint fP1
Definition GrShape.h:27
bool fUseCenter
Definition SkArc.h:26
SkScalar fSweepAngle
Definition SkArc.h:22
SkScalar fStartAngle
Definition SkArc.h:20
SkRect fOval
Definition SkArc.h:17
float fX
x-axis value
float fY
y-axis value
SkRect makeSorted() const
Definition SkRect.h:1330
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
bool contains(SkScalar x, SkScalar y) const
Definition extension.cpp:19
constexpr float centerX() const
Definition SkRect.h:776
constexpr float height() const
Definition SkRect.h:769
constexpr float centerY() const
Definition SkRect.h:785
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
void sort()
Definition SkRect.h:1313
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15