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