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 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 */ 30 class SK_API SkRRect { 31 public: 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 /** Returns SkRRect::Type, one of: 72 kEmpty_Type, kRect_Type, kOval_Type, kSimple_Type, kNinePatch_Type, 73 kComplex_Type. 74 75 @return SkRRect::Type 76 */ getType()77 Type getType() const { 78 SkASSERT(this->isValid()); 79 return static_cast<Type>(fType); 80 } 81 82 /** Returns SkRRect::Type, one of: 83 kEmpty_Type, kRect_Type, kOval_Type, kSimple_Type, kNinePatch_Type, 84 kComplex_Type. 85 86 @return SkRRect::Type 87 */ type()88 Type type() const { return this->getType(); } 89 isEmpty()90 inline bool isEmpty() const { return kEmpty_Type == this->getType(); } isRect()91 inline bool isRect() const { return kRect_Type == this->getType(); } isOval()92 inline bool isOval() const { return kOval_Type == this->getType(); } isSimple()93 inline bool isSimple() const { return kSimple_Type == this->getType(); } isNinePatch()94 inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); } isComplex()95 inline bool isComplex() const { return kComplex_Type == this->getType(); } 96 97 /** Returns span on the x-axis. This does not check if result fits in 32-bit float; 98 result may be infinity. 99 100 @return rect().fRight minus rect().fLeft 101 */ width()102 SkScalar width() const { return fRect.width(); } 103 104 /** Returns span on the y-axis. This does not check if result fits in 32-bit float; 105 result may be infinity. 106 107 @return rect().fBottom minus rect().fTop 108 */ height()109 SkScalar height() const { return fRect.height(); } 110 111 /** Returns top-left corner radii. If type() returns kEmpty_Type, kRect_Type, 112 kOval_Type, or kSimple_Type, returns a value representative of all corner radii. 113 If type() returns kNinePatch_Type or kComplex_Type, at least one of the 114 remaining three corners has a different value. 115 116 @return corner radii for simple types 117 */ getSimpleRadii()118 SkVector getSimpleRadii() const { 119 return fRadii[0]; 120 } 121 122 /** Sets bounds to zero width and height at (0, 0), the origin. Sets 123 corner radii to zero and sets type to kEmpty_Type. 124 */ setEmpty()125 void setEmpty() { *this = SkRRect(); } 126 127 /** Sets bounds to sorted rect, and sets corner radii to zero. 128 If set bounds has width and height, and sets type to kRect_Type; 129 otherwise, sets type to kEmpty_Type. 130 131 @param rect bounds to set 132 */ setRect(const SkRect & rect)133 void setRect(const SkRect& rect) { 134 if (!this->initializeRect(rect)) { 135 return; 136 } 137 138 memset(fRadii, 0, sizeof(fRadii)); 139 fType = kRect_Type; 140 141 SkASSERT(this->isValid()); 142 } 143 144 /** Initializes bounds at (0, 0), the origin, with zero width and height. 145 Initializes corner radii to (0, 0), and sets type of kEmpty_Type. 146 147 @return empty SkRRect 148 */ MakeEmpty()149 static SkRRect MakeEmpty() { return SkRRect(); } 150 151 /** Initializes to copy of r bounds and zeroes corner radii. 152 153 @param r bounds to copy 154 @return copy of r 155 */ MakeRect(const SkRect & r)156 static SkRRect MakeRect(const SkRect& r) { 157 SkRRect rr; 158 rr.setRect(r); 159 return rr; 160 } 161 162 /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii 163 to half oval.height(). If oval bounds is empty, sets to kEmpty_Type. 164 Otherwise, sets to kOval_Type. 165 166 @param oval bounds of oval 167 @return oval 168 */ MakeOval(const SkRect & oval)169 static SkRRect MakeOval(const SkRect& oval) { 170 SkRRect rr; 171 rr.setOval(oval); 172 return rr; 173 } 174 175 /** Sets to rounded rectangle with the same radii for all four corners. 176 If rect is empty, sets to kEmpty_Type. 177 Otherwise, if xRad and yRad are zero, sets to kRect_Type. 178 Otherwise, if xRad is at least half rect.width() and yRad is at least half 179 rect.height(), sets to kOval_Type. 180 Otherwise, sets to kSimple_Type. 181 182 @param rect bounds of rounded rectangle 183 @param xRad x-axis radius of corners 184 @param yRad y-axis radius of corners 185 @return rounded rectangle 186 */ MakeRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)187 static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 188 SkRRect rr; 189 rr.setRectXY(rect, xRad, yRad); 190 return rr; 191 } 192 193 /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii 194 to half oval.height(). If oval bounds is empty, sets to kEmpty_Type. 195 Otherwise, sets to kOval_Type. 196 197 @param oval bounds of oval 198 */ setOval(const SkRect & oval)199 void setOval(const SkRect& oval) { 200 if (!this->initializeRect(oval)) { 201 return; 202 } 203 204 SkScalar xRad = SkScalarHalf(fRect.width()); 205 SkScalar yRad = SkScalarHalf(fRect.height()); 206 207 for (int i = 0; i < 4; ++i) { 208 fRadii[i].set(xRad, yRad); 209 } 210 fType = kOval_Type; 211 212 SkASSERT(this->isValid()); 213 } 214 215 /** Sets to rounded rectangle with the same radii for all four corners. 216 If rect is empty, sets to kEmpty_Type. 217 Otherwise, if xRad or yRad is zero, sets to kRect_Type. 218 Otherwise, if xRad is at least half rect.width() and yRad is at least half 219 rect.height(), sets to kOval_Type. 220 Otherwise, sets to kSimple_Type. 221 222 @param rect bounds of rounded rectangle 223 @param xRad x-axis radius of corners 224 @param yRad y-axis radius of corners 225 */ 226 void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); 227 228 /** Sets bounds to rect. Sets radii to (leftRad, topRad), (rightRad, topRad), 229 (rightRad, bottomRad), (leftRad, bottomRad). 230 231 If rect is empty, sets to kEmpty_Type. 232 Otherwise, if leftRad and rightRad are zero, sets to kRect_Type. 233 Otherwise, if topRad and bottomRad are zero, sets to kRect_Type. 234 Otherwise, if leftRad and rightRad are equal and at least half rect.width(), and 235 topRad and bottomRad are equal at least half rect.height(), sets to kOval_Type. 236 Otherwise, if leftRad and rightRad are equal, and topRad and bottomRad are equal, 237 sets to kSimple_Type. Otherwise, sets to kNinePatch_Type. 238 239 Nine patch refers to the nine parts defined by the radii: one center rectangle, 240 four edge patches, and four corner patches. 241 242 @param rect bounds of rounded rectangle 243 @param leftRad left-top and left-bottom x-axis radius 244 @param topRad left-top and right-top y-axis radius 245 @param rightRad right-top and right-bottom x-axis radius 246 @param bottomRad left-bottom and right-bottom y-axis radius 247 */ 248 void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, 249 SkScalar rightRad, SkScalar bottomRad); 250 251 /** Sets bounds to rect. Sets radii array for individual control of all for corners. 252 253 If rect is empty, sets to kEmpty_Type. 254 Otherwise, if one of each corner radii are zero, sets to kRect_Type. 255 Otherwise, if all x-axis radii are equal and at least half rect.width(), and 256 all y-axis radii are equal at least half rect.height(), sets to kOval_Type. 257 Otherwise, if all x-axis radii are equal, and all y-axis radii are equal, 258 sets to kSimple_Type. Otherwise, sets to kNinePatch_Type. 259 260 @param rect bounds of rounded rectangle 261 @param radii corner x-axis and y-axis radii 262 */ 263 void setRectRadii(const SkRect& rect, const SkVector radii[4]); 264 265 /** \enum SkRRect::Corner 266 The radii are stored: top-left, top-right, bottom-right, bottom-left. 267 */ 268 enum Corner { 269 kUpperLeft_Corner, //!< index of top-left corner radii 270 kUpperRight_Corner, //!< index of top-right corner radii 271 kLowerRight_Corner, //!< index of bottom-right corner radii 272 kLowerLeft_Corner, //!< index of bottom-left corner radii 273 }; 274 275 /** Returns bounds. Bounds may have zero width or zero height. Bounds right is 276 greater than or equal to left; bounds bottom is greater than or equal to top. 277 Result is identical to getBounds(). 278 279 @return bounding box 280 */ rect()281 const SkRect& rect() const { return fRect; } 282 283 /** Returns scalar pair for radius of curve on x-axis and y-axis for one corner. 284 Both radii may be zero. If not zero, both are positive and finite. 285 286 @param corner one of: kUpperLeft_Corner, kUpperRight_Corner, 287 kLowerRight_Corner, kLowerLeft_Corner 288 @return x-axis and y-axis radii for one corner 289 */ radii(Corner corner)290 SkVector radii(Corner corner) const { return fRadii[corner]; } 291 292 /** Returns bounds. Bounds may have zero width or zero height. Bounds right is 293 greater than or equal to left; bounds bottom is greater than or equal to top. 294 Result is identical to rect(). 295 296 @return bounding box 297 */ getBounds()298 const SkRect& getBounds() const { return fRect; } 299 300 /** Returns true if bounds and radii in a are equal to bounds and radii in b. 301 302 a and b are not equal if either contain NaN. a and b are equal if members 303 contain zeroes with different signs. 304 305 @param a SkRect bounds and radii to compare 306 @param b SkRect bounds and radii to compare 307 @return true if members are equal 308 */ 309 friend bool operator==(const SkRRect& a, const SkRRect& b) { 310 return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); 311 } 312 313 /** Returns true if bounds and radii in a are not equal to bounds and radii in b. 314 315 a and b are not equal if either contain NaN. a and b are equal if members 316 contain zeroes with different signs. 317 318 @param a SkRect bounds and radii to compare 319 @param b SkRect bounds and radii to compare 320 @return true if members are not equal 321 */ 322 friend bool operator!=(const SkRRect& a, const SkRRect& b) { 323 return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); 324 } 325 326 /** Copies SkRRect to dst, then insets dst bounds by dx and dy, and adjusts dst 327 radii by dx and dy. dx and dy may be positive, negative, or zero. dst may be 328 SkRRect. 329 330 If either corner radius is zero, the corner has no curvature and is unchanged. 331 Otherwise, if adjusted radius becomes negative, pins radius to zero. 332 If dx exceeds half dst bounds width, dst bounds left and right are set to 333 bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and 334 bottom are set to bounds y-axis center. 335 336 If dx or dy cause the bounds to become infinite, dst bounds is zeroed. 337 338 @param dx added to rect().fLeft, and subtracted from rect().fRight 339 @param dy added to rect().fTop, and subtracted from rect().fBottom 340 @param dst insets bounds and radii 341 */ 342 void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; 343 344 /** Insets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be 345 positive, negative, or zero. 346 347 If either corner radius is zero, the corner has no curvature and is unchanged. 348 Otherwise, if adjusted radius becomes negative, pins radius to zero. 349 If dx exceeds half bounds width, bounds left and right are set to 350 bounds x-axis center. If dy exceeds half bounds height, bounds top and 351 bottom are set to bounds y-axis center. 352 353 If dx or dy cause the bounds to become infinite, bounds is zeroed. 354 355 @param dx added to rect().fLeft, and subtracted from rect().fRight 356 @param dy added to rect().fTop, and subtracted from rect().fBottom 357 */ inset(SkScalar dx,SkScalar dy)358 void inset(SkScalar dx, SkScalar dy) { 359 this->inset(dx, dy, this); 360 } 361 362 /** Outsets dst bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be 363 positive, negative, or zero. 364 365 If either corner radius is zero, the corner has no curvature and is unchanged. 366 Otherwise, if adjusted radius becomes negative, pins radius to zero. 367 If dx exceeds half dst bounds width, dst bounds left and right are set to 368 bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and 369 bottom are set to bounds y-axis center. 370 371 If dx or dy cause the bounds to become infinite, dst bounds is zeroed. 372 373 @param dx subtracted from rect().fLeft, and added to rect().fRight 374 @param dy subtracted from rect().fTop, and added to rect().fBottom 375 @param dst outset bounds and radii 376 */ outset(SkScalar dx,SkScalar dy,SkRRect * dst)377 void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 378 this->inset(-dx, -dy, dst); 379 } 380 381 /** Outsets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be 382 positive, negative, or zero. 383 384 If either corner radius is zero, the corner has no curvature and is unchanged. 385 Otherwise, if adjusted radius becomes negative, pins radius to zero. 386 If dx exceeds half bounds width, bounds left and right are set to 387 bounds x-axis center. If dy exceeds half bounds height, bounds top and 388 bottom are set to bounds y-axis center. 389 390 If dx or dy cause the bounds to become infinite, bounds is zeroed. 391 392 @param dx subtracted from rect().fLeft, and added to rect().fRight 393 @param dy subtracted from rect().fTop, and added to rect().fBottom 394 */ outset(SkScalar dx,SkScalar dy)395 void outset(SkScalar dx, SkScalar dy) { 396 this->inset(-dx, -dy, this); 397 } 398 399 /** Translates SkRRect by (dx, dy). 400 401 @param dx offset added to rect().fLeft and rect().fRight 402 @param dy offset added to rect().fTop and rect().fBottom 403 */ offset(SkScalar dx,SkScalar dy)404 void offset(SkScalar dx, SkScalar dy) { 405 fRect.offset(dx, dy); 406 } 407 408 /** Returns SkRRect translated by (dx, dy). 409 410 @param dx offset added to rect().fLeft and rect().fRight 411 @param dy offset added to rect().fTop and rect().fBottom 412 @return SkRRect bounds offset by (dx, dy), with unchanged corner radii 413 */ makeOffset(SkScalar dx,SkScalar dy)414 SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const { 415 return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType); 416 } 417 418 /** Returns true if rect is inside the bounds and corner radii, and if 419 SkRRect and rect are not empty. 420 421 @param rect area tested for containment 422 @return true if SkRRect contains rect 423 */ 424 bool contains(const SkRect& rect) const; 425 426 /** Returns true if bounds and radii values are finite and describe a SkRRect 427 SkRRect::Type that matches getType(). All SkRRect methods construct valid types, 428 even if the input values are not valid. Invalid SkRRect data can only 429 be generated by corrupting memory. 430 431 @return true if bounds and radii match type() 432 */ 433 bool isValid() const; 434 435 static constexpr size_t kSizeInMemory = 12 * sizeof(SkScalar); 436 437 /** Writes SkRRect to buffer. Writes kSizeInMemory bytes, and returns 438 kSizeInMemory, the number of bytes written. 439 440 @param buffer storage for SkRRect 441 @return bytes written, kSizeInMemory 442 */ 443 size_t writeToMemory(void* buffer) const; 444 445 /** Reads SkRRect from buffer, reading kSizeInMemory bytes. 446 Returns kSizeInMemory, bytes read if length is at least kSizeInMemory. 447 Otherwise, returns zero. 448 449 @param buffer memory to read from 450 @param length size of buffer 451 @return bytes read, or 0 if length is less than kSizeInMemory 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 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 void dump(bool asHex) const; 474 475 /** Writes text representation of SkRRect to standard output. The representation 476 may be directly compiled as C++ code. Floating point values are written 477 with limited precision; it may not be possible to reconstruct original 478 SkRRect from output. 479 */ dump()480 void dump() const { this->dump(false); } 481 482 /** Writes text representation of SkRRect to standard output. The representation 483 may be directly compiled as C++ code. Floating point values are written 484 in hexadecimal to preserve their exact bit pattern. The output reconstructs the 485 original SkRRect. 486 */ dumpHex()487 void dumpHex() const { this->dump(true); } 488 489 private: 490 static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]); 491 SkRRect(const SkRect & rect,const SkVector radii[4],int32_t type)492 SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type) 493 : fRect(rect) 494 , fRadii{radii[0], radii[1], radii[2], radii[3]} 495 , fType(type) {} 496 497 /** 498 * Initializes fRect. If the passed in rect is not finite or empty the rrect will be fully 499 * initialized and false is returned. Otherwise, just fRect is initialized and true is returned. 500 */ 501 bool initializeRect(const SkRect&); 502 503 void computeType(); 504 bool checkCornerContainment(SkScalar x, SkScalar y) const; 505 void scaleRadii(const SkRect& rect); 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