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