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//      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*/
48class SK_API SkRRect {
49public:
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     */
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
101    Type type() const { return this->getType(); }
102
103    inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
104    inline bool isRect() const { return kRect_Type == this->getType(); }
105    inline bool isOval() const { return kOval_Type == this->getType(); }
106    inline bool isSimple() const { return kSimple_Type == this->getType(); }
107    inline bool isSimpleCircular() const {
108        return this->isSimple() && fRadii[0].fX == fRadii[0].fY;
109    }
110    inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
111    inline bool isComplex() const { return kComplex_Type == this->getType(); }
112
113    bool allCornersCircular() const;
114
115    SkScalar width() const { return fRect.width(); }
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     */
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     */
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     */
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
191    const SkRect& rect() const { return fRect; }
192    const SkVector& radii(Corner corner) const { return fRadii[corner]; }
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     */
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
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     */
238    void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
239        this->inset(-dx, -dy, dst);
240    }
241    void outset(SkScalar dx, SkScalar dy) {
242        this->inset(-dx, -dy, this);
243    }
244
245    /**
246     * Translate the rrect by (dx, dy).
247     */
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
303private:
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