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        // !< The RR is empty
56        kEmpty_Type,
57
58        //!< The RR is actually a (non-empty) rect (i.e., at least one radius
59        //!< at each corner is zero)
60        kRect_Type,
61
62        //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
63        //!< and >= width/2 and all the y radii are equal and >= height/2
64        kOval_Type,
65
66        //!< The RR is non-empty and all the x radii are equal & all y radii
67        //!< are equal but it is not an oval (i.e., there are lines between
68        //!< the curves) nor a rect (i.e., both radii are non-zero)
69        kSimple_Type,
70
71        //!< The RR is non-empty and the two left x radii are equal, the two top
72        //!< y radii are equal, and the same for the right and bottom but it is
73        //!< neither an rect, oval, nor a simple RR. It is called "nine patch"
74        //!< because the centers of the corner ellipses form an axis aligned
75        //!< rect with edges that divide the RR into an 9 rectangular patches:
76        //!< an interior patch, four edge patches, and four corner patches.
77        kNinePatch_Type,
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
85    /**
86     * Returns the RR's sub type.
87     */
88    Type getType() const {
89        SkDEBUGCODE(this->validate();)
90        return static_cast<Type>(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 isNinePatch() const { return kNinePatch_Type == this->getType(); }
103    inline bool isComplex() const { return kComplex_Type == this->getType(); }
104
105    bool allCornersCircular() const;
106
107    SkScalar width() const { return fRect.width(); }
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     */
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     */
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     */
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 one radius per-side.
166     */
167    void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
168                      SkScalar rightRad, SkScalar bottomRad);
169
170    /**
171     * Initialize the RR with potentially different radii for all four corners.
172     */
173    void setRectRadii(const SkRect& rect, const SkVector radii[4]);
174
175    // The radii are stored in UL, UR, LR, LL order.
176    enum Corner {
177        kUpperLeft_Corner,
178        kUpperRight_Corner,
179        kLowerRight_Corner,
180        kLowerLeft_Corner
181    };
182
183    const SkRect& rect() const { return fRect; }
184    const SkVector& radii(Corner corner) const { return fRadii[corner]; }
185    const SkRect& getBounds() const { return fRect; }
186
187    /**
188     *  When a rrect is simple, all of its radii are equal. This returns one
189     *  of those radii. This call requires the rrect to be non-complex.
190     */
191    const SkVector& getSimpleRadii() const {
192        SkASSERT(!this->isComplex());
193        return fRadii[0];
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    friend bool operator!=(const SkRRect& a, const SkRRect& b) {
203        return a.fRect != b.fRect ||
204               !SkScalarsEqual(a.fRadii[0].asScalars(),
205                               b.fRadii[0].asScalars(), 8);
206    }
207
208    /**
209     *  Call inset on the bounds, and adjust the radii to reflect what happens
210     *  in stroking: If the corner is sharp (no curvature), leave it alone,
211     *  otherwise we grow/shrink the radii by the amount of the inset. If a
212     *  given radius becomes negative, it is pinned to 0.
213     *
214     *  It is valid for dst == this.
215     */
216    void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
217
218    void inset(SkScalar dx, SkScalar dy) {
219        this->inset(dx, dy, this);
220    }
221
222    /**
223     *  Call outset on the bounds, and adjust the radii to reflect what happens
224     *  in stroking: If the corner is sharp (no curvature), leave it alone,
225     *  otherwise we grow/shrink the radii by the amount of the inset. If a
226     *  given radius becomes negative, it is pinned to 0.
227     *
228     *  It is valid for dst == this.
229     */
230    void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
231        this->inset(-dx, -dy, dst);
232    }
233    void outset(SkScalar dx, SkScalar dy) {
234        this->inset(-dx, -dy, this);
235    }
236
237    /**
238     * Translate the rrect by (dx, dy).
239     */
240    void offset(SkScalar dx, SkScalar dy) {
241        fRect.offset(dx, dy);
242    }
243
244    /**
245     *  Returns true if 'rect' is wholy inside the RR, and both
246     *  are not empty.
247     */
248    bool contains(const SkRect& rect) const;
249
250    SkDEBUGCODE(void validate() const;)
251
252    enum {
253        kSizeInMemory = 12 * sizeof(SkScalar)
254    };
255
256    /**
257     *  Write the rrect into the specified buffer. This is guaranteed to always
258     *  write kSizeInMemory bytes, and that value is guaranteed to always be
259     *  a multiple of 4. Return kSizeInMemory.
260     */
261    size_t writeToMemory(void* buffer) const;
262
263    /**
264     * Reads the rrect from the specified buffer
265     *
266     * If the specified buffer is large enough, this will read kSizeInMemory bytes,
267     * and that value is guaranteed to always be a multiple of 4.
268     *
269     * @param buffer Memory to read from
270     * @param length Amount of memory available in the buffer
271     * @return number of bytes read (must be a multiple of 4) or
272     *         0 if there was not enough memory available
273     */
274    size_t readFromMemory(const void* buffer, size_t length);
275
276    /**
277     *  Transform by the specified matrix, and put the result in dst.
278     *
279     *  @param matrix SkMatrix specifying the transform. Must only contain
280     *      scale and/or translate, or this call will fail.
281     *  @param dst SkRRect to store the result. It is an error to use this,
282     *      which would make this function no longer const.
283     *  @return true on success, false on failure. If false, dst is unmodified.
284     */
285    bool transform(const SkMatrix& matrix, SkRRect* dst) const;
286
287    void dump(bool asHex) const;
288    void dump() const { this->dump(false); }
289    void dumpHex() const { this->dump(true); }
290
291private:
292    SkRect fRect;
293    // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
294    SkVector fRadii[4];
295    // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
296    int32_t fType;
297    // TODO: add padding so we can use memcpy for flattening and not copy
298    // uninitialized data
299
300    void computeType();
301    bool checkCornerContainment(SkScalar x, SkScalar y) const;
302
303    // to access fRadii directly
304    friend class SkPath;
305};
306
307#endif
308