Coverage Report

Created: 2024-09-14 07:19

/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/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
3.59M
    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
77
1.04M
    Type getType() const {
78
1.04M
        SkASSERT(this->isValid());
79
1.04M
        return static_cast<Type>(fType);
80
1.04M
    }
81
82
6.63k
    Type type() const { return this->getType(); }
83
84
334k
    inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
85
588k
    inline bool isRect() const { return kRect_Type == this->getType(); }
86
111k
    inline bool isOval() const { return kOval_Type == this->getType(); }
87
3.60k
    inline bool isSimple() const { return kSimple_Type == this->getType(); }
88
1.32k
    inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
89
2.55k
    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
    */
96
0
    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
    */
103
0
    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
    */
112
386
    SkVector getSimpleRadii() const {
113
386
        return fRadii[0];
114
386
    }
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
    */
119
726
    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
    */
127
181k
    void setRect(const SkRect& rect) {
128
181k
        if (!this->initializeRect(rect)) {
129
7.67k
            return;
130
7.67k
        }
131
132
174k
        memset(fRadii, 0, sizeof(fRadii));
133
174k
        fType = kRect_Type;
134
135
174k
        SkASSERT(this->isValid());
136
174k
    }
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
    */
143
1.36k
    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
    */
150
153k
    static SkRRect MakeRect(const SkRect& r) {
151
153k
        SkRRect rr;
152
153k
        rr.setRect(r);
153
153k
        return rr;
154
153k
    }
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
    */
163
195k
    static SkRRect MakeOval(const SkRect& oval) {
164
195k
        SkRRect rr;
165
195k
        rr.setOval(oval);
166
195k
        return rr;
167
195k
    }
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
    */
181
78.3k
    static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
182
78.3k
        SkRRect rr;
183
78.3k
        rr.setRectXY(rect, xRad, yRad);
184
78.3k
        return rr;
185
78.3k
    }
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
    */
265
26.1k
    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
    */
272
189k
    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
     */
276
0
    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
    */
284
822k
    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
273k
    friend bool operator==(const SkRRect& a, const SkRRect& b) {
296
273k
        return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
297
273k
    }
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
0
    friend bool operator!=(const SkRRect& a, const SkRRect& b) {
309
0
        return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8);
310
0
    }
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
    */
346
0
    void inset(SkScalar dx, SkScalar dy) {
347
0
        this->inset(dx, dy, this);
348
0
    }
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
    */
365
0
    void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
366
0
        this->inset(-dx, -dy, dst);
367
0
    }
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
    */
383
0
    void outset(SkScalar dx, SkScalar dy) {
384
0
        this->inset(-dx, -dy, this);
385
0
    }
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
    */
392
0
    void offset(SkScalar dx, SkScalar dy) {
393
0
        fRect.offset(dx, dy);
394
0
    }
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
    */
402
0
    [[nodiscard]] SkRRect makeOffset(SkScalar dx, SkScalar dy) const {
403
0
        return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
404
0
    }
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
    */
481
0
    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
    */
488
0
    void dumpHex() const { this->dump(true); }
489
490
private:
491
    static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]);
492
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
0
        , 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