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