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// add entry points (clipRRect, drawRRect) - plumb down to SkDevice 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*/ 54class SK_API SkRRect { 55public: 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 */ 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 99 Type type() const { return this->getType(); } 100 101 inline bool isEmpty() const { return kEmpty_Type == this->getType(); } 102 inline bool isRect() const { return kRect_Type == this->getType(); } 103 inline bool isOval() const { return kOval_Type == this->getType(); } 104 inline bool isSimple() const { return kSimple_Type == this->getType(); } 105 inline bool isComplex() const { return kComplex_Type == this->getType(); } 106 107 SkScalar width() const { return fRect.width(); } 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 */ 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 */ 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 */ 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 177 const SkRect& rect() const { return fRect; } 178 const SkVector& radii(Corner corner) const { return fRadii[corner]; } 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 */ 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 * Returns true if (p.fX,p.fY) is inside the RR, and the RR 204 * is not empty. 205 * 206 * Contains treats the left and top differently from the right and bottom. 207 * The left and top coordinates of the RR are themselves considered 208 * to be inside, while the right and bottom are not. All the points on the 209 * edges of the corners are considered to be inside. 210 */ 211 bool contains(const SkPoint& p) const { 212 return contains(p.fX, p.fY); 213 } 214 215 /** 216 * Returns true if (x,y) is inside the RR, and the RR 217 * is not empty. 218 * 219 * Contains treats the left and top differently from the right and bottom. 220 * The left and top coordinates of the RR are themselves considered 221 * to be inside, while the right and bottom are not. All the points on the 222 * edges of the corners are considered to be inside. 223 */ 224 bool contains(SkScalar x, SkScalar y) const; 225 226 /** 227 * Call inset 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 inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; 235 236 void inset(SkScalar dx, SkScalar dy) { 237 this->inset(dx, dy, this); 238 } 239 240 /** 241 * Call outset on the bounds, and adjust the radii to reflect what happens 242 * in stroking: If the corner is sharp (no curvature), leave it alone, 243 * otherwise we grow/shrink the radii by the amount of the inset. If a 244 * given radius becomes negative, it is pinned to 0. 245 * 246 * It is valid for dst == this. 247 */ 248 void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 249 this->inset(-dx, -dy, dst); 250 } 251 void outset(SkScalar dx, SkScalar dy) { 252 this->inset(-dx, -dy, this); 253 } 254 255 /** 256 * Returns true if 'rect' is wholy inside the RR, and both 257 * are not empty. 258 */ 259 bool contains(const SkRect& rect) const; 260 261 SkDEBUGCODE(void validate() const;) 262 263 enum { 264 kSizeInMemory = 12 * sizeof(SkScalar) 265 }; 266 267 /** 268 * Write the rrect into the specified buffer. This is guaranteed to always 269 * write kSizeInMemory bytes, and that value is guaranteed to always be 270 * a multiple of 4. Return kSizeInMemory. 271 */ 272 uint32_t writeToMemory(void* buffer) const; 273 274 /** 275 * Read the rrect from the specified buffer. This is guaranteed to always 276 * read kSizeInMemory bytes, and that value is guaranteed to always be 277 * a multiple of 4. Return kSizeInMemory. 278 */ 279 uint32_t readFromMemory(const void* buffer); 280 281 /** 282 * Transform by the specified matrix, and put the result in dst. 283 * 284 * @param matrix SkMatrix specifying the transform. Must only contain 285 * scale and/or translate, or this call will fail. 286 * @param dst SkRRect to store the result. It is an error to use this, 287 * which would make this function no longer const. 288 * @return true on success, false on failure. If false, dst is unmodified. 289 */ 290 bool transform(const SkMatrix& matrix, SkRRect* dst) const; 291 292private: 293 SkRect fRect; 294 // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] 295 SkVector fRadii[4]; 296 mutable Type fType; 297 // TODO: add padding so we can use memcpy for flattening and not copy 298 // uninitialized data 299 300 void computeType() const; 301 bool checkCornerContainment(SkScalar x, SkScalar y) const; 302 303 // to access fRadii directly 304 friend class SkPath; 305}; 306 307#endif 308