|
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 // 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 */ |
|
48 class SK_API SkRRect { |
|
49 public: |
|
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 |
|
291 private: |
|
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 |