SkRRect.h revision fa5edbe5750632b4590ed951c594dd2766ccc4cf
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 14class SkPath; 15class 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*/ 48class SK_API SkRRect { 49public: 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 //!< A fully general (non-empty) RR. Some of the x and/or y radii are 75 //!< different from the others and there must be one corner where 76 //!< both radii are non-zero. 77 kComplex_Type, 78 }; 79 80 /** 81 * Returns the RR's sub type. 82 */ 83 Type getType() const { 84 SkDEBUGCODE(this->validate();) 85 86 if (kUnknown_Type == fType) { 87 this->computeType(); 88 } 89 SkASSERT(kUnknown_Type != fType); 90 return fType; 91 } 92 93 Type type() const { return this->getType(); } 94 95 inline bool isEmpty() const { return kEmpty_Type == this->getType(); } 96 inline bool isRect() const { return kRect_Type == this->getType(); } 97 inline bool isOval() const { return kOval_Type == this->getType(); } 98 inline bool isSimple() const { return kSimple_Type == this->getType(); } 99 inline bool isSimpleCircular() const { 100 return this->isSimple() && fRadii[0].fX == fRadii[0].fY; 101 } 102 inline bool isComplex() const { return kComplex_Type == this->getType(); } 103 104 bool allCornersCircular() const; 105 106 /** 107 * Are both x-radii the same on the two left corners, and similar for the top, right, and 108 * bottom. When this is the case the four ellipse centers form a rectangle. 109 */ 110 bool isNinePatch() const { 111 return fRadii[kUpperLeft_Corner].fX == fRadii[kLowerLeft_Corner].fX && 112 fRadii[kUpperRight_Corner].fX == fRadii[kLowerRight_Corner].fX && 113 fRadii[kUpperLeft_Corner].fY == fRadii[kUpperRight_Corner].fY && 114 fRadii[kLowerLeft_Corner].fY == fRadii[kLowerRight_Corner].fY; 115 } 116 117 SkScalar width() const { return fRect.width(); } 118 SkScalar height() const { return fRect.height(); } 119 120 /** 121 * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii. 122 */ 123 void setEmpty() { 124 fRect.setEmpty(); 125 memset(fRadii, 0, sizeof(fRadii)); 126 fType = kEmpty_Type; 127 128 SkDEBUGCODE(this->validate();) 129 } 130 131 /** 132 * Set this RR to match the supplied rect. All radii will be 0. 133 */ 134 void setRect(const SkRect& rect) { 135 if (rect.isEmpty()) { 136 this->setEmpty(); 137 return; 138 } 139 140 fRect = rect; 141 memset(fRadii, 0, sizeof(fRadii)); 142 fType = kRect_Type; 143 144 SkDEBUGCODE(this->validate();) 145 } 146 147 /** 148 * Set this RR to match the supplied oval. All x radii will equal half the 149 * width and all y radii will equal half the height. 150 */ 151 void setOval(const SkRect& oval) { 152 if (oval.isEmpty()) { 153 this->setEmpty(); 154 return; 155 } 156 157 SkScalar xRad = SkScalarHalf(oval.width()); 158 SkScalar yRad = SkScalarHalf(oval.height()); 159 160 fRect = oval; 161 for (int i = 0; i < 4; ++i) { 162 fRadii[i].set(xRad, yRad); 163 } 164 fType = kOval_Type; 165 166 SkDEBUGCODE(this->validate();) 167 } 168 169 /** 170 * Initialize the RR with the same radii for all four corners. 171 */ 172 void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); 173 174 /** 175 * Initialize the RR with potentially different radii for all four corners. 176 */ 177 void setRectRadii(const SkRect& rect, const SkVector radii[4]); 178 179 // The radii are stored in UL, UR, LR, LL order. 180 enum Corner { 181 kUpperLeft_Corner, 182 kUpperRight_Corner, 183 kLowerRight_Corner, 184 kLowerLeft_Corner 185 }; 186 187 const SkRect& rect() const { return fRect; } 188 const SkVector& radii(Corner corner) const { return fRadii[corner]; } 189 const SkRect& getBounds() const { return fRect; } 190 191 /** 192 * When a rrect is simple, all of its radii are equal. This returns one 193 * of those radii. This call requires the rrect to be non-complex. 194 */ 195 const SkVector& getSimpleRadii() const { 196 SkASSERT(!this->isComplex()); 197 return fRadii[0]; 198 } 199 200 friend bool operator==(const SkRRect& a, const SkRRect& b) { 201 return a.fRect == b.fRect && 202 SkScalarsEqual(a.fRadii[0].asScalars(), 203 b.fRadii[0].asScalars(), 8); 204 } 205 206 friend bool operator!=(const SkRRect& a, const SkRRect& b) { 207 return a.fRect != b.fRect || 208 !SkScalarsEqual(a.fRadii[0].asScalars(), 209 b.fRadii[0].asScalars(), 8); 210 } 211 212 /** 213 * Call inset on the bounds, and adjust the radii to reflect what happens 214 * in stroking: If the corner is sharp (no curvature), leave it alone, 215 * otherwise we grow/shrink the radii by the amount of the inset. If a 216 * given radius becomes negative, it is pinned to 0. 217 * 218 * It is valid for dst == this. 219 */ 220 void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; 221 222 void inset(SkScalar dx, SkScalar dy) { 223 this->inset(dx, dy, this); 224 } 225 226 /** 227 * Call outset on the bounds, and adjust the radii to reflect what happens 228 * in stroking: If the corner is sharp (no curvature), leave it alone, 229 * otherwise we grow/shrink the radii by the amount of the inset. If a 230 * given radius becomes negative, it is pinned to 0. 231 * 232 * It is valid for dst == this. 233 */ 234 void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 235 this->inset(-dx, -dy, dst); 236 } 237 void outset(SkScalar dx, SkScalar dy) { 238 this->inset(-dx, -dy, this); 239 } 240 241 /** 242 * Translate the rrect by (dx, dy). 243 */ 244 void offset(SkScalar dx, SkScalar dy) { 245 fRect.offset(dx, dy); 246 } 247 248 /** 249 * Returns true if 'rect' is wholy inside the RR, and both 250 * are not empty. 251 */ 252 bool contains(const SkRect& rect) const; 253 254 SkDEBUGCODE(void validate() const;) 255 256 enum { 257 kSizeInMemory = 12 * sizeof(SkScalar) 258 }; 259 260 /** 261 * Write the rrect into the specified buffer. This is guaranteed to always 262 * write kSizeInMemory bytes, and that value is guaranteed to always be 263 * a multiple of 4. Return kSizeInMemory. 264 */ 265 size_t writeToMemory(void* buffer) const; 266 267 /** 268 * Reads the rrect from the specified buffer 269 * 270 * If the specified buffer is large enough, this will read kSizeInMemory bytes, 271 * and that value is guaranteed to always be a multiple of 4. 272 * 273 * @param buffer Memory to read from 274 * @param length Amount of memory available in the buffer 275 * @return number of bytes read (must be a multiple of 4) or 276 * 0 if there was not enough memory available 277 */ 278 size_t readFromMemory(const void* buffer, size_t length); 279 280 /** 281 * Transform by the specified matrix, and put the result in dst. 282 * 283 * @param matrix SkMatrix specifying the transform. Must only contain 284 * scale and/or translate, or this call will fail. 285 * @param dst SkRRect to store the result. It is an error to use this, 286 * which would make this function no longer const. 287 * @return true on success, false on failure. If false, dst is unmodified. 288 */ 289 bool transform(const SkMatrix& matrix, SkRRect* dst) const; 290 291private: 292 SkRect fRect; 293 // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] 294 SkVector fRadii[4]; 295 mutable Type fType; 296 // TODO: add padding so we can use memcpy for flattening and not copy 297 // uninitialized data 298 299 void computeType() const; 300 bool checkCornerContainment(SkScalar x, SkScalar y) const; 301 302 // to access fRadii directly 303 friend class SkPath; 304}; 305 306#endif 307