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