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