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