• 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 "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkTypes.h"
15 
16 #include <cstdint>
17 #include <cstring>
18 
19 class SkMatrix;
20 class SkString;
21 
22 /** \class SkRRect
23     SkRRect describes a rounded rectangle with a bounds and a pair of radii for each corner.
24     The bounds and radii can be set so that SkRRect describes: a rectangle with sharp corners;
25     a circle; an oval; or a rectangle with one or more rounded corners.
26 
27     SkRRect allows implementing CSS properties that describe rounded corners.
28     SkRRect may have up to eight different radii, one for each axis on each of its four
29     corners.
30 
31     SkRRect may modify the provided parameters when initializing bounds and radii.
32     If either axis radii is zero or less: radii are stored as zero; corner is square.
33     If corner curves overlap, radii are proportionally reduced to fit within bounds.
34 */
35 class SK_API SkRRect {
36 public:
37 
38     /** Initializes bounds at (0, 0), the origin, with zero width and height.
39         Initializes corner radii to (0, 0), and sets type of kEmpty_Type.
40 
41         @return  empty SkRRect
42     */
43     SkRRect() = default;
44 
45     /** Initializes to copy of rrect bounds and corner radii.
46 
47         @param rrect  bounds and corner to copy
48         @return       copy of rrect
49     */
50     SkRRect(const SkRRect& rrect) = default;
51 
52     /** Copies rrect bounds and corner radii.
53 
54         @param rrect  bounds and corner to copy
55         @return       copy of rrect
56     */
57     SkRRect& operator=(const SkRRect& rrect) = default;
58 
59     /** \enum SkRRect::Type
60         Type describes possible specializations of SkRRect. Each Type is
61         exclusive; a SkRRect may only have one type.
62 
63         Type members become progressively less restrictive; larger values of
64         Type have more degrees of freedom than smaller values.
65     */
66     enum Type {
67         kEmpty_Type,                     //!< zero width or height
68         kRect_Type,                      //!< non-zero width and height, and zeroed radii
69         kOval_Type,                      //!< non-zero width and height filled with radii
70         kSimple_Type,                    //!< non-zero width and height with equal radii
71         kNinePatch_Type,                 //!< non-zero width and height with axis-aligned radii
72         kComplex_Type,                   //!< non-zero width and height with arbitrary radii
73         kLastType       = kComplex_Type, //!< largest Type value
74     };
75 
getType()76     Type getType() const {
77         SkASSERT(this->isValid());
78         return static_cast<Type>(fType);
79     }
80 
type()81     Type type() const { return this->getType(); }
82 
isEmpty()83     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
isRect()84     inline bool isRect() const { return kRect_Type == this->getType(); }
isOval()85     inline bool isOval() const { return kOval_Type == this->getType(); }
isSimple()86     inline bool isSimple() const { return kSimple_Type == this->getType(); }
isNinePatch()87     inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
isComplex()88     inline bool isComplex() const { return kComplex_Type == this->getType(); }
89 
90     /** Returns span on the x-axis. This does not check if result fits in 32-bit float;
91         result may be infinity.
92 
93         @return  rect().fRight minus rect().fLeft
94     */
width()95     SkScalar width() const { return fRect.width(); }
96 
97     /** Returns span on the y-axis. This does not check if result fits in 32-bit float;
98         result may be infinity.
99 
100         @return  rect().fBottom minus rect().fTop
101     */
height()102     SkScalar height() const { return fRect.height(); }
103 
104     /** Returns top-left corner radii. If type() returns kEmpty_Type, kRect_Type,
105         kOval_Type, or kSimple_Type, returns a value representative of all corner radii.
106         If type() returns kNinePatch_Type or kComplex_Type, at least one of the
107         remaining three corners has a different value.
108 
109         @return  corner radii for simple types
110     */
getSimpleRadii()111     SkVector getSimpleRadii() const {
112         return fRadii[0];
113     }
114 
115     /** Sets bounds to zero width and height at (0, 0), the origin. Sets
116         corner radii to zero and sets type to kEmpty_Type.
117     */
setEmpty()118     void setEmpty() { *this = SkRRect(); }
119 
120     /** Sets bounds to sorted rect, and sets corner radii to zero.
121         If set bounds has width and height, and sets type to kRect_Type;
122         otherwise, sets type to kEmpty_Type.
123 
124         @param rect  bounds to set
125     */
setRect(const SkRect & rect)126     void setRect(const SkRect& rect) {
127         if (!this->initializeRect(rect)) {
128             return;
129         }
130 
131         memset(fRadii, 0, sizeof(fRadii));
132         fType = kRect_Type;
133 
134         SkASSERT(this->isValid());
135     }
136 
137     /** Initializes bounds at (0, 0), the origin, with zero width and height.
138         Initializes corner radii to (0, 0), and sets type of kEmpty_Type.
139 
140         @return  empty SkRRect
141     */
MakeEmpty()142     static SkRRect MakeEmpty() { return SkRRect(); }
143 
144     /** Initializes to copy of r bounds and zeroes corner radii.
145 
146         @param r  bounds to copy
147         @return   copy of r
148     */
MakeRect(const SkRect & r)149     static SkRRect MakeRect(const SkRect& r) {
150         SkRRect rr;
151         rr.setRect(r);
152         return rr;
153     }
154 
155     /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii
156         to half oval.height(). If oval bounds is empty, sets to kEmpty_Type.
157         Otherwise, sets to kOval_Type.
158 
159         @param oval  bounds of oval
160         @return      oval
161     */
MakeOval(const SkRect & oval)162     static SkRRect MakeOval(const SkRect& oval) {
163         SkRRect rr;
164         rr.setOval(oval);
165         return rr;
166     }
167 
168     /** Sets to rounded rectangle with the same radii for all four corners.
169         If rect is empty, sets to kEmpty_Type.
170         Otherwise, if xRad and yRad are zero, sets to kRect_Type.
171         Otherwise, if xRad is at least half rect.width() and yRad is at least half
172         rect.height(), sets to kOval_Type.
173         Otherwise, sets to kSimple_Type.
174 
175         @param rect  bounds of rounded rectangle
176         @param xRad  x-axis radius of corners
177         @param yRad  y-axis radius of corners
178         @return      rounded rectangle
179     */
MakeRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)180     static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
181         SkRRect rr;
182         rr.setRectXY(rect, xRad, yRad);
183         return rr;
184     }
185 
186     /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii
187         to half oval.height(). If oval bounds is empty, sets to kEmpty_Type.
188         Otherwise, sets to kOval_Type.
189 
190         @param oval  bounds of oval
191     */
192     void setOval(const SkRect& oval);
193 
194     /** Sets to rounded rectangle with the same radii for all four corners.
195         If rect is empty, sets to kEmpty_Type.
196         Otherwise, if xRad or yRad is zero, sets to kRect_Type.
197         Otherwise, if xRad is at least half rect.width() and yRad is at least half
198         rect.height(), sets to kOval_Type.
199         Otherwise, sets to kSimple_Type.
200 
201         @param rect  bounds of rounded rectangle
202         @param xRad  x-axis radius of corners
203         @param yRad  y-axis radius of corners
204 
205         example: https://fiddle.skia.org/c/@RRect_setRectXY
206     */
207     void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
208 
209     /** Sets bounds to rect. Sets radii to (leftRad, topRad), (rightRad, topRad),
210         (rightRad, bottomRad), (leftRad, bottomRad).
211 
212         If rect is empty, sets to kEmpty_Type.
213         Otherwise, if leftRad and rightRad are zero, sets to kRect_Type.
214         Otherwise, if topRad and bottomRad are zero, sets to kRect_Type.
215         Otherwise, if leftRad and rightRad are equal and at least half rect.width(), and
216         topRad and bottomRad are equal at least half rect.height(), sets to kOval_Type.
217         Otherwise, if leftRad and rightRad are equal, and topRad and bottomRad are equal,
218         sets to kSimple_Type. Otherwise, sets to kNinePatch_Type.
219 
220         Nine patch refers to the nine parts defined by the radii: one center rectangle,
221         four edge patches, and four corner patches.
222 
223         @param rect       bounds of rounded rectangle
224         @param leftRad    left-top and left-bottom x-axis radius
225         @param topRad     left-top and right-top y-axis radius
226         @param rightRad   right-top and right-bottom x-axis radius
227         @param bottomRad  left-bottom and right-bottom y-axis radius
228     */
229     void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
230                       SkScalar rightRad, SkScalar bottomRad);
231 
232     /** Sets bounds to rect. Sets radii array for individual control of all for corners.
233 
234         If rect is empty, sets to kEmpty_Type.
235         Otherwise, if one of each corner radii are zero, sets to kRect_Type.
236         Otherwise, if all x-axis radii are equal and at least half rect.width(), and
237         all y-axis radii are equal at least half rect.height(), sets to kOval_Type.
238         Otherwise, if all x-axis radii are equal, and all y-axis radii are equal,
239         sets to kSimple_Type. Otherwise, sets to kNinePatch_Type.
240 
241         @param rect   bounds of rounded rectangle
242         @param radii  corner x-axis and y-axis radii
243 
244         example: https://fiddle.skia.org/c/@RRect_setRectRadii
245     */
246     void setRectRadii(const SkRect& rect, const SkVector radii[4]);
247 
248     /** \enum SkRRect::Corner
249         The radii are stored: top-left, top-right, bottom-right, bottom-left.
250     */
251     enum Corner {
252         kUpperLeft_Corner,  //!< index of top-left corner radii
253         kUpperRight_Corner, //!< index of top-right corner radii
254         kLowerRight_Corner, //!< index of bottom-right corner radii
255         kLowerLeft_Corner,  //!< index of bottom-left corner radii
256     };
257 
258     /** Returns bounds. Bounds may have zero width or zero height. Bounds right is
259         greater than or equal to left; bounds bottom is greater than or equal to top.
260         Result is identical to getBounds().
261 
262         @return  bounding box
263     */
rect()264     const SkRect& rect() const { return fRect; }
265 
266     /** Returns scalar pair for radius of curve on x-axis and y-axis for one corner.
267         Both radii may be zero. If not zero, both are positive and finite.
268 
269         @return        x-axis and y-axis radii for one corner
270     */
radii(Corner corner)271     SkVector radii(Corner corner) const { return fRadii[corner]; }
272 
273     /** Returns bounds. Bounds may have zero width or zero height. Bounds right is
274         greater than or equal to left; bounds bottom is greater than or equal to top.
275         Result is identical to rect().
276 
277         @return  bounding box
278     */
getBounds()279     const SkRect& getBounds() const { return fRect; }
280 
281     /** Returns true if bounds and radii in a are equal to bounds and radii in b.
282 
283         a and b are not equal if either contain NaN. a and b are equal if members
284         contain zeroes with different signs.
285 
286         @param a  SkRect bounds and radii to compare
287         @param b  SkRect bounds and radii to compare
288         @return   true if members are equal
289     */
290     friend bool operator==(const SkRRect& a, const SkRRect& b) {
291         return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
292     }
293 
294     /** Returns true if bounds and radii in a are not equal to bounds and radii in b.
295 
296         a and b are not equal if either contain NaN. a and b are equal if members
297         contain zeroes with different signs.
298 
299         @param a  SkRect bounds and radii to compare
300         @param b  SkRect bounds and radii to compare
301         @return   true if members are not equal
302     */
303     friend bool operator!=(const SkRRect& a, const SkRRect& b) {
304         return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
305     }
306 
307     /** Copies SkRRect to dst, then insets dst bounds by dx and dy, and adjusts dst
308         radii by dx and dy. dx and dy may be positive, negative, or zero. dst may be
309         SkRRect.
310 
311         If either corner radius is zero, the corner has no curvature and is unchanged.
312         Otherwise, if adjusted radius becomes negative, pins radius to zero.
313         If dx exceeds half dst bounds width, dst bounds left and right are set to
314         bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and
315         bottom are set to bounds y-axis center.
316 
317         If dx or dy cause the bounds to become infinite, dst bounds is zeroed.
318 
319         @param dx   added to rect().fLeft, and subtracted from rect().fRight
320         @param dy   added to rect().fTop, and subtracted from rect().fBottom
321         @param dst  insets bounds and radii
322 
323         example: https://fiddle.skia.org/c/@RRect_inset
324     */
325     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
326 
327     /** Insets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be
328         positive, negative, or zero.
329 
330         If either corner radius is zero, the corner has no curvature and is unchanged.
331         Otherwise, if adjusted radius becomes negative, pins radius to zero.
332         If dx exceeds half bounds width, bounds left and right are set to
333         bounds x-axis center. If dy exceeds half bounds height, bounds top and
334         bottom are set to bounds y-axis center.
335 
336         If dx or dy cause the bounds to become infinite, bounds is zeroed.
337 
338         @param dx  added to rect().fLeft, and subtracted from rect().fRight
339         @param dy  added to rect().fTop, and subtracted from rect().fBottom
340     */
inset(SkScalar dx,SkScalar dy)341     void inset(SkScalar dx, SkScalar dy) {
342         this->inset(dx, dy, this);
343     }
344 
345     /** Outsets dst bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be
346         positive, negative, or zero.
347 
348         If either corner radius is zero, the corner has no curvature and is unchanged.
349         Otherwise, if adjusted radius becomes negative, pins radius to zero.
350         If dx exceeds half dst bounds width, dst bounds left and right are set to
351         bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and
352         bottom are set to bounds y-axis center.
353 
354         If dx or dy cause the bounds to become infinite, dst bounds is zeroed.
355 
356         @param dx   subtracted from rect().fLeft, and added to rect().fRight
357         @param dy   subtracted from rect().fTop, and added to rect().fBottom
358         @param dst  outset bounds and radii
359     */
outset(SkScalar dx,SkScalar dy,SkRRect * dst)360     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
361         this->inset(-dx, -dy, dst);
362     }
363 
364     /** Outsets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be
365         positive, negative, or zero.
366 
367         If either corner radius is zero, the corner has no curvature and is unchanged.
368         Otherwise, if adjusted radius becomes negative, pins radius to zero.
369         If dx exceeds half bounds width, bounds left and right are set to
370         bounds x-axis center. If dy exceeds half bounds height, bounds top and
371         bottom are set to bounds y-axis center.
372 
373         If dx or dy cause the bounds to become infinite, bounds is zeroed.
374 
375         @param dx  subtracted from rect().fLeft, and added to rect().fRight
376         @param dy  subtracted from rect().fTop, and added to rect().fBottom
377     */
outset(SkScalar dx,SkScalar dy)378     void outset(SkScalar dx, SkScalar dy) {
379         this->inset(-dx, -dy, this);
380     }
381 
382     /** Translates SkRRect by (dx, dy).
383 
384         @param dx  offset added to rect().fLeft and rect().fRight
385         @param dy  offset added to rect().fTop and rect().fBottom
386     */
offset(SkScalar dx,SkScalar dy)387     void offset(SkScalar dx, SkScalar dy) {
388         fRect.offset(dx, dy);
389     }
390 
391     /** Returns SkRRect translated by (dx, dy).
392 
393         @param dx  offset added to rect().fLeft and rect().fRight
394         @param dy  offset added to rect().fTop and rect().fBottom
395         @return    SkRRect bounds offset by (dx, dy), with unchanged corner radii
396     */
makeOffset(SkScalar dx,SkScalar dy)397     SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const {
398         return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
399     }
400 
401     /** Returns true if rect is inside the bounds and corner radii, and if
402         SkRRect and rect are not empty.
403 
404         @param rect  area tested for containment
405         @return      true if SkRRect contains rect
406 
407         example: https://fiddle.skia.org/c/@RRect_contains
408     */
409     bool contains(const SkRect& rect) const;
410 
411     /** Returns true if bounds and radii values are finite and describe a SkRRect
412         SkRRect::Type that matches getType(). All SkRRect methods construct valid types,
413         even if the input values are not valid. Invalid SkRRect data can only
414         be generated by corrupting memory.
415 
416         @return  true if bounds and radii match type()
417 
418         example: https://fiddle.skia.org/c/@RRect_isValid
419     */
420     bool isValid() const;
421 
422     static constexpr size_t kSizeInMemory = 12 * sizeof(SkScalar);
423 
424     /** Writes SkRRect to buffer. Writes kSizeInMemory bytes, and returns
425         kSizeInMemory, the number of bytes written.
426 
427         @param buffer  storage for SkRRect
428         @return        bytes written, kSizeInMemory
429 
430         example: https://fiddle.skia.org/c/@RRect_writeToMemory
431     */
432     size_t writeToMemory(void* buffer) const;
433 
434     /** Reads SkRRect from buffer, reading kSizeInMemory bytes.
435         Returns kSizeInMemory, bytes read if length is at least kSizeInMemory.
436         Otherwise, returns zero.
437 
438         @param buffer  memory to read from
439         @param length  size of buffer
440         @return        bytes read, or 0 if length is less than kSizeInMemory
441 
442         example: https://fiddle.skia.org/c/@RRect_readFromMemory
443     */
444     size_t readFromMemory(const void* buffer, size_t length);
445 
446     /** Transforms by SkRRect by matrix, storing result in dst.
447         Returns true if SkRRect transformed can be represented by another SkRRect.
448         Returns false if matrix contains transformations that are not axis aligned.
449 
450         Asserts in debug builds if SkRRect equals dst.
451 
452         @param matrix  SkMatrix specifying the transform
453         @param dst     SkRRect to store the result
454         @return        true if transformation succeeded.
455 
456         example: https://fiddle.skia.org/c/@RRect_transform
457     */
458     bool transform(const SkMatrix& matrix, SkRRect* dst) const;
459 
460     /** Writes text representation of SkRRect to standard output.
461         Set asHex true to generate exact binary representations
462         of floating point numbers.
463 
464         @param asHex  true if SkScalar values are written as hexadecimal
465 
466         example: https://fiddle.skia.org/c/@RRect_dump
467     */
468     void dump(bool asHex) const;
469     SkString dumpToString(bool asHex) const;
470 
471     /** Writes text representation of SkRRect to standard output. The representation
472         may be directly compiled as C++ code. Floating point values are written
473         with limited precision; it may not be possible to reconstruct original
474         SkRRect from output.
475     */
dump()476     void dump() const { this->dump(false); }
477 
478     /** Writes text representation of SkRRect to standard output. The representation
479         may be directly compiled as C++ code. Floating point values are written
480         in hexadecimal to preserve their exact bit pattern. The output reconstructs the
481         original SkRRect.
482     */
dumpHex()483     void dumpHex() const { this->dump(true); }
484 
485 private:
486     static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]);
487 
SkRRect(const SkRect & rect,const SkVector radii[4],int32_t type)488     SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type)
489         : fRect(rect)
490         , fRadii{radii[0], radii[1], radii[2], radii[3]}
491         , fType(type) {}
492 
493     /**
494      * Initializes fRect. If the passed in rect is not finite or empty the rrect will be fully
495      * initialized and false is returned. Otherwise, just fRect is initialized and true is returned.
496      */
497     bool initializeRect(const SkRect&);
498 
499     void computeType();
500     bool checkCornerContainment(SkScalar x, SkScalar y) const;
501     // Returns true if the radii had to be scaled to fit rect
502     bool scaleRadii();
503 
504     SkRect fRect = SkRect::MakeEmpty();
505     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
506     SkVector fRadii[4] = {{0, 0}, {0, 0}, {0,0}, {0,0}};
507     // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
508     int32_t fType = kEmpty_Type;
509     // TODO: add padding so we can use memcpy for flattening and not copy uninitialized data
510 
511     // to access fRadii directly
512     friend class SkPath;
513     friend class SkRRectPriv;
514 };
515 
516 #endif
517