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