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