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