• 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 //      add entry points (clipRRect, drawRRect) - plumb down to SkBaseDevice
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 */
54 class SK_API SkRRect {
55 public:
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      */
getType()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 
type()99     Type type() const { return this->getType(); }
100 
isEmpty()101     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
isRect()102     inline bool isRect() const { return kRect_Type == this->getType(); }
isOval()103     inline bool isOval() const { return kOval_Type == this->getType(); }
isSimple()104     inline bool isSimple() const { return kSimple_Type == this->getType(); }
isComplex()105     inline bool isComplex() const { return kComplex_Type == this->getType(); }
106 
width()107     SkScalar width() const { return fRect.width(); }
height()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      */
setEmpty()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      */
setRect(const SkRect & rect)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      */
setOval(const SkRect & oval)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 
rect()177     const SkRect& rect() const { return fRect; }
radii(Corner corner)178     const SkVector& radii(Corner corner) const { return fRadii[corner]; }
getBounds()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      */
getSimpleRadii()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      *  Call inset on the bounds, and adjust the radii to reflect what happens
204      *  in stroking: If the corner is sharp (no curvature), leave it alone,
205      *  otherwise we grow/shrink the radii by the amount of the inset. If a
206      *  given radius becomes negative, it is pinned to 0.
207      *
208      *  It is valid for dst == this.
209      */
210     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
211 
inset(SkScalar dx,SkScalar dy)212     void inset(SkScalar dx, SkScalar dy) {
213         this->inset(dx, dy, this);
214     }
215 
216     /**
217      *  Call outset 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      */
outset(SkScalar dx,SkScalar dy,SkRRect * dst)224     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
225         this->inset(-dx, -dy, dst);
226     }
outset(SkScalar dx,SkScalar dy)227     void outset(SkScalar dx, SkScalar dy) {
228         this->inset(-dx, -dy, this);
229     }
230 
231     /**
232      *  Returns true if 'rect' is wholy inside the RR, and both
233      *  are not empty.
234      */
235     bool contains(const SkRect& rect) const;
236 
237     SkDEBUGCODE(void validate() const;)
238 
239     enum {
240         kSizeInMemory = 12 * sizeof(SkScalar)
241     };
242 
243     /**
244      *  Write the rrect into the specified buffer. This is guaranteed to always
245      *  write kSizeInMemory bytes, and that value is guaranteed to always be
246      *  a multiple of 4. Return kSizeInMemory.
247      */
248     size_t writeToMemory(void* buffer) const;
249 
250     /**
251      * Reads the rrect from the specified buffer
252      *
253      * If the specified buffer is large enough, this will read kSizeInMemory bytes,
254      * and that value is guaranteed to always be a multiple of 4.
255      *
256      * @param buffer Memory to read from
257      * @param length Amount of memory available in the buffer
258      * @return number of bytes read (must be a multiple of 4) or
259      *         0 if there was not enough memory available
260      */
261     size_t readFromMemory(const void* buffer, size_t length);
262 
263     /**
264      *  Transform by the specified matrix, and put the result in dst.
265      *
266      *  @param matrix SkMatrix specifying the transform. Must only contain
267      *      scale and/or translate, or this call will fail.
268      *  @param dst SkRRect to store the result. It is an error to use this,
269      *      which would make this function no longer const.
270      *  @return true on success, false on failure. If false, dst is unmodified.
271      */
272     bool transform(const SkMatrix& matrix, SkRRect* dst) const;
273 
274 private:
275     SkRect fRect;
276     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
277     SkVector fRadii[4];
278     mutable Type fType;
279     // TODO: add padding so we can use memcpy for flattening and not copy
280     // uninitialized data
281 
282     void computeType() const;
283     bool checkCornerContainment(SkScalar x, SkScalar y) const;
284 
285     // to access fRadii directly
286     friend class SkPath;
287 };
288 
289 #endif
290