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