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