xref: /third_party/skia/include/core/SkRRect.h (revision cb93a386)
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
14class SkPath;
15class SkMatrix;
16class 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*/
31class SK_API SkRRect {
32public:
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
72    Type getType() const {
73        SkASSERT(this->isValid());
74        return static_cast<Type>(fType);
75    }
76
77    Type type() const { return this->getType(); }
78
79    inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
80    inline bool isRect() const { return kRect_Type == this->getType(); }
81    inline bool isOval() const { return kOval_Type == this->getType(); }
82    inline bool isSimple() const { return kSimple_Type == this->getType(); }
83    inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
486    void dumpHex() const { this->dump(true); }
487
488private:
489    static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]);
490
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