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