• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 SkRRect_DEFINED
9 #define SkRRect_DEFINED
10 
11 #include "SkRect.h"
12 #include "SkPoint.h"
13 
14 class SkPath;
15 class SkMatrix;
16 class SkRBuffer;
17 class SkWBuffer;
18 
19 // Path forward:
20 //   core work
21 //      add contains(SkRect&)  - for clip stack
22 //      add contains(SkRRect&) - for clip stack
23 //      add heart rect computation (max rect inside RR)
24 //      add 9patch rect computation
25 //      add growToInclude(SkPath&)
26 //   analysis
27 //      use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
28 //      check on # of rectorus's the RRs could handle
29 //   rendering work
30 //      update SkPath.addRRect() to only use quads
31 //      add GM and bench
32 //   further out
33 //      detect and triangulate RRectorii rather than falling back to SW in Ganesh
34 //
35 
36 /** \class SkRRect
37 
38     The SkRRect class represents a rounded rect with a potentially different
39     radii for each corner. It does not have a constructor so must be
40     initialized with one of the initialization functions (e.g., setEmpty,
41     setRectRadii, etc.)
42 
43     This class is intended to roughly match CSS' border-*-*-radius capabilities.
44     This means:
45         If either of a corner's radii are 0 the corner will be square.
46         Negative radii are not allowed (they are clamped to zero).
47         If the corner curves overlap they will be proportionally reduced to fit.
48 */
49 class SK_API SkRRect {
50 public:
51     /** Default initialized to a rrect at the origin with zero width and height. */
52     SkRRect() = default;
53 
54     SkRRect(const SkRRect&) = default;
55     SkRRect& operator=(const SkRRect&) = default;
56 
57     /**
58      * Enum to capture the various possible subtypes of RR. Accessed
59      * by type(). The subtypes become progressively less restrictive.
60      */
61     enum Type {
62         // !< The RR has zero width and/or zero height. All radii are zero.
63         kEmpty_Type,
64 
65         //!< The RR is actually a (non-empty) rect (i.e., at least one radius
66         //!< at each corner is zero)
67         kRect_Type,
68 
69         //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
70         //!< and >= width/2 and all the y radii are equal and >= height/2
71         kOval_Type,
72 
73         //!< The RR is non-empty and all the x radii are equal & all y radii
74         //!< are equal but it is not an oval (i.e., there are lines between
75         //!< the curves) nor a rect (i.e., both radii are non-zero)
76         kSimple_Type,
77 
78         //!< The RR is non-empty and the two left x radii are equal, the two top
79         //!< y radii are equal, and the same for the right and bottom but it is
80         //!< neither an rect, oval, nor a simple RR. It is called "nine patch"
81         //!< because the centers of the corner ellipses form an axis aligned
82         //!< rect with edges that divide the RR into an 9 rectangular patches:
83         //!< an interior patch, four edge patches, and four corner patches.
84         kNinePatch_Type,
85 
86         //!< A fully general (non-empty) RR. Some of the x and/or y radii are
87         //!< different from the others and there must be one corner where
88         //!< both radii are non-zero.
89         kComplex_Type,
90 
91         kLastType = kComplex_Type,
92     };
93 
94     /**
95      * Returns the RR's sub type.
96      */
getType()97     Type getType() const {
98         SkASSERT(this->isValid());
99         return static_cast<Type>(fType);
100     }
101 
type()102     Type type() const { return this->getType(); }
103 
isEmpty()104     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
isRect()105     inline bool isRect() const { return kRect_Type == this->getType(); }
isOval()106     inline bool isOval() const { return kOval_Type == this->getType(); }
isSimple()107     inline bool isSimple() const { return kSimple_Type == this->getType(); }
108     // TODO: should isSimpleCircular & isCircle take a tolerance? This could help
109     // instances where the mapping to device space is noisy.
isSimpleCircular()110     inline bool isSimpleCircular() const {
111         return this->isSimple() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
112     }
isCircle()113     inline bool isCircle() const {
114         return this->isOval() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
115     }
isNinePatch()116     inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
isComplex()117     inline bool isComplex() const { return kComplex_Type == this->getType(); }
118 
119     bool allCornersCircular(SkScalar tolerance = SK_ScalarNearlyZero) const;
120 
width()121     SkScalar width() const { return fRect.width(); }
height()122     SkScalar height() const { return fRect.height(); }
123 
124     /**
125      * Same as default initialized - zero width and height at the origin.
126      */
setEmpty()127     void setEmpty() { *this = SkRRect(); }
128 
129     /**
130      * Set this RR to match the supplied rect. All radii will be 0.
131      */
setRect(const SkRect & rect)132     void setRect(const SkRect& rect) {
133         if (!this->initializeRect(rect)) {
134             return;
135         }
136 
137         memset(fRadii, 0, sizeof(fRadii));
138         fType = kRect_Type;
139 
140         SkASSERT(this->isValid());
141     }
142 
143     /** Makes an empty rrect at the origin with zero width and height. */
MakeEmpty()144     static SkRRect MakeEmpty() { return SkRRect(); }
145 
MakeRect(const SkRect & r)146     static SkRRect MakeRect(const SkRect& r) {
147         SkRRect rr;
148         rr.setRect(r);
149         return rr;
150     }
151 
MakeOval(const SkRect & oval)152     static SkRRect MakeOval(const SkRect& oval) {
153         SkRRect rr;
154         rr.setOval(oval);
155         return rr;
156     }
157 
MakeRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)158     static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
159         SkRRect rr;
160         rr.setRectXY(rect, xRad, yRad);
161         return rr;
162     }
163 
164     /**
165      * Set this RR to match the supplied oval. All x radii will equal half the
166      * width and all y radii will equal half the height.
167      */
setOval(const SkRect & oval)168     void setOval(const SkRect& oval) {
169         if (!this->initializeRect(oval)) {
170             return;
171         }
172 
173         SkScalar xRad = SkScalarHalf(fRect.width());
174         SkScalar yRad = SkScalarHalf(fRect.height());
175 
176         for (int i = 0; i < 4; ++i) {
177             fRadii[i].set(xRad, yRad);
178         }
179         fType = kOval_Type;
180 
181         SkASSERT(this->isValid());
182     }
183 
184     /**
185      * Initialize the RR with the same radii for all four corners.
186      */
187     void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
188 
189     /**
190      * Initialize the rr with one radius per-side.
191      */
192     void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
193                       SkScalar rightRad, SkScalar bottomRad);
194 
195     /**
196      * Initialize the RR with potentially different radii for all four corners.
197      */
198     void setRectRadii(const SkRect& rect, const SkVector radii[4]);
199 
200     // The radii are stored in UL, UR, LR, LL order.
201     enum Corner {
202         kUpperLeft_Corner,
203         kUpperRight_Corner,
204         kLowerRight_Corner,
205         kLowerLeft_Corner
206     };
207 
rect()208     const SkRect& rect() const { return fRect; }
radii(Corner corner)209     const SkVector& radii(Corner corner) const { return fRadii[corner]; }
getBounds()210     const SkRect& getBounds() const { return fRect; }
211 
212     /**
213      *  When a rrect is simple, all of its radii are equal. This returns one
214      *  of those radii. This call requires the rrect to be non-complex.
215      */
getSimpleRadii()216     const SkVector& getSimpleRadii() const {
217         SkASSERT(!this->isComplex());
218         return fRadii[0];
219     }
220 
221     friend bool operator==(const SkRRect& a, const SkRRect& b) {
222         return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
223     }
224 
225     friend bool operator!=(const SkRRect& a, const SkRRect& b) {
226         return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
227     }
228 
229     /**
230      *  Call inset on the bounds, and adjust the radii to reflect what happens
231      *  in stroking: If the corner is sharp (no curvature), leave it alone,
232      *  otherwise we grow/shrink the radii by the amount of the inset. If a
233      *  given radius becomes negative, it is pinned to 0.
234      *
235      *  If the inset amount is larger than the width/height then the rrect collapses to
236      *  a degenerate line or point.
237      *
238      *  If the inset is sufficiently negative to cause the bounds to become infinite then
239      *  the result is a default initialized rrect.
240      *
241      *  It is valid for dst == this.
242      */
243     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
244 
inset(SkScalar dx,SkScalar dy)245     void inset(SkScalar dx, SkScalar dy) {
246         this->inset(dx, dy, this);
247     }
248 
249     /**
250      *  Call outset on the bounds, and adjust the radii to reflect what happens
251      *  in stroking: If the corner is sharp (no curvature), leave it alone,
252      *  otherwise we grow/shrink the radii by the amount of the inset. If a
253      *  given radius becomes negative, it is pinned to 0.
254      *
255      *  It is valid for dst == this.
256      */
outset(SkScalar dx,SkScalar dy,SkRRect * dst)257     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
258         this->inset(-dx, -dy, dst);
259     }
outset(SkScalar dx,SkScalar dy)260     void outset(SkScalar dx, SkScalar dy) {
261         this->inset(-dx, -dy, this);
262     }
263 
264     /**
265      * Translate the rrect by (dx, dy).
266      */
offset(SkScalar dx,SkScalar dy)267     void offset(SkScalar dx, SkScalar dy) {
268         fRect.offset(dx, dy);
269     }
270 
makeOffset(SkScalar dx,SkScalar dy)271     SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const {
272         return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
273     }
274 
275     /**
276      *  Returns true if 'rect' is wholy inside the RR, and both
277      *  are not empty.
278      */
279     bool contains(const SkRect& rect) const;
280 
281     bool isValid() const;
282     static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]);
283 
284     enum {
285         kSizeInMemory = 12 * sizeof(SkScalar)
286     };
287 
288     /**
289      *  Write the rrect into the specified buffer. This is guaranteed to always
290      *  write kSizeInMemory bytes, and that value is guaranteed to always be
291      *  a multiple of 4. Return kSizeInMemory.
292      */
293     size_t writeToMemory(void* buffer) const;
294     void writeToBuffer(SkWBuffer*) const;
295 
296     /**
297      * Reads the rrect from the specified buffer
298      *
299      * If the specified buffer is large enough, this will read kSizeInMemory bytes,
300      * and that value is guaranteed to always be a multiple of 4.
301      *
302      * @param buffer Memory to read from
303      * @param length Amount of memory available in the buffer
304      * @return number of bytes read (must be a multiple of 4) or
305      *         0 if there was not enough memory available
306      */
307     size_t readFromMemory(const void* buffer, size_t length);
308     bool readFromBuffer(SkRBuffer*);
309 
310     /**
311      *  Transform by the specified matrix, and put the result in dst.
312      *
313      *  @param matrix SkMatrix specifying the transform. Must only contain
314      *      scale and/or translate, or this call will fail.
315      *  @param dst SkRRect to store the result. It is an error to use this,
316      *      which would make this function no longer const.
317      *  @return true on success, false on failure.
318      */
319     bool transform(const SkMatrix& matrix, SkRRect* dst) const;
320 
321     void dump(bool asHex) const;
dump()322     void dump() const { this->dump(false); }
dumpHex()323     void dumpHex() const { this->dump(true); }
324 
325 private:
SkRRect(const SkRect & rect,const SkVector radii[4],int32_t type)326     SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type)
327         : fRect(rect)
328         , fRadii{radii[0], radii[1], radii[2], radii[3]}
329         , fType(type) {}
330 
331     /**
332      * Initializes fRect. If the passed in rect is not finite or empty the rrect will be fully
333      * initialized and false is returned. Otherwise, just fRect is initialized and true is returned.
334      */
335     bool initializeRect(const SkRect&);
336 
337     void computeType();
338     bool checkCornerContainment(SkScalar x, SkScalar y) const;
339     void scaleRadii();
340 
341     SkRect fRect = SkRect::MakeEmpty();
342     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
343     SkVector fRadii[4] = {{0, 0}, {0, 0}, {0,0}, {0,0}};
344     // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
345     int32_t fType = kEmpty_Type;
346     // TODO: add padding so we can use memcpy for flattening and not copy uninitialized data
347 
348     // to access fRadii directly
349     friend class SkPath;
350 };
351 
352 #endif
353