Coverage Report

Created: 2024-05-20 07:14

/src/skia/include/core/SkRRect.h
Line
Count
Source (jump to first uncovered line)
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
3.42M
    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
76
859k
    Type getType() const {
77
859k
        SkASSERT(this->isValid());
78
859k
        return static_cast<Type>(fType);
79
859k
    }
80
81
1.57k
    Type type() const { return this->getType(); }
82
83
288k
    inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
84
473k
    inline bool isRect() const { return kRect_Type == this->getType(); }
85
93.4k
    inline bool isOval() const { return kOval_Type == this->getType(); }
86
1.06k
    inline bool isSimple() const { return kSimple_Type == this->getType(); }
87
156
    inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
88
547
    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
    */
95
0
    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
    */
102
0
    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
    */
111
284
    SkVector getSimpleRadii() const {
112
284
        return fRadii[0];
113
284
    }
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
    */
118
2.06k
    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
    */
126
109k
    void setRect(const SkRect& rect) {
127
109k
        if (!this->initializeRect(rect)) {
128
5.15k
            return;
129
5.15k
        }
130
131
104k
        memset(fRadii, 0, sizeof(fRadii));
132
104k
        fType = kRect_Type;
133
134
104k
        SkASSERT(this->isValid());
135
104k
    }
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
    */
142
467
    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
    */
149
74.3k
    static SkRRect MakeRect(const SkRect& r) {
150
74.3k
        SkRRect rr;
151
74.3k
        rr.setRect(r);
152
74.3k
        return rr;
153
74.3k
    }
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
    */
162
142k
    static SkRRect MakeOval(const SkRect& oval) {
163
142k
        SkRRect rr;
164
142k
        rr.setOval(oval);
165
142k
        return rr;
166
142k
    }
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
    */
180
63.8k
    static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
181
63.8k
        SkRRect rr;
182
63.8k
        rr.setRectXY(rect, xRad, yRad);
183
63.8k
        return rr;
184
63.8k
    }
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
    */
264
10.7k
    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
    */
271
198k
    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
    */
279
650k
    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
204k
    friend bool operator==(const SkRRect& a, const SkRRect& b) {
291
204k
        return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
292
204k
    }
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
0
    friend bool operator!=(const SkRRect& a, const SkRRect& b) {
304
0
        return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
305
0
    }
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
    */
341
0
    void inset(SkScalar dx, SkScalar dy) {
342
0
        this->inset(dx, dy, this);
343
0
    }
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
    */
360
0
    void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
361
0
        this->inset(-dx, -dy, dst);
362
0
    }
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
    */
378
0
    void outset(SkScalar dx, SkScalar dy) {
379
0
        this->inset(-dx, -dy, this);
380
0
    }
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
    */
387
0
    void offset(SkScalar dx, SkScalar dy) {
388
0
        fRect.offset(dx, dy);
389
0
    }
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
    */
397
0
    [[nodiscard]] SkRRect makeOffset(SkScalar dx, SkScalar dy) const {
398
0
        return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
399
0
    }
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
    */
476
0
    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
    */
483
0
    void dumpHex() const { this->dump(true); }
484
485
private:
486
    static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]);
487
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
0
        , 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