robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 | |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 14 | class SkPath; |
| 15 | |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 |
robertphillips@google.com | 1f2f338 | 2013-08-29 11:54:56 +0000 | [diff] [blame^] | 28 | // add entry points (clipRRect, drawRRect) - plumb down to SkBaseDevice |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 38 | // |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 39 | |
| 40 | /** \class SkRRect |
| 41 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 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, |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 45 | setRectRadii, etc.) |
| 46 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 47 | This class is intended to roughly match CSS' border-*-*-radius capabilities. |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 48 | This means: |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 49 | If either of a corner's radii are 0 the corner will be square. |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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: |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 55 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 56 | * Enum to capture the various possible subtypes of RR. Accessed |
| 57 | * by type(). The subtypes become progressively less restrictive. |
| 58 | */ |
| 59 | enum Type { |
tomhudson@google.com | 0da23a5 | 2012-12-03 11:41:21 +0000 | [diff] [blame] | 60 | // !< Internal indicator that the sub type must be computed. |
| 61 | kUnknown_Type = -1, |
| 62 | |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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) |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 68 | kRect_Type, |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 69 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 70 | //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 71 | //!< and >= width/2 and all the y radii are equal and >= height/2 |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 72 | kOval_Type, |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 73 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 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 |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 76 | //!< the curves) nor a rect (i.e., both radii are non-zero) |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 77 | kSimple_Type, |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 85 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 86 | * Returns the RR's sub type. |
| 87 | */ |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 88 | Type getType() const { |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 89 | SkDEBUGCODE(this->validate();) |
| 90 | |
| 91 | if (kUnknown_Type == fType) { |
| 92 | this->computeType(); |
| 93 | } |
| 94 | SkASSERT(kUnknown_Type != fType); |
| 95 | return fType; |
| 96 | } |
| 97 | |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 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 | |
mike@reedtribe.org | 3707164 | 2012-12-17 02:10:42 +0000 | [diff] [blame] | 106 | SkScalar width() const { return fRect.width(); } |
| 107 | SkScalar height() const { return fRect.height(); } |
| 108 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 109 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 110 | * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii. |
| 111 | */ |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 112 | void setEmpty() { |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 113 | fRect.setEmpty(); |
| 114 | memset(fRadii, 0, sizeof(fRadii)); |
| 115 | fType = kEmpty_Type; |
| 116 | |
| 117 | SkDEBUGCODE(this->validate();) |
| 118 | } |
| 119 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 120 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 136 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 158 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 159 | * Initialize the RR with the same radii for all four corners. |
| 160 | */ |
| 161 | void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); |
| 162 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 163 | /** |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 164 | * Initialize the RR with potentially different radii for all four corners. |
| 165 | */ |
| 166 | void setRectRadii(const SkRect& rect, const SkVector radii[4]); |
| 167 | |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 168 | // The radii are stored in UL, UR, LR, LL order. |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 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]; } |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 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 | } |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 188 | |
| 189 | friend bool operator==(const SkRRect& a, const SkRRect& b) { |
| 190 | return a.fRect == b.fRect && |
djsollen@google.com | 4bd2bdb | 2013-03-08 18:35:13 +0000 | [diff] [blame] | 191 | SkScalarsEqual(a.fRadii[0].asScalars(), |
| 192 | b.fRadii[0].asScalars(), 8); |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | friend bool operator!=(const SkRRect& a, const SkRRect& b) { |
| 196 | return a.fRect != b.fRect || |
djsollen@google.com | 4bd2bdb | 2013-03-08 18:35:13 +0000 | [diff] [blame] | 197 | !SkScalarsEqual(a.fRadii[0].asScalars(), |
| 198 | b.fRadii[0].asScalars(), 8); |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 199 | } |
| 200 | |
| 201 | /** |
| 202 | * Returns true if (p.fX,p.fY) is inside the RR, and the RR |
| 203 | * is not empty. |
| 204 | * |
| 205 | * Contains treats the left and top differently from the right and bottom. |
| 206 | * The left and top coordinates of the RR are themselves considered |
| 207 | * to be inside, while the right and bottom are not. All the points on the |
| 208 | * edges of the corners are considered to be inside. |
| 209 | */ |
| 210 | bool contains(const SkPoint& p) const { |
| 211 | return contains(p.fX, p.fY); |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Returns true if (x,y) is inside the RR, and the RR |
| 216 | * is not empty. |
| 217 | * |
| 218 | * Contains treats the left and top differently from the right and bottom. |
| 219 | * The left and top coordinates of the RR are themselves considered |
| 220 | * to be inside, while the right and bottom are not. All the points on the |
| 221 | * edges of the corners are considered to be inside. |
| 222 | */ |
| 223 | bool contains(SkScalar x, SkScalar y) const; |
| 224 | |
mike@reedtribe.org | bcbef57 | 2012-12-23 23:11:21 +0000 | [diff] [blame] | 225 | /** |
| 226 | * Call inset on the bounds, and adjust the radii to reflect what happens |
| 227 | * in stroking: If the corner is sharp (no curvature), leave it alone, |
| 228 | * otherwise we grow/shrink the radii by the amount of the inset. If a |
| 229 | * given radius becomes negative, it is pinned to 0. |
| 230 | * |
| 231 | * It is valid for dst == this. |
| 232 | */ |
mike@reedtribe.org | 3707164 | 2012-12-17 02:10:42 +0000 | [diff] [blame] | 233 | void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; |
mike@reedtribe.org | bcbef57 | 2012-12-23 23:11:21 +0000 | [diff] [blame] | 234 | |
mike@reedtribe.org | 3707164 | 2012-12-17 02:10:42 +0000 | [diff] [blame] | 235 | void inset(SkScalar dx, SkScalar dy) { |
| 236 | this->inset(dx, dy, this); |
| 237 | } |
mike@reedtribe.org | bcbef57 | 2012-12-23 23:11:21 +0000 | [diff] [blame] | 238 | |
| 239 | /** |
| 240 | * Call outset on the bounds, and adjust the radii to reflect what happens |
| 241 | * in stroking: If the corner is sharp (no curvature), leave it alone, |
| 242 | * otherwise we grow/shrink the radii by the amount of the inset. If a |
| 243 | * given radius becomes negative, it is pinned to 0. |
| 244 | * |
| 245 | * It is valid for dst == this. |
| 246 | */ |
mike@reedtribe.org | 3707164 | 2012-12-17 02:10:42 +0000 | [diff] [blame] | 247 | void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { |
| 248 | this->inset(-dx, -dy, dst); |
| 249 | } |
| 250 | void outset(SkScalar dx, SkScalar dy) { |
| 251 | this->inset(-dx, -dy, this); |
| 252 | } |
| 253 | |
robertphillips@google.com | 32c1b66 | 2013-04-25 12:23:00 +0000 | [diff] [blame] | 254 | /** |
| 255 | * Returns true if 'rect' is wholy inside the RR, and both |
| 256 | * are not empty. |
| 257 | */ |
| 258 | bool contains(const SkRect& rect) const; |
| 259 | |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 260 | SkDEBUGCODE(void validate() const;) |
| 261 | |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 262 | enum { |
| 263 | kSizeInMemory = 12 * sizeof(SkScalar) |
| 264 | }; |
skia.committer@gmail.com | 306ab9d | 2012-12-13 02:01:33 +0000 | [diff] [blame] | 265 | |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 266 | /** |
| 267 | * Write the rrect into the specified buffer. This is guaranteed to always |
| 268 | * write kSizeInMemory bytes, and that value is guaranteed to always be |
| 269 | * a multiple of 4. Return kSizeInMemory. |
| 270 | */ |
| 271 | uint32_t writeToMemory(void* buffer) const; |
| 272 | |
| 273 | /** |
| 274 | * Read the rrect from the specified buffer. This is guaranteed to always |
| 275 | * read kSizeInMemory bytes, and that value is guaranteed to always be |
| 276 | * a multiple of 4. Return kSizeInMemory. |
| 277 | */ |
| 278 | uint32_t readFromMemory(const void* buffer); |
| 279 | |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 280 | private: |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 281 | SkRect fRect; |
| 282 | // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] |
skia.committer@gmail.com | c3d7d90 | 2012-11-30 02:01:24 +0000 | [diff] [blame] | 283 | SkVector fRadii[4]; |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 284 | mutable Type fType; |
| 285 | // TODO: add padding so we can use memcpy for flattening and not copy |
| 286 | // uninitialized data |
| 287 | |
| 288 | void computeType() const; |
robertphillips@google.com | 32c1b66 | 2013-04-25 12:23:00 +0000 | [diff] [blame] | 289 | bool checkCornerContainment(SkScalar x, SkScalar y) const; |
reed@google.com | 4ed0fb7 | 2012-12-12 20:48:18 +0000 | [diff] [blame] | 290 | |
| 291 | // to access fRadii directly |
| 292 | friend class SkPath; |
robertphillips@google.com | 5985e7c | 2012-11-29 13:24:55 +0000 | [diff] [blame] | 293 | }; |
| 294 | |
robertphillips@google.com | 8520301 | 2012-11-29 13:49:33 +0000 | [diff] [blame] | 295 | #endif |