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 "SkRect.h" 12 #include "SkPoint.h" 13 14 class SkPath; 15 class SkMatrix; 16 class SkRBuffer; 17 class SkWBuffer; 18 19 // Path forward: 20 // core work 21 // add contains(SkRect&) - for clip stack 22 // add contains(SkRRect&) - for clip stack 23 // add heart rect computation (max rect inside RR) 24 // add 9patch rect computation 25 // add growToInclude(SkPath&) 26 // analysis 27 // use growToInclude to fit skp round rects & generate stats (RRs vs. real paths) 28 // check on # of rectorus's the RRs could handle 29 // rendering work 30 // update SkPath.addRRect() to only use quads 31 // add GM and bench 32 // further out 33 // detect and triangulate RRectorii rather than falling back to SW in Ganesh 34 // 35 36 /** \class SkRRect 37 38 The SkRRect class represents a rounded rect with a potentially different 39 radii for each corner. It does not have a constructor so must be 40 initialized with one of the initialization functions (e.g., setEmpty, 41 setRectRadii, etc.) 42 43 This class is intended to roughly match CSS' border-*-*-radius capabilities. 44 This means: 45 If either of a corner's radii are 0 the corner will be square. 46 Negative radii are not allowed (they are clamped to zero). 47 If the corner curves overlap they will be proportionally reduced to fit. 48 */ 49 class SK_API SkRRect { 50 public: 51 /** Default initialized to a rrect at the origin with zero width and height. */ 52 SkRRect() = default; 53 54 SkRRect(const SkRRect&) = default; 55 SkRRect& operator=(const SkRRect&) = default; 56 57 /** 58 * Enum to capture the various possible subtypes of RR. Accessed 59 * by type(). The subtypes become progressively less restrictive. 60 */ 61 enum Type { 62 // !< The RR has zero width and/or zero height. All radii are zero. 63 kEmpty_Type, 64 65 //!< The RR is actually a (non-empty) rect (i.e., at least one radius 66 //!< at each corner is zero) 67 kRect_Type, 68 69 //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal 70 //!< and >= width/2 and all the y radii are equal and >= height/2 71 kOval_Type, 72 73 //!< The RR is non-empty and all the x radii are equal & all y radii 74 //!< are equal but it is not an oval (i.e., there are lines between 75 //!< the curves) nor a rect (i.e., both radii are non-zero) 76 kSimple_Type, 77 78 //!< The RR is non-empty and the two left x radii are equal, the two top 79 //!< y radii are equal, and the same for the right and bottom but it is 80 //!< neither an rect, oval, nor a simple RR. It is called "nine patch" 81 //!< because the centers of the corner ellipses form an axis aligned 82 //!< rect with edges that divide the RR into an 9 rectangular patches: 83 //!< an interior patch, four edge patches, and four corner patches. 84 kNinePatch_Type, 85 86 //!< A fully general (non-empty) RR. Some of the x and/or y radii are 87 //!< different from the others and there must be one corner where 88 //!< both radii are non-zero. 89 kComplex_Type, 90 91 kLastType = kComplex_Type, 92 }; 93 94 /** 95 * Returns the RR's sub type. 96 */ getType()97 Type getType() const { 98 SkASSERT(this->isValid()); 99 return static_cast<Type>(fType); 100 } 101 type()102 Type type() const { return this->getType(); } 103 isEmpty()104 inline bool isEmpty() const { return kEmpty_Type == this->getType(); } isRect()105 inline bool isRect() const { return kRect_Type == this->getType(); } isOval()106 inline bool isOval() const { return kOval_Type == this->getType(); } isSimple()107 inline bool isSimple() const { return kSimple_Type == this->getType(); } 108 // TODO: should isSimpleCircular & isCircle take a tolerance? This could help 109 // instances where the mapping to device space is noisy. isSimpleCircular()110 inline bool isSimpleCircular() const { 111 return this->isSimple() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY); 112 } isCircle()113 inline bool isCircle() const { 114 return this->isOval() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY); 115 } isNinePatch()116 inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); } isComplex()117 inline bool isComplex() const { return kComplex_Type == this->getType(); } 118 119 bool allCornersCircular(SkScalar tolerance = SK_ScalarNearlyZero) const; 120 width()121 SkScalar width() const { return fRect.width(); } height()122 SkScalar height() const { return fRect.height(); } 123 124 /** 125 * Same as default initialized - zero width and height at the origin. 126 */ setEmpty()127 void setEmpty() { *this = SkRRect(); } 128 129 /** 130 * Set this RR to match the supplied rect. All radii will be 0. 131 */ setRect(const SkRect & rect)132 void setRect(const SkRect& rect) { 133 if (!this->initializeRect(rect)) { 134 return; 135 } 136 137 memset(fRadii, 0, sizeof(fRadii)); 138 fType = kRect_Type; 139 140 SkASSERT(this->isValid()); 141 } 142 143 /** Makes an empty rrect at the origin with zero width and height. */ MakeEmpty()144 static SkRRect MakeEmpty() { return SkRRect(); } 145 MakeRect(const SkRect & r)146 static SkRRect MakeRect(const SkRect& r) { 147 SkRRect rr; 148 rr.setRect(r); 149 return rr; 150 } 151 MakeOval(const SkRect & oval)152 static SkRRect MakeOval(const SkRect& oval) { 153 SkRRect rr; 154 rr.setOval(oval); 155 return rr; 156 } 157 MakeRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)158 static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 159 SkRRect rr; 160 rr.setRectXY(rect, xRad, yRad); 161 return rr; 162 } 163 164 /** 165 * Set this RR to match the supplied oval. All x radii will equal half the 166 * width and all y radii will equal half the height. 167 */ setOval(const SkRect & oval)168 void setOval(const SkRect& oval) { 169 if (!this->initializeRect(oval)) { 170 return; 171 } 172 173 SkScalar xRad = SkScalarHalf(fRect.width()); 174 SkScalar yRad = SkScalarHalf(fRect.height()); 175 176 for (int i = 0; i < 4; ++i) { 177 fRadii[i].set(xRad, yRad); 178 } 179 fType = kOval_Type; 180 181 SkASSERT(this->isValid()); 182 } 183 184 /** 185 * Initialize the RR with the same radii for all four corners. 186 */ 187 void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); 188 189 /** 190 * Initialize the rr with one radius per-side. 191 */ 192 void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, 193 SkScalar rightRad, SkScalar bottomRad); 194 195 /** 196 * Initialize the RR with potentially different radii for all four corners. 197 */ 198 void setRectRadii(const SkRect& rect, const SkVector radii[4]); 199 200 // The radii are stored in UL, UR, LR, LL order. 201 enum Corner { 202 kUpperLeft_Corner, 203 kUpperRight_Corner, 204 kLowerRight_Corner, 205 kLowerLeft_Corner 206 }; 207 rect()208 const SkRect& rect() const { return fRect; } radii(Corner corner)209 const SkVector& radii(Corner corner) const { return fRadii[corner]; } getBounds()210 const SkRect& getBounds() const { return fRect; } 211 212 /** 213 * When a rrect is simple, all of its radii are equal. This returns one 214 * of those radii. This call requires the rrect to be non-complex. 215 */ getSimpleRadii()216 const SkVector& getSimpleRadii() const { 217 SkASSERT(!this->isComplex()); 218 return fRadii[0]; 219 } 220 221 friend bool operator==(const SkRRect& a, const SkRRect& b) { 222 return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); 223 } 224 225 friend bool operator!=(const SkRRect& a, const SkRRect& b) { 226 return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); 227 } 228 229 /** 230 * Call inset on the bounds, and adjust the radii to reflect what happens 231 * in stroking: If the corner is sharp (no curvature), leave it alone, 232 * otherwise we grow/shrink the radii by the amount of the inset. If a 233 * given radius becomes negative, it is pinned to 0. 234 * 235 * If the inset amount is larger than the width/height then the rrect collapses to 236 * a degenerate line or point. 237 * 238 * If the inset is sufficiently negative to cause the bounds to become infinite then 239 * the result is a default initialized rrect. 240 * 241 * It is valid for dst == this. 242 */ 243 void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; 244 inset(SkScalar dx,SkScalar dy)245 void inset(SkScalar dx, SkScalar dy) { 246 this->inset(dx, dy, this); 247 } 248 249 /** 250 * Call outset on the bounds, and adjust the radii to reflect what happens 251 * in stroking: If the corner is sharp (no curvature), leave it alone, 252 * otherwise we grow/shrink the radii by the amount of the inset. If a 253 * given radius becomes negative, it is pinned to 0. 254 * 255 * It is valid for dst == this. 256 */ outset(SkScalar dx,SkScalar dy,SkRRect * dst)257 void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 258 this->inset(-dx, -dy, dst); 259 } outset(SkScalar dx,SkScalar dy)260 void outset(SkScalar dx, SkScalar dy) { 261 this->inset(-dx, -dy, this); 262 } 263 264 /** 265 * Translate the rrect by (dx, dy). 266 */ offset(SkScalar dx,SkScalar dy)267 void offset(SkScalar dx, SkScalar dy) { 268 fRect.offset(dx, dy); 269 } 270 makeOffset(SkScalar dx,SkScalar dy)271 SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const { 272 return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType); 273 } 274 275 /** 276 * Returns true if 'rect' is wholy inside the RR, and both 277 * are not empty. 278 */ 279 bool contains(const SkRect& rect) const; 280 281 bool isValid() const; 282 static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]); 283 284 enum { 285 kSizeInMemory = 12 * sizeof(SkScalar) 286 }; 287 288 /** 289 * Write the rrect into the specified buffer. This is guaranteed to always 290 * write kSizeInMemory bytes, and that value is guaranteed to always be 291 * a multiple of 4. Return kSizeInMemory. 292 */ 293 size_t writeToMemory(void* buffer) const; 294 void writeToBuffer(SkWBuffer*) const; 295 296 /** 297 * Reads the rrect from the specified buffer 298 * 299 * If the specified buffer is large enough, this will read kSizeInMemory bytes, 300 * and that value is guaranteed to always be a multiple of 4. 301 * 302 * @param buffer Memory to read from 303 * @param length Amount of memory available in the buffer 304 * @return number of bytes read (must be a multiple of 4) or 305 * 0 if there was not enough memory available 306 */ 307 size_t readFromMemory(const void* buffer, size_t length); 308 bool readFromBuffer(SkRBuffer*); 309 310 /** 311 * Transform by the specified matrix, and put the result in dst. 312 * 313 * @param matrix SkMatrix specifying the transform. Must only contain 314 * scale and/or translate, or this call will fail. 315 * @param dst SkRRect to store the result. It is an error to use this, 316 * which would make this function no longer const. 317 * @return true on success, false on failure. 318 */ 319 bool transform(const SkMatrix& matrix, SkRRect* dst) const; 320 321 void dump(bool asHex) const; dump()322 void dump() const { this->dump(false); } dumpHex()323 void dumpHex() const { this->dump(true); } 324 325 private: SkRRect(const SkRect & rect,const SkVector radii[4],int32_t type)326 SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type) 327 : fRect(rect) 328 , fRadii{radii[0], radii[1], radii[2], radii[3]} 329 , fType(type) {} 330 331 /** 332 * Initializes fRect. If the passed in rect is not finite or empty the rrect will be fully 333 * initialized and false is returned. Otherwise, just fRect is initialized and true is returned. 334 */ 335 bool initializeRect(const SkRect&); 336 337 void computeType(); 338 bool checkCornerContainment(SkScalar x, SkScalar y) const; 339 void scaleRadii(); 340 341 SkRect fRect = SkRect::MakeEmpty(); 342 // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] 343 SkVector fRadii[4] = {{0, 0}, {0, 0}, {0,0}, {0,0}}; 344 // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes) 345 int32_t fType = kEmpty_Type; 346 // TODO: add padding so we can use memcpy for flattening and not copy uninitialized data 347 348 // to access fRadii directly 349 friend class SkPath; 350 }; 351 352 #endif 353